Switch to github.com/golang/dep for vendoring
Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
This commit is contained in:
parent
d6ab91be27
commit
8e5b17cf13
15431 changed files with 3971413 additions and 8881 deletions
4
vendor/github.com/containernetworking/cni/.gitignore
generated
vendored
Normal file
4
vendor/github.com/containernetworking/cni/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
bin/
|
||||
gopath/
|
||||
*.sw[ponm]
|
||||
.vagrant
|
50
vendor/github.com/containernetworking/cni/.travis.yml
generated
vendored
Normal file
50
vendor/github.com/containernetworking/cni/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
language: go
|
||||
sudo: required
|
||||
dist: trusty
|
||||
|
||||
go:
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- tip
|
||||
|
||||
env:
|
||||
global:
|
||||
- TOOLS_CMD=golang.org/x/tools/cmd
|
||||
- PATH=$GOROOT/bin:$PATH
|
||||
- GO15VENDOREXPERIMENT=1
|
||||
matrix:
|
||||
- TARGET=amd64
|
||||
- TARGET=arm
|
||||
- TARGET=arm64
|
||||
- TARGET=ppc64le
|
||||
|
||||
matrix:
|
||||
fast_finish: true
|
||||
allow_failures:
|
||||
- go: tip
|
||||
exclude:
|
||||
- go: tip
|
||||
env: arm
|
||||
- go: tip
|
||||
env: arm64
|
||||
- go: tip
|
||||
env: ppc64le
|
||||
|
||||
install:
|
||||
- go get ${TOOLS_CMD}/cover
|
||||
- go get github.com/modocache/gover
|
||||
- go get github.com/mattn/goveralls
|
||||
|
||||
script:
|
||||
- >
|
||||
if [ "${TARGET}" == "amd64" ]; then
|
||||
GOARCH="${TARGET}" ./test;
|
||||
else
|
||||
GOARCH="${TARGET}" ./build;
|
||||
fi
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
||||
git:
|
||||
depth: 9999999
|
125
vendor/github.com/containernetworking/cni/CONTRIBUTING.md
generated
vendored
Normal file
125
vendor/github.com/containernetworking/cni/CONTRIBUTING.md
generated
vendored
Normal file
|
@ -0,0 +1,125 @@
|
|||
# How to Contribute
|
||||
|
||||
CNI is [Apache 2.0 licensed](LICENSE) and accepts contributions via GitHub
|
||||
pull requests. This document outlines some of the conventions on development
|
||||
workflow, commit message formatting, contact points and other resources to make
|
||||
it easier to get your contribution accepted.
|
||||
|
||||
We gratefully welcome improvements to documentation as well as to code.
|
||||
|
||||
# Certificate of Origin
|
||||
|
||||
By contributing to this project you agree to the Developer Certificate of
|
||||
Origin (DCO). This document was created by the Linux Kernel community and is a
|
||||
simple statement that you, as a contributor, have the legal right to make the
|
||||
contribution. See the [DCO](DCO) file for details.
|
||||
|
||||
# Email and Chat
|
||||
|
||||
The project uses the the cni-dev email list and IRC chat:
|
||||
- Email: [cni-dev](https://groups.google.com/forum/#!forum/cni-dev)
|
||||
- IRC: #[containernetworking](irc://irc.freenode.org:6667/#containernetworking) channel on freenode.org
|
||||
|
||||
Please avoid emailing maintainers found in the MAINTAINERS file directly. They
|
||||
are very busy and read the mailing lists.
|
||||
|
||||
## Getting Started
|
||||
|
||||
- Fork the repository on GitHub
|
||||
- Read the [README](README.md) for build and test instructions
|
||||
- Play with the project, submit bugs, submit pull requests!
|
||||
|
||||
## Contribution workflow
|
||||
|
||||
This is a rough outline of how to prepare a contribution:
|
||||
|
||||
- Create a topic branch from where you want to base your work (usually branched from master).
|
||||
- Make commits of logical units.
|
||||
- Make sure your commit messages are in the proper format (see below).
|
||||
- Push your changes to a topic branch in your fork of the repository.
|
||||
- If you changed code:
|
||||
- add automated tests to cover your changes, using the [Ginkgo](http://onsi.github.io/ginkgo/) & [Gomega](http://onsi.github.io/gomega/) style
|
||||
- if the package did not previously have any test coverage, add it to the list
|
||||
of `TESTABLE` packages in the `test` script.
|
||||
- run the full test script and ensure it passes
|
||||
- Make sure any new code files have a license header (this is now enforced by automated tests)
|
||||
- Submit a pull request to the original repository.
|
||||
|
||||
## How to run the test suite
|
||||
We generally require test coverage of any new features or bug fixes.
|
||||
|
||||
Here's how you can run the test suite on any system (even Mac or Windows) using
|
||||
[Vagrant](https://www.vagrantup.com/) and a hypervisor of your choice:
|
||||
|
||||
```bash
|
||||
vagrant up
|
||||
vagrant ssh
|
||||
# you're now in a shell in a virtual machine
|
||||
sudo su
|
||||
cd /go/src/github.com/containernetworking/cni
|
||||
|
||||
# to run the full test suite
|
||||
./test
|
||||
|
||||
# to focus on a particular test suite
|
||||
cd plugins/main/loopback
|
||||
go test
|
||||
```
|
||||
|
||||
# Acceptance policy
|
||||
|
||||
These things will make a PR more likely to be accepted:
|
||||
|
||||
* a well-described requirement
|
||||
* tests for new code
|
||||
* tests for old code!
|
||||
* new code and tests follow the conventions in old code and tests
|
||||
* a good commit message (see below)
|
||||
|
||||
In general, we will merge a PR once two maintainers have endorsed it.
|
||||
Trivial changes (e.g., corrections to spelling) may get waved through.
|
||||
For substantial changes, more people may become involved, and you might get asked to resubmit the PR or divide the changes into more than one PR.
|
||||
|
||||
### Format of the Commit Message
|
||||
|
||||
We follow a rough convention for commit messages that is designed to answer two
|
||||
questions: what changed and why. The subject line should feature the what and
|
||||
the body of the commit should describe the why.
|
||||
|
||||
```
|
||||
scripts: add the test-cluster command
|
||||
|
||||
this uses tmux to setup a test cluster that you can easily kill and
|
||||
start for debugging.
|
||||
|
||||
Fixes #38
|
||||
```
|
||||
|
||||
The format can be described more formally as follows:
|
||||
|
||||
```
|
||||
<subsystem>: <what changed>
|
||||
<BLANK LINE>
|
||||
<why this change was made>
|
||||
<BLANK LINE>
|
||||
<footer>
|
||||
```
|
||||
|
||||
The first line is the subject and should be no longer than 70 characters, the
|
||||
second line is always blank, and other lines should be wrapped at 80 characters.
|
||||
This allows the message to be easier to read on GitHub as well as in various
|
||||
git tools.
|
||||
|
||||
## 3rd party plugins
|
||||
So you've built a CNI plugin. Where should it live?
|
||||
|
||||
Short answer: We'd be happy to link to it from our [list of 3rd party plugins](README.md#3rd-party-plugins).
|
||||
But we'd rather you kept the code in your own repo.
|
||||
|
||||
Long answer: An advantage of the CNI model is that independent plugins can be
|
||||
built, distributed and used without any code changes to this repository. While
|
||||
some widely used plugins (and a few less-popular legacy ones) live in this repo,
|
||||
we're reluctant to add more.
|
||||
|
||||
If you have a good reason why the CNI maintainers should take custody of your
|
||||
plugin, please open an issue or PR.
|
36
vendor/github.com/containernetworking/cni/DCO
generated
vendored
Normal file
36
vendor/github.com/containernetworking/cni/DCO
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
Developer Certificate of Origin
|
||||
Version 1.1
|
||||
|
||||
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
|
||||
660 York Street, Suite 102,
|
||||
San Francisco, CA 94110 USA
|
||||
|
||||
Everyone is permitted to copy and distribute verbatim copies of this
|
||||
license document, but changing it is not allowed.
|
||||
|
||||
|
||||
Developer's Certificate of Origin 1.1
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
42
vendor/github.com/containernetworking/cni/Documentation/bridge.md
generated
vendored
Normal file
42
vendor/github.com/containernetworking/cni/Documentation/bridge.md
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
# bridge plugin
|
||||
|
||||
## Overview
|
||||
|
||||
With bridge plugin, all containers (on the same host) are plugged into a bridge (virtual switch) that resides in the host network namespace.
|
||||
The containers receive one end of the veth pair with the other end connected to the bridge.
|
||||
An IP address is only assigned to one end of the veth pair -- one residing in the container.
|
||||
The bridge itself can also be assigned an IP address, turning it into a gateway for the containers.
|
||||
Alternatively, the bridge can function purely in L2 mode and would need to be bridged to the host network interface (if other than container-to-container communication on the same host is desired).
|
||||
|
||||
The network configuration specifies the name of the bridge to be used.
|
||||
If the bridge is missing, the plugin will create one on first use and, if gateway mode is used, assign it an IP that was returned by IPAM plugin via the gateway field.
|
||||
|
||||
## Example configuration
|
||||
```
|
||||
{
|
||||
"name": "mynet",
|
||||
"type": "bridge",
|
||||
"bridge": "mynet0",
|
||||
"isDefaultGateway": true,
|
||||
"forceAddress": false,
|
||||
"ipMasq": true,
|
||||
"hairpinMode": true,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.10.0.0/16"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Network configuration reference
|
||||
|
||||
* `name` (string, required): the name of the network.
|
||||
* `type` (string, required): "bridge".
|
||||
* `bridge` (string, optional): name of the bridge to use/create. Defaults to "cni0".
|
||||
* `isGateway` (boolean, optional): assign an IP address to the bridge. Defaults to false.
|
||||
* `isDefaultGateway` (boolean, optional): Sets isGateway to true and makes the assigned IP the default route. Defaults to false.
|
||||
* `forceAddress` (boolean, optional): Indicates if a new IP address should be set if the previous value has been changed. Defaults to false.
|
||||
* `ipMasq` (boolean, optional): set up IP Masquerade on the host for traffic originating from this network and destined outside of it. Defaults to false.
|
||||
* `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to the value chosen by the kernel.
|
||||
* `hairpinMode` (boolean, optional): set hairpin mode for interfaces on the bridge. Defaults to false.
|
||||
* `ipam` (dictionary, required): IPAM configuration to be used for this network.
|
35
vendor/github.com/containernetworking/cni/Documentation/dhcp.md
generated
vendored
Normal file
35
vendor/github.com/containernetworking/cni/Documentation/dhcp.md
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
# dhcp plugin
|
||||
|
||||
## Overview
|
||||
|
||||
With dhcp plugin the containers can get an IP allocated by a DHCP server already running on your network.
|
||||
This can be especially useful with plugin types such as [macvlan](https://github.com/containernetworking/cni/blob/master/Documentation/macvlan.md).
|
||||
Because a DHCP lease must be periodically renewed for the duration of container lifetime, a separate daemon is required to be running.
|
||||
The same plugin binary can also be run in the daemon mode.
|
||||
|
||||
## Operation
|
||||
To use the dhcp IPAM plugin, first launch the dhcp daemon:
|
||||
|
||||
```
|
||||
# Make sure the unix socket has been removed
|
||||
$ rm -f /run/cni/dhcp.sock
|
||||
$ ./dhcp daemon
|
||||
```
|
||||
|
||||
Alternatively, you can use systemd socket activation protocol.
|
||||
Be sure that the .socket file uses /run/cni/dhcp.sock as the socket path.
|
||||
|
||||
With the daemon running, containers using the dhcp plugin can be launched.
|
||||
|
||||
## Example configuration
|
||||
|
||||
```
|
||||
{
|
||||
"ipam": {
|
||||
"type": "dhcp",
|
||||
}
|
||||
}
|
||||
|
||||
## Network configuration reference
|
||||
|
||||
* `type` (string, required): "dhcp"
|
88
vendor/github.com/containernetworking/cni/Documentation/flannel.md
generated
vendored
Normal file
88
vendor/github.com/containernetworking/cni/Documentation/flannel.md
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
# flannel plugin
|
||||
|
||||
## Overview
|
||||
This plugin is designed to work in conjunction with [flannel](https://github.com/coreos/flannel), a network fabric for containers.
|
||||
When flannel daemon is started, it outputs a `/run/flannel/subnet.env` file that looks like this:
|
||||
```
|
||||
FLANNEL_NETWORK=10.1.0.0/16
|
||||
FLANNEL_SUBNET=10.1.17.1/24
|
||||
FLANNEL_MTU=1472
|
||||
FLANNEL_IPMASQ=true
|
||||
```
|
||||
|
||||
This information reflects the attributes of flannel network on the host.
|
||||
The flannel CNI plugin uses this information to configure another CNI plugin, such as bridge plugin.
|
||||
|
||||
## Operation
|
||||
Given the following network configuration file and the contents of `/run/flannel/subnet.env` above,
|
||||
```
|
||||
{
|
||||
"name": "mynet",
|
||||
"type": "flannel"
|
||||
}
|
||||
```
|
||||
the flannel plugin will generate another network configuration file:
|
||||
```
|
||||
{
|
||||
"name": "mynet",
|
||||
"type": "bridge",
|
||||
"mtu": 1472,
|
||||
"ipMasq": false,
|
||||
"isGateway": true,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.17.0/24"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
It will then invoke the bridge plugin, passing it the generated configuration.
|
||||
|
||||
As can be seen from above, the flannel plugin, by default, will delegate to the bridge plugin.
|
||||
If additional configuration values need to be passed to the bridge plugin, it can be done so via the `delegate` field:
|
||||
```
|
||||
{
|
||||
"name": "mynet",
|
||||
"type": "flannel",
|
||||
"delegate": {
|
||||
"bridge": "mynet0",
|
||||
"mtu": 1400
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This supplies a configuration parameter to the bridge plugin -- the created bridge will now be named `mynet0`.
|
||||
Notice that `mtu` has also been specified and this value will not be overwritten by flannel plugin.
|
||||
|
||||
Additionally, the `delegate` field can be used to select a different kind of plugin altogether.
|
||||
To use `ipvlan` instead of `bridge`, the following configuration can be specified:
|
||||
|
||||
```
|
||||
{
|
||||
"name": "mynet",
|
||||
"type": "flannel",
|
||||
"delegate": {
|
||||
"type": "ipvlan",
|
||||
"master": "eth0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Network configuration reference
|
||||
|
||||
* `name` (string, required): the name of the network
|
||||
* `type` (string, required): "flannel"
|
||||
* `subnetFile` (string, optional): full path to the subnet file written out by flanneld. Defaults to /run/flannel/subnet.env
|
||||
* `dataDir` (string, optional): path to directory where plugin will store generated network configuration files. Defaults to `/var/lib/cni/flannel`
|
||||
* `delegate` (dictionary, optional): specifies configuration options for the delegated plugin.
|
||||
|
||||
flannel plugin will always set the following fields in the delegated plugin configuration:
|
||||
|
||||
* `name`: value of its "name" field.
|
||||
* `ipam`: "host-local" type will be used with "subnet" set to `$FLANNEL_SUBNET`.
|
||||
|
||||
flannel plugin will set the following fields in the delegated plugin configuration if they are not present:
|
||||
* `ipMasq`: the inverse of `$FLANNEL_IPMASQ`
|
||||
* `mtu`: `$FLANNEL_MTU`
|
||||
|
||||
Additionally, for the bridge plugin, `isGateway` will be set to `true`, if not present.
|
42
vendor/github.com/containernetworking/cni/Documentation/host-local.md
generated
vendored
Normal file
42
vendor/github.com/containernetworking/cni/Documentation/host-local.md
generated
vendored
Normal file
|
@ -0,0 +1,42 @@
|
|||
# host-local plugin
|
||||
|
||||
## Overview
|
||||
|
||||
host-local IPAM plugin allocates IPv4 addresses out of a specified address range.
|
||||
It stores the state locally on the host filesystem, therefore ensuring uniqueness of IP addresses on a single host.
|
||||
|
||||
## Example configuration
|
||||
```
|
||||
{
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.10.0.0/16",
|
||||
"rangeStart": "10.10.1.20",
|
||||
"rangeEnd": "10.10.3.50",
|
||||
"gateway": "10.10.0.254",
|
||||
"routes": [
|
||||
{ "dst": "0.0.0.0/0" },
|
||||
{ "dst": "192.168.0.0/16", "gw": "10.10.5.1" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Network configuration reference
|
||||
|
||||
* `type` (string, required): "host-local".
|
||||
* `subnet` (string, required): CIDR block to allocate out of.
|
||||
* `rangeStart` (string, optional): IP inside of "subnet" from which to start allocating addresses. Defaults to ".2" IP inside of the "subnet" block.
|
||||
* `rangeEnd` (string, optional): IP inside of "subnet" with which to end allocating addresses. Defaults to ".254" IP inside of the "subnet" block.
|
||||
* `gateway` (string, optional): IP inside of "subnet" to designate as the gateway. Defaults to ".1" IP inside of the "subnet" block.
|
||||
* `routes` (string, optional): list of routes to add to the container namespace. Each route is a dictionary with "dst" and optional "gw" fields. If "gw" is omitted, value of "gateway" will be used.
|
||||
* `resolvConf` (string, optional): Path to a `resolv.conf` on the host to parse and return as the DNS configuration
|
||||
|
||||
## Supported arguments
|
||||
The following [CNI_ARGS](https://github.com/containernetworking/cni/blob/master/SPEC.md#parameters) are supported:
|
||||
|
||||
* `ip`: request a specific IP address from the subnet. If it's not available, the plugin will exit with an error
|
||||
|
||||
## Files
|
||||
|
||||
Allocated IP addresses are stored as files in /var/lib/cni/networks/$NETWORK_NAME.
|
40
vendor/github.com/containernetworking/cni/Documentation/ipvlan.md
generated
vendored
Normal file
40
vendor/github.com/containernetworking/cni/Documentation/ipvlan.md
generated
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
# ipvlan plugin
|
||||
|
||||
## Overview
|
||||
|
||||
ipvlan is a new [addition](https://lwn.net/Articles/620087/) to the Linux kernel.
|
||||
Like its cousin macvlan, it virtualizes the host interface.
|
||||
However unlike macvlan which generates a new MAC address for each interface, ipvlan devices all share the same MAC.
|
||||
The kernel driver inspects the IP address of each packet when making a decision about which virtual interface should process the packet.
|
||||
|
||||
Because all ipvlan interfaces share the MAC address with the host interface, DHCP can only be used in conjunction with ClientID (currently not supported by DHCP plugin).
|
||||
|
||||
## Example configuration
|
||||
|
||||
```
|
||||
{
|
||||
"name": "mynet",
|
||||
"type": "ipvlan",
|
||||
"master": "eth0",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Network configuration reference
|
||||
|
||||
* `name` (string, required): the name of the network.
|
||||
* `type` (string, required): "ipvlan".
|
||||
* `master` (string, required): name of the host interface to enslave.
|
||||
* `mode` (string, optional): one of "l2", "l3". Defaults to "l2".
|
||||
* `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to the value chosen by the kernel.
|
||||
* `ipam` (dictionary, required): IPAM configuration to be used for this network.
|
||||
|
||||
## Notes
|
||||
|
||||
* `ipvlan` does not allow virtual interfaces to communicate with the master interface.
|
||||
Therefore the container will not be able to reach the host via `ipvlan` interface.
|
||||
Be sure to also have container join a network that provides connectivity to the host (e.g. `ptp`).
|
||||
* A single master interface can not be enslaved by both `macvlan` and `ipvlan`.
|
34
vendor/github.com/containernetworking/cni/Documentation/macvlan.md
generated
vendored
Normal file
34
vendor/github.com/containernetworking/cni/Documentation/macvlan.md
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
# macvlan plugin
|
||||
|
||||
## Overview
|
||||
|
||||
[macvlan](http://backreference.org/2014/03/20/some-notes-on-macvlanmacvtap/) functions like a switch that is already connected to the host interface.
|
||||
A host interface gets "enslaved" with the virtual interfaces sharing the physical device but having distinct MAC addresses.
|
||||
Since each macvlan interface has its own MAC address, it makes it easy to use with existing DHCP servers already present on the network.
|
||||
|
||||
## Example configuration
|
||||
|
||||
```
|
||||
{
|
||||
"name": "mynet",
|
||||
"type": "macvlan",
|
||||
"master": "eth0",
|
||||
"ipam": {
|
||||
"type": "dhcp"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Network configuration reference
|
||||
|
||||
* `name` (string, required): the name of the network
|
||||
* `type` (string, required): "macvlan"
|
||||
* `master` (string, required): name of the host interface to enslave
|
||||
* `mode` (string, optional): one of "bridge", "private", "vepa", "passthrough". Defaults to "bridge".
|
||||
* `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to the value chosen by the kernel.
|
||||
* `ipam` (dictionary, required): IPAM configuration to be used for this network.
|
||||
|
||||
## Notes
|
||||
|
||||
* If are testing on a laptop, please remember that most wireless cards do not support being enslaved by macvlan.
|
||||
* A single master interface can not be enslaved by both `macvlan` and `ipvlan`.
|
32
vendor/github.com/containernetworking/cni/Documentation/ptp.md
generated
vendored
Normal file
32
vendor/github.com/containernetworking/cni/Documentation/ptp.md
generated
vendored
Normal file
|
@ -0,0 +1,32 @@
|
|||
# ptp plugin
|
||||
|
||||
## Overview
|
||||
The ptp plugin creates a point-to-point link between a container and the host by using a veth device.
|
||||
One end of the veth pair is placed inside a container and the other end resides on the host.
|
||||
The host-local IPAM plugin can be used to allocate an IP address to the container.
|
||||
The traffic of the container interface will be routed through the interface of the host.
|
||||
|
||||
## Example network configuration
|
||||
|
||||
```
|
||||
{
|
||||
"name": "mynet",
|
||||
"type": "ptp",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.1.0/24"
|
||||
},
|
||||
"dns": {
|
||||
"nameservers": [ "10.1.1.1", "8.8.8.8" ]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Network configuration reference
|
||||
|
||||
* `name` (string, required): the name of the network
|
||||
* `type` (string, required): "ptp"
|
||||
* `ipMasq` (boolean, optional): set up IP Masquerade on the host for traffic originating from this network and destined outside of it. Defaults to false.
|
||||
* `mtu` (integer, optional): explicitly set MTU to the specified value. Defaults to value chosen by the kernel.
|
||||
* `ipam` (dictionary, required): IPAM configuration to be used for this network.
|
||||
* `dns` (dictionary, optional): DNS information to return as described in the [Result](/SPEC.md#result).
|
34
vendor/github.com/containernetworking/cni/Documentation/tuning.md
generated
vendored
Normal file
34
vendor/github.com/containernetworking/cni/Documentation/tuning.md
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
# tuning plugin
|
||||
|
||||
## Overview
|
||||
|
||||
This plugin can change some system controls (sysctls) in the network namespace.
|
||||
It does not create any network interfaces and therefore does not bring connectivity by itself.
|
||||
It is only useful when used in addition to other plugins.
|
||||
|
||||
## Operation
|
||||
The following network configuration file
|
||||
```
|
||||
{
|
||||
"name": "mytuning",
|
||||
"type": "tuning",
|
||||
"sysctl": {
|
||||
"net.core.somaxconn": "500"
|
||||
}
|
||||
}
|
||||
```
|
||||
will set /proc/sys/net/core/somaxconn to 500.
|
||||
Other sysctls can be modified as long as they belong to the network namespace (`/proc/sys/net/*`).
|
||||
|
||||
A successful result would simply be:
|
||||
```
|
||||
{ }
|
||||
```
|
||||
|
||||
## Network sysctls documentation
|
||||
|
||||
Some network sysctls are documented in the Linux sources:
|
||||
|
||||
- [Documentation/sysctl/net.txt](https://www.kernel.org/doc/Documentation/sysctl/net.txt)
|
||||
- [Documentation/networking/ip-sysctl.txt](https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt)
|
||||
- [Documentation/networking/](https://www.kernel.org/doc/Documentation/networking/)
|
234
vendor/github.com/containernetworking/cni/Godeps/Godeps.json
generated
vendored
Normal file
234
vendor/github.com/containernetworking/cni/Godeps/Godeps.json
generated
vendored
Normal file
|
@ -0,0 +1,234 @@
|
|||
{
|
||||
"ImportPath": "github.com/containernetworking/cni",
|
||||
"GoVersion": "go1.6",
|
||||
"GodepVersion": "v75",
|
||||
"Packages": [
|
||||
"./..."
|
||||
],
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "github.com/coreos/go-iptables/iptables",
|
||||
"Comment": "v0.1.0",
|
||||
"Rev": "fbb73372b87f6e89951c2b6b31470c2c9d5cfae3"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/coreos/go-systemd/activation",
|
||||
"Comment": "v2-53-g2688e91",
|
||||
"Rev": "2688e91251d9d8e404e86dd8f096e23b2f086958"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/d2g/dhcp4",
|
||||
"Rev": "f0e4d29ff0231dce36e250b2ed9ff08412584bca"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/d2g/dhcp4client",
|
||||
"Rev": "bed07e1bc5b85f69c6f0fd73393aa35ec68ed892"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/config",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/extensions/table",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/ginkgo",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/ginkgo/convert",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/ginkgo/interrupthandler",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/ginkgo/nodot",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/ginkgo/testrunner",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/ginkgo/testsuite",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/ginkgo/watch",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/integration",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/internal/codelocation",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/internal/containernode",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/internal/failer",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/internal/leafnodes",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/internal/remote",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/internal/spec",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/internal/specrunner",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/internal/suite",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/internal/testingtproxy",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/internal/writer",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/reporters",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/reporters/stenographer",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/ginkgo/types",
|
||||
"Comment": "v1.2.0-29-g7f8ab55",
|
||||
"Rev": "7f8ab55aaf3b86885aa55b762e803744d1674700"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/gomega",
|
||||
"Comment": "v1.0-71-g2152b45",
|
||||
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/gomega/format",
|
||||
"Comment": "v1.0-71-g2152b45",
|
||||
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/gomega/gbytes",
|
||||
"Comment": "v1.0-71-g2152b45",
|
||||
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/gomega/gexec",
|
||||
"Comment": "v1.0-71-g2152b45",
|
||||
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/gomega/internal/assertion",
|
||||
"Comment": "v1.0-71-g2152b45",
|
||||
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/gomega/internal/asyncassertion",
|
||||
"Comment": "v1.0-71-g2152b45",
|
||||
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/gomega/internal/oraclematcher",
|
||||
"Comment": "v1.0-71-g2152b45",
|
||||
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/gomega/internal/testingtsupport",
|
||||
"Comment": "v1.0-71-g2152b45",
|
||||
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/gomega/matchers",
|
||||
"Comment": "v1.0-71-g2152b45",
|
||||
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/bipartitegraph",
|
||||
"Comment": "v1.0-71-g2152b45",
|
||||
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/edge",
|
||||
"Comment": "v1.0-71-g2152b45",
|
||||
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/node",
|
||||
"Comment": "v1.0-71-g2152b45",
|
||||
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/gomega/matchers/support/goraph/util",
|
||||
"Comment": "v1.0-71-g2152b45",
|
||||
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/onsi/gomega/types",
|
||||
"Comment": "v1.0-71-g2152b45",
|
||||
"Rev": "2152b45fa28a361beba9aab0885972323a444e28"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vishvananda/netlink",
|
||||
"Rev": "a1f8555521646b5a9800f39c5fd477597697135c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vishvananda/netlink/nl",
|
||||
"Rev": "a1f8555521646b5a9800f39c5fd477597697135c"
|
||||
},
|
||||
{
|
||||
"ImportPath": "golang.org/x/sys/unix",
|
||||
"Rev": "076b546753157f758b316e59bcb51e6807c04057"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/vishvananda/netns",
|
||||
"Rev": "8ba1072b58e0c2a240eb5f6120165c7776c3e7b8"
|
||||
}
|
||||
]
|
||||
}
|
5
vendor/github.com/containernetworking/cni/Godeps/Readme
generated
vendored
Normal file
5
vendor/github.com/containernetworking/cni/Godeps/Readme
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
This directory tree is generated automatically by godep.
|
||||
|
||||
Please do not edit.
|
||||
|
||||
See https://github.com/tools/godep for more information.
|
6
vendor/github.com/containernetworking/cni/MAINTAINERS
generated
vendored
Normal file
6
vendor/github.com/containernetworking/cni/MAINTAINERS
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
Casey Callendrello <casey.callendrello@coreos.com> (@squeed)
|
||||
Dan Williams <dcbw@redhat.com> (@dcbw)
|
||||
Gabe Rosenhouse <grosenhouse@pivotal.io> (@rosenhouse)
|
||||
Michael Bridgen <michael@weave.works> (@squaremo)
|
||||
Stefan Junker <stefan.junker@coreos.com> (@steveeJ)
|
||||
Tom Denham <tom@tigera.io> (@tomdee)
|
176
vendor/github.com/containernetworking/cni/README.md
generated
vendored
Normal file
176
vendor/github.com/containernetworking/cni/README.md
generated
vendored
Normal file
|
@ -0,0 +1,176 @@
|
|||
[](https://travis-ci.org/containernetworking/cni)
|
||||
[](https://coveralls.io/github/containernetworking/cni?branch=master)
|
||||
[](https://cryptic-tundra-43194.herokuapp.com/)
|
||||
|
||||
# CNI - the Container Network Interface
|
||||
|
||||
## What is CNI?
|
||||
|
||||
The CNI (_Container Network Interface_) project consists of a specification and libraries for writing plugins to configure network interfaces in Linux containers, along with a number of supported plugins.
|
||||
CNI concerns itself only with network connectivity of containers and removing allocated resources when the container is deleted.
|
||||
Because of this focus, CNI has a wide range of support and the specification is simple to implement.
|
||||
|
||||
As well as the [specification](SPEC.md), this repository contains the Go source code of a library for integrating CNI into applications, an example command-line tool, a template for making new plugins, and the supported plugins.
|
||||
|
||||
The template code makes it straight-forward to create a CNI plugin for an existing container networking project.
|
||||
CNI also makes a good framework for creating a new container networking project from scratch.
|
||||
|
||||
## Why develop CNI?
|
||||
|
||||
Application containers on Linux are a rapidly evolving area, and within this area networking is not well addressed as it is highly environment-specific.
|
||||
We believe that many container runtimes and orchestrators will seek to solve the same problem of making the network layer pluggable.
|
||||
|
||||
To avoid duplication, we think it is prudent to define a common interface between the network plugins and container execution: hence we put forward this specification, along with libraries for Go and a set of plugins.
|
||||
|
||||
## Who is using CNI?
|
||||
### Container runtimes
|
||||
- [rkt - container engine](https://coreos.com/blog/rkt-cni-networking.html)
|
||||
- [Kurma - container runtime](http://kurma.io/)
|
||||
- [Kubernetes - a system to simplify container operations](http://kubernetes.io/docs/admin/network-plugins/)
|
||||
- [Cloud Foundry - a platform for cloud applications](https://github.com/cloudfoundry-incubator/netman-release)
|
||||
- [Mesos - a distributed systems kernel](https://github.com/apache/mesos/blob/master/docs/cni.md)
|
||||
|
||||
### 3rd party plugins
|
||||
- [Project Calico - a layer 3 virtual network](https://github.com/projectcalico/calico-cni)
|
||||
- [Weave - a multi-host Docker network](https://github.com/weaveworks/weave)
|
||||
- [Contiv Networking - policy networking for various use cases](https://github.com/contiv/netplugin)
|
||||
- [SR-IOV](https://github.com/hustcat/sriov-cni)
|
||||
- [Cilium - BPF & XDP for containers](https://github.com/cilium/cilium)
|
||||
- [Infoblox - enterprise IP address management for containers](https://github.com/infobloxopen/cni-infoblox)
|
||||
|
||||
The CNI team also maintains some [core plugins](plugins).
|
||||
|
||||
|
||||
## Contributing to CNI
|
||||
|
||||
We welcome contributions, including [bug reports](https://github.com/containernetworking/cni/issues), and code and documentation improvements.
|
||||
If you intend to contribute to code or documentation, please read [CONTRIBUTING.md](CONTRIBUTING.md). Also see the [contact section](#contact) in this README.
|
||||
|
||||
## How do I use CNI?
|
||||
|
||||
### Requirements
|
||||
|
||||
CNI requires Go 1.5+ to build.
|
||||
|
||||
Go 1.5 users will need to set GO15VENDOREXPERIMENT=1 to get vendored
|
||||
dependencies. This flag is set by default in 1.6.
|
||||
|
||||
### Included Plugins
|
||||
|
||||
This repository includes a number of common plugins in the `plugins/` directory.
|
||||
Please see the [Documentation/](Documentation/) directory for documentation about particular plugins.
|
||||
|
||||
### Running the plugins
|
||||
|
||||
The scripts/ directory contains two scripts, `priv-net-run.sh` and `docker-run.sh`, that can be used to exercise the plugins.
|
||||
|
||||
**note - priv-net-run.sh depends on `jq`**
|
||||
|
||||
Start out by creating a netconf file to describe a network:
|
||||
|
||||
```bash
|
||||
$ mkdir -p /etc/cni/net.d
|
||||
$ cat >/etc/cni/net.d/10-mynet.conf <<EOF
|
||||
{
|
||||
"cniVersion": "0.2.0",
|
||||
"name": "mynet",
|
||||
"type": "bridge",
|
||||
"bridge": "cni0",
|
||||
"isGateway": true,
|
||||
"ipMasq": true,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.22.0.0/16",
|
||||
"routes": [
|
||||
{ "dst": "0.0.0.0/0" }
|
||||
]
|
||||
}
|
||||
}
|
||||
EOF
|
||||
$ cat >/etc/cni/net.d/99-loopback.conf <<EOF
|
||||
{
|
||||
"cniVersion": "0.2.0",
|
||||
"type": "loopback"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
The directory `/etc/cni/net.d` is the default location in which the scripts will look for net configurations.
|
||||
|
||||
Next, build the plugins:
|
||||
|
||||
```bash
|
||||
$ ./build
|
||||
```
|
||||
|
||||
Finally, execute a command (`ifconfig` in this example) in a private network namespace that has joined the `mynet` network:
|
||||
|
||||
```bash
|
||||
$ CNI_PATH=`pwd`/bin
|
||||
$ cd scripts
|
||||
$ sudo CNI_PATH=$CNI_PATH ./priv-net-run.sh ifconfig
|
||||
eth0 Link encap:Ethernet HWaddr f2:c2:6f:54:b8:2b
|
||||
inet addr:10.22.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
|
||||
inet6 addr: fe80::f0c2:6fff:fe54:b82b/64 Scope:Link
|
||||
UP BROADCAST MULTICAST MTU:1500 Metric:1
|
||||
RX packets:1 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:0 errors:0 dropped:1 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:0
|
||||
RX bytes:90 (90.0 B) TX bytes:0 (0.0 B)
|
||||
|
||||
lo Link encap:Local Loopback
|
||||
inet addr:127.0.0.1 Mask:255.0.0.0
|
||||
inet6 addr: ::1/128 Scope:Host
|
||||
UP LOOPBACK RUNNING MTU:65536 Metric:1
|
||||
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:0
|
||||
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
|
||||
```
|
||||
|
||||
The environment variable `CNI_PATH` tells the scripts and library where to look for plugin executables.
|
||||
|
||||
## Running a Docker container with network namespace set up by CNI plugins
|
||||
|
||||
Use the instructions in the previous section to define a netconf and build the plugins.
|
||||
Next, docker-run.sh script wraps `docker run`, to execute the plugins prior to entering the container:
|
||||
|
||||
```bash
|
||||
$ CNI_PATH=`pwd`/bin
|
||||
$ cd scripts
|
||||
$ sudo CNI_PATH=$CNI_PATH ./docker-run.sh --rm busybox:latest ifconfig
|
||||
eth0 Link encap:Ethernet HWaddr fa:60:70:aa:07:d1
|
||||
inet addr:10.22.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
|
||||
inet6 addr: fe80::f860:70ff:feaa:7d1/64 Scope:Link
|
||||
UP BROADCAST MULTICAST MTU:1500 Metric:1
|
||||
RX packets:1 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:0 errors:0 dropped:1 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:0
|
||||
RX bytes:90 (90.0 B) TX bytes:0 (0.0 B)
|
||||
|
||||
lo Link encap:Local Loopback
|
||||
inet addr:127.0.0.1 Mask:255.0.0.0
|
||||
inet6 addr: ::1/128 Scope:Host
|
||||
UP LOOPBACK RUNNING MTU:65536 Metric:1
|
||||
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
|
||||
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
|
||||
collisions:0 txqueuelen:0
|
||||
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
|
||||
```
|
||||
|
||||
## What might CNI do in the future?
|
||||
|
||||
CNI currently covers a wide range of needs for network configuration due to it simple model and API.
|
||||
However, in the future CNI might want to branch out into other directions:
|
||||
|
||||
- Dynamic updates to existing network configuration
|
||||
- Dynamic policies for network bandwidth and firewall rules
|
||||
|
||||
If these topics of are interest, please contact the team via the mailing list or IRC and find some like-minded people in the community to put a proposal together.
|
||||
|
||||
## Contact
|
||||
|
||||
For any questions about CNI, please reach out on the mailing list:
|
||||
- Email: [cni-dev](https://groups.google.com/forum/#!forum/cni-dev)
|
||||
- IRC: #[containernetworking](irc://irc.freenode.org:6667/#containernetworking) channel on freenode.org
|
||||
- Slack: [containernetworking.slack.com](https://cryptic-tundra-43194.herokuapp.com)
|
33
vendor/github.com/containernetworking/cni/ROADMAP.md
generated
vendored
Normal file
33
vendor/github.com/containernetworking/cni/ROADMAP.md
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
# CNI Roadmap
|
||||
|
||||
This document defines a high level roadmap for CNI development.
|
||||
The list below is not complete, and we advise to get the current project state from the [milestones defined in GitHub](https://github.com/containernetworking/cni/milestones).
|
||||
|
||||
## CNI Milestones
|
||||
|
||||
### [v0.2.0](https://github.com/containernetworking/cni/milestones/v0.2.0)
|
||||
|
||||
* Signed release binaries
|
||||
* Introduction of a testing strategy/framework
|
||||
|
||||
### [v0.3.0](https://github.com/containernetworking/cni/milestones/v0.3.0)
|
||||
|
||||
* Further increase test coverage
|
||||
* Simpler default route handling in bridge plugin
|
||||
* Clarify project description, documentation and contribution guidelines
|
||||
|
||||
### [v0.4.0](https://github.com/containernetworking/cni/milestones/v0.4.0)
|
||||
|
||||
* Further increase test coverage
|
||||
* Simpler bridging of host interface
|
||||
* Improve IPAM allocator predictability
|
||||
* Allow in- and output of arbitrary K/V pairs for plugins
|
||||
|
||||
### [v1.0.0](https://github.com/containernetworking/cni/milestones/v1.0.0)
|
||||
|
||||
- Plugin composition functionality
|
||||
- IPv6 support
|
||||
- Stable SPEC
|
||||
- Strategy and tooling for backwards compatibility
|
||||
- Complete test coverage
|
||||
- Integrate build artefact generation with CI
|
282
vendor/github.com/containernetworking/cni/SPEC.md
generated
vendored
Normal file
282
vendor/github.com/containernetworking/cni/SPEC.md
generated
vendored
Normal file
|
@ -0,0 +1,282 @@
|
|||
# Container Networking Interface Proposal
|
||||
|
||||
## Version
|
||||
This is CNI **spec** version **0.2.0**.
|
||||
|
||||
Note that this is **independent from the version of the CNI library and plugins** in this repository (e.g. the versions of [releases](https://github.com/containernetworking/cni/releases)).
|
||||
|
||||
## Overview
|
||||
|
||||
This document proposes a generic plugin-based networking solution for application containers on Linux, the _Container Networking Interface_, or _CNI_.
|
||||
It is derived from the [rkt Networking Proposal][rkt-networking-proposal], which aimed to satisfy many of the [design considerations][rkt-networking-design] for networking in [rkt][rkt-github].
|
||||
|
||||
For the purposes of this proposal, we define two terms very specifically:
|
||||
- _container_ can be considered synonymous with a [Linux _network namespace_][namespaces]. What unit this corresponds to depends on a particular container runtime implementation: for example, in implementations of the [App Container Spec][appc-github] like rkt, each _pod_ runs in a unique network namespace. In [Docker][docker], on the other hand, network namespaces generally exist for each separate Docker container.
|
||||
- _network_ refers to a group of entities that are uniquely addressable that can communicate amongst each other. This could be either an individual container (as specified above), a machine, or some other network device (e.g. a router). Containers can be conceptually _added to_ or _removed from_ one or more networks.
|
||||
|
||||
[rkt-networking-proposal]: https://docs.google.com/a/coreos.com/document/d/1PUeV68q9muEmkHmRuW10HQ6cHgd4819_67pIxDRVNlM/edit#heading=h.ievko3xsjwxd
|
||||
[rkt-networking-design]:
|
||||
https://docs.google.com/a/coreos.com/document/d/1CTAL4gwqRofjxyp4tTkbgHtAwb2YCcP14UEbHNizd8g
|
||||
[rkt-github]: https://github.com/coreos/rkt
|
||||
[namespaces]: http://man7.org/linux/man-pages/man7/namespaces.7.html
|
||||
[appc-github]: https://github.com/appc/spec
|
||||
[docker]: https://docker.com
|
||||
|
||||
## General considerations
|
||||
|
||||
The intention is for the container runtime to first create a new network namespace for the container.
|
||||
It then determines which networks this container should belong to and for each network, which plugin must be executed.
|
||||
The network configuration is in JSON format and can easily be stored in a file.
|
||||
The network configuration includes mandatory fields such as "name" and "type" as well as plugin (type) specific ones.
|
||||
The network configuration allows for fields to change values between invocations. For this purpose there is an optional field "args" which should contain the varying information.
|
||||
The container runtime sequentially sets up the networks by executing the corresponding plugin for each network.
|
||||
Upon completion of the container lifecycle, the runtime executes the plugins in reverse order (relative to the order in which they were added) to disconnect them from the networks.
|
||||
|
||||
## CNI Plugin
|
||||
|
||||
### Overview
|
||||
|
||||
Each CNI plugin is implemented as an executable that is invoked by the container management system (e.g. rkt or Docker).
|
||||
|
||||
A CNI plugin is responsible for inserting a network interface into the container network namespace (e.g. one end of a veth pair) and making any necessary changes on the host (e.g. attaching other end of veth into a bridge).
|
||||
It should then assign the IP to the interface and setup the routes consistent with IP Address Management section by invoking appropriate IPAM plugin.
|
||||
|
||||
### Parameters
|
||||
|
||||
The operations that the CNI plugin needs to support are:
|
||||
|
||||
|
||||
- Add container to network
|
||||
- Parameters:
|
||||
- **Version**. The version of CNI spec that the caller is using (container management system or the invoking plugin).
|
||||
- **Container ID**. This is optional but recommended, and should be unique across an administrative domain while the container is live (it may be reused in the future). For example, an environment with an IPAM system may require that each container is allocated a unique ID and that each IP allocation can thus be correlated back to a particular container. As another example, in appc implementations this would be the _pod ID_.
|
||||
- **Network namespace path**. This represents the path to the network namespace to be added, i.e. /proc/[pid]/ns/net or a bind-mount/link to it.
|
||||
- **Network configuration**. This is a JSON document describing a network to which a container can be joined. The schema is described below.
|
||||
- **Extra arguments**. This provides an alternative mechanism to allow simple configuration of CNI plugins on a per-container basis.
|
||||
- **Name of the interface inside the container**. This is the name that should be assigned to the interface created inside the container (network namespace); consequently it must comply with the standard Linux restrictions on interface names.
|
||||
- Result:
|
||||
- **IPs assigned to the interface**. This is either an IPv4 address, an IPv6 address, or both.
|
||||
- **DNS information**. Dictionary that includes DNS information for nameservers, domain, search domains and options.
|
||||
|
||||
- Delete container from network
|
||||
- Parameters:
|
||||
- **Version**. The version of CNI spec that the caller is using (container management system or the invoking plugin).
|
||||
- **Container ID**, as defined above.
|
||||
- **Network namespace path**, as defined above.
|
||||
- **Network configuration**, as defined above.
|
||||
- **Extra arguments**, as defined above.
|
||||
- **Name of the interface inside the container**, as defined above.
|
||||
|
||||
- Report version
|
||||
- Parameters: NONE.
|
||||
- Result: information about the CNI spec versions supported by the plugin
|
||||
|
||||
```
|
||||
{
|
||||
"cniVersion": "0.2.0", // the version of the CNI spec in use for this output
|
||||
"supportedVersions": [ "0.1.0", "0.2.0" ] // the list of CNI spec versions that this plugin supports
|
||||
}
|
||||
```
|
||||
|
||||
The executable command-line API uses the type of network (see [Network Configuration](#network-configuration) below) as the name of the executable to invoke.
|
||||
It will then look for this executable in a list of predefined directories. Once found, it will invoke the executable using the following environment variables for argument passing:
|
||||
- `CNI_COMMAND`: indicates the desired operation; `ADD`, `DEL` or `VERSION`.
|
||||
- `CNI_CONTAINERID`: Container ID
|
||||
- `CNI_NETNS`: Path to network namespace file
|
||||
- `CNI_IFNAME`: Interface name to set up
|
||||
- `CNI_ARGS`: Extra arguments passed in by the user at invocation time. Alphanumeric key-value pairs separated by semicolons; for example, "FOO=BAR;ABC=123"
|
||||
- `CNI_PATH`: Colon-separated list of paths to search for CNI plugin executables
|
||||
|
||||
Network configuration in JSON format is streamed to the plugin through stdin. This means it is not tied to a particular file on disk and can contain information which changes between invocations.
|
||||
|
||||
|
||||
### Result
|
||||
|
||||
Success is indicated by a return code of zero and the following JSON printed to stdout in the case of the ADD command. This should be the same output as was returned by the IPAM plugin (see [IP Allocation](#ip-allocation) for details).
|
||||
|
||||
```
|
||||
{
|
||||
"cniVersion": "0.2.0",
|
||||
"ip4": {
|
||||
"ip": <ipv4-and-subnet-in-CIDR>,
|
||||
"gateway": <ipv4-of-the-gateway>, (optional)
|
||||
"routes": <list-of-ipv4-routes> (optional)
|
||||
},
|
||||
"ip6": {
|
||||
"ip": <ipv6-and-subnet-in-CIDR>,
|
||||
"gateway": <ipv6-of-the-gateway>, (optional)
|
||||
"routes": <list-of-ipv6-routes> (optional)
|
||||
},
|
||||
"dns": {
|
||||
"nameservers": <list-of-nameservers> (optional)
|
||||
"domain": <name-of-local-domain> (optional)
|
||||
"search": <list-of-additional-search-domains> (optional)
|
||||
"options": <list-of-options> (optional)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`cniVersion` specifies a [Semantic Version 2.0](http://semver.org) of CNI specification used by the plugin.
|
||||
`dns` field contains a dictionary consisting of common DNS information that this network is aware of.
|
||||
The result is returned in the same format as specified in the [configuration](#network-configuration).
|
||||
The specification does not declare how this information must be processed by CNI consumers.
|
||||
Examples include generating an `/etc/resolv.conf` file to be injected into the container filesystem or running a DNS forwarder on the host.
|
||||
|
||||
Errors are indicated by a non-zero return code and the following JSON being printed to stdout:
|
||||
```
|
||||
{
|
||||
"cniVersion": "0.2.0",
|
||||
"code": <numeric-error-code>,
|
||||
"msg": <short-error-message>,
|
||||
"details": <long-error-message> (optional)
|
||||
}
|
||||
```
|
||||
|
||||
`cniVersion` specifies a [Semantic Version 2.0](http://semver.org) of CNI specification used by the plugin.
|
||||
Error codes 0-99 are reserved for well-known errors (see [Well-known Error Codes](#well-known-error-codes) section).
|
||||
Values of 100+ can be freely used for plugin specific errors.
|
||||
|
||||
In addition, stderr can be used for unstructured output such as logs.
|
||||
|
||||
### Network Configuration
|
||||
|
||||
The network configuration is described in JSON form. The configuration can be stored on disk or generated from other sources by the container runtime. The following fields are well-known and have the following meaning:
|
||||
- `cniVersion` (string): [Semantic Version 2.0](http://semver.org) of CNI specification to which this configuration conforms.
|
||||
- `name` (string): Network name. This should be unique across all containers on the host (or other administrative domain).
|
||||
- `type` (string): Refers to the filename of the CNI plugin executable.
|
||||
- `args` (dictionary): Optional additional arguments provided by the container runtime. For example a dictionary of labels could be passed to CNI plugins by adding them to a labels field under `args`.
|
||||
- `ipMasq` (boolean): Optional (if supported by the plugin). Set up an IP masquerade on the host for this network. This is necessary if the host will act as a gateway to subnets that are not able to route to the IP assigned to the container.
|
||||
- `ipam`: Dictionary with IPAM specific values:
|
||||
- `type` (string): Refers to the filename of the IPAM plugin executable.
|
||||
- `routes` (list): List of subnets (in CIDR notation) that the CNI plugin should ensure are reachable by routing them through the network. Each entry is a dictionary containing:
|
||||
- `dst` (string): subnet in CIDR notation
|
||||
- `gw` (string): IP address of the gateway to use. If not specified, the default gateway for the subnet is assumed (as determined by the IPAM plugin).
|
||||
- `dns`: Dictionary with DNS specific values:
|
||||
- `nameservers` (list of strings): list of a priority-ordered list of DNS nameservers that this network is aware of. Each entry in the list is a string containing either an IPv4 or an IPv6 address.
|
||||
- `domain` (string): the local domain used for short hostname lookups.
|
||||
- `search` (list of strings): list of priority ordered search domains for short hostname lookups. Will be preferred over `domain` by most resolvers.
|
||||
- `options` (list of strings): list of options that can be passed to the resolver
|
||||
|
||||
Plugins may define additional fields that they accept and may generate an error if called with unknown fields. The exception to this is the `args` field may be used to pass arbitrary data which may be ignored by plugins.
|
||||
### Example configurations
|
||||
|
||||
```json
|
||||
{
|
||||
"cniVersion": "0.2.0",
|
||||
"name": "dbnet",
|
||||
"type": "bridge",
|
||||
// type (plugin) specific
|
||||
"bridge": "cni0",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
// ipam specific
|
||||
"subnet": "10.1.0.0/16",
|
||||
"gateway": "10.1.0.1"
|
||||
},
|
||||
"dns": {
|
||||
"nameservers": [ "10.1.0.1" ]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"cniVersion": "0.2.0",
|
||||
"name": "pci",
|
||||
"type": "ovs",
|
||||
// type (plugin) specific
|
||||
"bridge": "ovs0",
|
||||
"vxlanID": 42,
|
||||
"ipam": {
|
||||
"type": "dhcp",
|
||||
"routes": [ { "dst": "10.3.0.0/16" }, { "dst": "10.4.0.0/16" } ]
|
||||
}
|
||||
// args may be ignored by plugins
|
||||
"args": {
|
||||
"labels" : {
|
||||
"appVersion" : "1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"cniVersion": "0.1",
|
||||
"name": "wan",
|
||||
"type": "macvlan",
|
||||
// ipam specific
|
||||
"ipam": {
|
||||
"type": "dhcp",
|
||||
"routes": [ { "dst": "10.0.0.0/8", "gw": "10.0.0.1" } ]
|
||||
},
|
||||
"dns": {
|
||||
"nameservers": [ "10.0.0.1" ]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### IP Allocation
|
||||
|
||||
As part of its operation, a CNI plugin is expected to assign (and maintain) an IP address to the interface and install any necessary routes relevant for that interface. This gives the CNI plugin great flexibility but also places a large burden on it. Many CNI plugins would need to have the same code to support several IP management schemes that users may desire (e.g. dhcp, host-local).
|
||||
|
||||
To lessen the burden and make IP management strategy be orthogonal to the type of CNI plugin, we define a second type of plugin -- IP Address Management Plugin (IPAM plugin). It is however the responsibility of the CNI plugin to invoke the IPAM plugin at the proper moment in its execution. The IPAM plugin is expected to determine the interface IP/subnet, Gateway and Routes and return this information to the "main" plugin to apply. The IPAM plugin may obtain the information via a protocol (e.g. dhcp), data stored on a local filesystem, the "ipam" section of the Network Configuration file or a combination of the above.
|
||||
|
||||
#### IP Address Management (IPAM) Interface
|
||||
|
||||
Like CNI plugins, the IPAM plugins are invoked by running an executable. The executable is searched for in a predefined list of paths, indicated to the CNI plugin via `CNI_PATH`. The IPAM Plugin receives all the same environment variables that were passed in to the CNI plugin. Just like the CNI plugin, IPAM receives the network configuration via stdin.
|
||||
|
||||
Success is indicated by a zero return code and the following JSON being printed to stdout (in the case of the ADD command):
|
||||
|
||||
```
|
||||
{
|
||||
"cniVersion": "0.2.0",
|
||||
"ip4": {
|
||||
"ip": <ipv4-and-subnet-in-CIDR>,
|
||||
"gateway": <ipv4-of-the-gateway>, (optional)
|
||||
"routes": <list-of-ipv4-routes> (optional)
|
||||
},
|
||||
"ip6": {
|
||||
"ip": <ipv6-and-subnet-in-CIDR>,
|
||||
"gateway": <ipv6-of-the-gateway>, (optional)
|
||||
"routes": <list-of-ipv6-routes> (optional)
|
||||
},
|
||||
"dns": {
|
||||
"nameservers": <list-of-nameservers> (optional)
|
||||
"domain": <name-of-local-domain> (optional)
|
||||
"search": <list-of-search-domains> (optional)
|
||||
"options": <list-of-options> (optional)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`cniVersion` specifies a [Semantic Version 2.0](http://semver.org) of CNI specification used by the plugin.
|
||||
`gateway` is the default gateway for this subnet, if one exists.
|
||||
It does not instruct the CNI plugin to add any routes with this gateway: routes to add are specified separately via the `routes` field.
|
||||
An example use of this value is for the CNI plugin to add this IP address to the linux-bridge to make it a gateway.
|
||||
|
||||
Each route entry is a dictionary with the following fields:
|
||||
- `dst` (string): Destination subnet specified in CIDR notation.
|
||||
- `gw` (string): IP of the gateway. If omitted, a default gateway is assumed (as determined by the CNI plugin).
|
||||
|
||||
The "dns" field contains a dictionary consisting of common DNS information.
|
||||
- `nameservers` (list of strings): list of a priority-ordered list of DNS nameservers that this network is aware of. Each entry in the list is a string containing either an IPv4 or an IPv6 address.
|
||||
- `domain` (string): the local domain used for short hostname lookups.
|
||||
- `search` (list of strings): list of priority ordered search domains for short hostname lookups. Will be preferred over `domain` by most resolvers.
|
||||
- `options` (list of strings): list of options that can be passed to the resolver
|
||||
See [CNI Plugin Result](#result) section for more information.
|
||||
|
||||
Errors and logs are communicated in the same way as the CNI plugin. See [CNI Plugin Result](#result) section for details.
|
||||
|
||||
IPAM plugin examples:
|
||||
- **host-local**: Select an unused (by other containers on the same host) IP within the specified range.
|
||||
- **dhcp**: Use DHCP protocol to acquire and maintain a lease. The DHCP requests will be sent via the created container interface; therefore, the associated network must support broadcast.
|
||||
|
||||
#### Notes
|
||||
- Routes are expected to be added with a 0 metric.
|
||||
- A default route may be specified via "0.0.0.0/0". Since another network might have already configured the default route, the CNI plugin should be prepared to skip over its default route definition.
|
||||
|
||||
## Well-known Error Codes
|
||||
- `1` - Incompatible CNI version
|
||||
- `2` - Unsupported field in network configuration. The error message must contain the key and value of the unsupported field.
|
20
vendor/github.com/containernetworking/cni/Vagrantfile
generated
vendored
Normal file
20
vendor/github.com/containernetworking/cni/Vagrantfile
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
Vagrant.configure(2) do |config|
|
||||
config.vm.box = "bento/ubuntu-16.04"
|
||||
|
||||
config.vm.synced_folder ".", "/go/src/github.com/containernetworking/cni"
|
||||
|
||||
config.vm.provision "shell", inline: <<-SHELL
|
||||
set -e -x -u
|
||||
|
||||
apt-get update -y || (sleep 40 && apt-get update -y)
|
||||
apt-get install -y golang git
|
||||
echo "export GOPATH=/go" >> /root/.bashrc
|
||||
export GOPATH=/go
|
||||
go get github.com/tools/godep
|
||||
cd /go/src/github.com/containernetworking/cni
|
||||
/go/bin/godep restore
|
||||
SHELL
|
||||
end
|
29
vendor/github.com/containernetworking/cni/build
generated
vendored
Executable file
29
vendor/github.com/containernetworking/cni/build
generated
vendored
Executable file
|
@ -0,0 +1,29 @@
|
|||
#!/usr/bin/env bash
|
||||
set -e
|
||||
|
||||
ORG_PATH="github.com/containernetworking"
|
||||
REPO_PATH="${ORG_PATH}/cni"
|
||||
|
||||
if [ ! -h gopath/src/${REPO_PATH} ]; then
|
||||
mkdir -p gopath/src/${ORG_PATH}
|
||||
ln -s ../../../.. gopath/src/${REPO_PATH} || exit 255
|
||||
fi
|
||||
|
||||
export GO15VENDOREXPERIMENT=1
|
||||
export GOPATH=${PWD}/gopath
|
||||
|
||||
echo "Building API"
|
||||
go build "$@" ${REPO_PATH}/libcni
|
||||
|
||||
echo "Building reference CLI"
|
||||
go build -o ${PWD}/bin/cnitool "$@" ${REPO_PATH}/cnitool
|
||||
|
||||
echo "Building plugins"
|
||||
PLUGINS="plugins/meta/* plugins/main/* plugins/ipam/* plugins/test/*"
|
||||
for d in $PLUGINS; do
|
||||
if [ -d $d ]; then
|
||||
plugin=$(basename $d)
|
||||
echo " " $plugin
|
||||
go build -o ${PWD}/bin/$plugin "$@" ${REPO_PATH}/$d
|
||||
fi
|
||||
done
|
87
vendor/github.com/containernetworking/cni/cnitool/cni.go
generated
vendored
Normal file
87
vendor/github.com/containernetworking/cni/cnitool/cni.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright 2015 CoreOS, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
)
|
||||
|
||||
const (
|
||||
EnvCNIPath = "CNI_PATH"
|
||||
EnvNetDir = "NETCONFPATH"
|
||||
|
||||
DefaultNetDir = "/etc/cni/net.d"
|
||||
|
||||
CmdAdd = "add"
|
||||
CmdDel = "del"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 3 {
|
||||
usage()
|
||||
return
|
||||
}
|
||||
|
||||
netdir := os.Getenv(EnvNetDir)
|
||||
if netdir == "" {
|
||||
netdir = DefaultNetDir
|
||||
}
|
||||
netconf, err := libcni.LoadConf(netdir, os.Args[2])
|
||||
if err != nil {
|
||||
exit(err)
|
||||
}
|
||||
|
||||
netns := os.Args[3]
|
||||
|
||||
cninet := &libcni.CNIConfig{
|
||||
Path: strings.Split(os.Getenv(EnvCNIPath), ":"),
|
||||
}
|
||||
|
||||
rt := &libcni.RuntimeConf{
|
||||
ContainerID: "cni",
|
||||
NetNS: netns,
|
||||
IfName: "eth0",
|
||||
}
|
||||
|
||||
switch os.Args[1] {
|
||||
case CmdAdd:
|
||||
_, err := cninet.AddNetwork(netconf, rt)
|
||||
exit(err)
|
||||
case CmdDel:
|
||||
exit(cninet.DelNetwork(netconf, rt))
|
||||
}
|
||||
}
|
||||
|
||||
func usage() {
|
||||
exe := filepath.Base(os.Args[0])
|
||||
|
||||
fmt.Fprintf(os.Stderr, "%s: Add or remove network interfaces from a network namespace\n", exe)
|
||||
fmt.Fprintf(os.Stderr, " %s %s <net> <netns>\n", exe, CmdAdd)
|
||||
fmt.Fprintf(os.Stderr, " %s %s <net> <netns>\n", exe, CmdDel)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func exit(err error) {
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
177
vendor/github.com/containernetworking/cni/libcni/api_test.go
generated
vendored
Normal file
177
vendor/github.com/containernetworking/cni/libcni/api_test.go
generated
vendored
Normal file
|
@ -0,0 +1,177 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package libcni_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Invoking the plugin", func() {
|
||||
var (
|
||||
debugFilePath string
|
||||
debug *noop_debug.Debug
|
||||
cniBinPath string
|
||||
pluginConfig string
|
||||
cniConfig libcni.CNIConfig
|
||||
netConfig *libcni.NetworkConfig
|
||||
runtimeConfig *libcni.RuntimeConf
|
||||
|
||||
expectedCmdArgs skel.CmdArgs
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
debugFile, err := ioutil.TempFile("", "cni_debug")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(debugFile.Close()).To(Succeed())
|
||||
debugFilePath = debugFile.Name()
|
||||
|
||||
debug = &noop_debug.Debug{
|
||||
ReportResult: `{ "ip4": { "ip": "10.1.2.3/24" }, "dns": {} }`,
|
||||
}
|
||||
Expect(debug.WriteDebug(debugFilePath)).To(Succeed())
|
||||
|
||||
cniBinPath = filepath.Dir(pluginPaths["noop"])
|
||||
pluginConfig = `{ "type": "noop", "some-key": "some-value", "cniVersion": "0.2.0" }`
|
||||
cniConfig = libcni.CNIConfig{Path: []string{cniBinPath}}
|
||||
netConfig = &libcni.NetworkConfig{
|
||||
Network: &types.NetConf{
|
||||
Type: "noop",
|
||||
},
|
||||
Bytes: []byte(pluginConfig),
|
||||
}
|
||||
runtimeConfig = &libcni.RuntimeConf{
|
||||
ContainerID: "some-container-id",
|
||||
NetNS: "/some/netns/path",
|
||||
IfName: "some-eth0",
|
||||
Args: [][2]string{[2]string{"DEBUG", debugFilePath}},
|
||||
}
|
||||
|
||||
expectedCmdArgs = skel.CmdArgs{
|
||||
ContainerID: "some-container-id",
|
||||
Netns: "/some/netns/path",
|
||||
IfName: "some-eth0",
|
||||
Args: "DEBUG=" + debugFilePath,
|
||||
Path: cniBinPath,
|
||||
StdinData: []byte(pluginConfig),
|
||||
}
|
||||
})
|
||||
|
||||
Describe("AddNetwork", func() {
|
||||
It("executes the plugin with command ADD", func() {
|
||||
result, err := cniConfig.AddNetwork(netConfig, runtimeConfig)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(result).To(Equal(&types.Result{
|
||||
IP4: &types.IPConfig{
|
||||
IP: net.IPNet{
|
||||
IP: net.ParseIP("10.1.2.3"),
|
||||
Mask: net.IPv4Mask(255, 255, 255, 0),
|
||||
},
|
||||
},
|
||||
}))
|
||||
|
||||
debug, err := noop_debug.ReadDebug(debugFilePath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(debug.Command).To(Equal("ADD"))
|
||||
Expect(debug.CmdArgs).To(Equal(expectedCmdArgs))
|
||||
})
|
||||
|
||||
Context("when finding the plugin fails", func() {
|
||||
BeforeEach(func() {
|
||||
netConfig.Network.Type = "does-not-exist"
|
||||
})
|
||||
|
||||
It("returns the error", func() {
|
||||
_, err := cniConfig.AddNetwork(netConfig, runtimeConfig)
|
||||
Expect(err).To(MatchError(ContainSubstring(`failed to find plugin "does-not-exist"`)))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the plugin errors", func() {
|
||||
BeforeEach(func() {
|
||||
debug.ReportError = "plugin error: banana"
|
||||
Expect(debug.WriteDebug(debugFilePath)).To(Succeed())
|
||||
})
|
||||
It("unmarshals and returns the error", func() {
|
||||
result, err := cniConfig.AddNetwork(netConfig, runtimeConfig)
|
||||
Expect(result).To(BeNil())
|
||||
Expect(err).To(MatchError("plugin error: banana"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("DelNetwork", func() {
|
||||
It("executes the plugin with command DEL", func() {
|
||||
err := cniConfig.DelNetwork(netConfig, runtimeConfig)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
debug, err := noop_debug.ReadDebug(debugFilePath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(debug.Command).To(Equal("DEL"))
|
||||
Expect(debug.CmdArgs).To(Equal(expectedCmdArgs))
|
||||
})
|
||||
|
||||
Context("when finding the plugin fails", func() {
|
||||
BeforeEach(func() {
|
||||
netConfig.Network.Type = "does-not-exist"
|
||||
})
|
||||
|
||||
It("returns the error", func() {
|
||||
err := cniConfig.DelNetwork(netConfig, runtimeConfig)
|
||||
Expect(err).To(MatchError(ContainSubstring(`failed to find plugin "does-not-exist"`)))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the plugin errors", func() {
|
||||
BeforeEach(func() {
|
||||
debug.ReportError = "plugin error: banana"
|
||||
Expect(debug.WriteDebug(debugFilePath)).To(Succeed())
|
||||
})
|
||||
It("unmarshals and returns the error", func() {
|
||||
err := cniConfig.DelNetwork(netConfig, runtimeConfig)
|
||||
Expect(err).To(MatchError("plugin error: banana"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("GetVersionInfo", func() {
|
||||
It("executes the plugin with the command VERSION", func() {
|
||||
versionInfo, err := cniConfig.GetVersionInfo("noop")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(versionInfo).NotTo(BeNil())
|
||||
Expect(versionInfo.SupportedVersions()).To(Equal([]string{
|
||||
"0.-42.0", "0.1.0", "0.2.0",
|
||||
}))
|
||||
})
|
||||
|
||||
Context("when finding the plugin fails", func() {
|
||||
It("returns the error", func() {
|
||||
_, err := cniConfig.GetVersionInfo("does-not-exist")
|
||||
Expect(err).To(MatchError(ContainSubstring(`failed to find plugin "does-not-exist"`)))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
83
vendor/github.com/containernetworking/cni/libcni/backwards_compatibility_test.go
generated
vendored
Normal file
83
vendor/github.com/containernetworking/cni/libcni/backwards_compatibility_test.go
generated
vendored
Normal file
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package libcni_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
"github.com/containernetworking/cni/pkg/version/legacy_examples"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/gexec"
|
||||
)
|
||||
|
||||
var _ = Describe("Backwards compatibility", func() {
|
||||
It("correctly handles the response from a legacy plugin", func() {
|
||||
example := legacy_examples.V010
|
||||
pluginPath, err := example.Build()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
netConf, err := libcni.ConfFromBytes([]byte(fmt.Sprintf(
|
||||
`{ "name": "old-thing", "type": "%s" }`, example.Name)))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
runtimeConf := &libcni.RuntimeConf{
|
||||
ContainerID: "some-container-id",
|
||||
NetNS: "/some/netns/path",
|
||||
IfName: "eth0",
|
||||
}
|
||||
|
||||
cniConfig := &libcni.CNIConfig{Path: []string{filepath.Dir(pluginPath)}}
|
||||
|
||||
result, err := cniConfig.AddNetwork(netConf, runtimeConf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(result).To(Equal(legacy_examples.ExpectedResult))
|
||||
|
||||
Expect(os.RemoveAll(pluginPath)).To(Succeed())
|
||||
})
|
||||
|
||||
It("correctly handles the request from a runtime with an older libcni", func() {
|
||||
// We need to be root (or have CAP_SYS_ADMIN...)
|
||||
if os.Geteuid() != 0 {
|
||||
Fail("must be run as root")
|
||||
}
|
||||
|
||||
example := legacy_examples.V010_Runtime
|
||||
|
||||
binPath, err := example.Build()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
for _, configName := range example.NetConfs {
|
||||
configStr, ok := legacy_examples.NetConfs[configName]
|
||||
if !ok {
|
||||
Fail("Invalid config name " + configName)
|
||||
}
|
||||
|
||||
cmd := exec.Command(binPath, pluginDirs...)
|
||||
cmd.Stdin = strings.NewReader(configStr)
|
||||
|
||||
session, err := gexec.Start(cmd, GinkgoWriter, GinkgoWriter)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Eventually(session).Should(gexec.Exit(0))
|
||||
}
|
||||
})
|
||||
})
|
215
vendor/github.com/containernetworking/cni/libcni/conf_test.go
generated
vendored
Normal file
215
vendor/github.com/containernetworking/cni/libcni/conf_test.go
generated
vendored
Normal file
|
@ -0,0 +1,215 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package libcni_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Loading configuration from disk", func() {
|
||||
var (
|
||||
configDir string
|
||||
pluginConfig []byte
|
||||
testNetConfig *libcni.NetworkConfig
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
configDir, err = ioutil.TempDir("", "plugin-conf")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
pluginConfig = []byte(`{ "name": "some-plugin", "some-key": "some-value" }`)
|
||||
Expect(ioutil.WriteFile(filepath.Join(configDir, "50-whatever.conf"), pluginConfig, 0600)).To(Succeed())
|
||||
|
||||
testNetConfig = &libcni.NetworkConfig{Network: &types.NetConf{Name: "some-plugin"},
|
||||
Bytes: []byte(`{ "name": "some-plugin" }`)}
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(os.RemoveAll(configDir)).To(Succeed())
|
||||
})
|
||||
|
||||
Describe("LoadConf", func() {
|
||||
It("finds the network config file for the plugin of the given type", func() {
|
||||
netConfig, err := libcni.LoadConf(configDir, "some-plugin")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(netConfig).To(Equal(&libcni.NetworkConfig{
|
||||
Network: &types.NetConf{Name: "some-plugin"},
|
||||
Bytes: pluginConfig,
|
||||
}))
|
||||
})
|
||||
|
||||
Context("when the config directory does not exist", func() {
|
||||
BeforeEach(func() {
|
||||
Expect(os.RemoveAll(configDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("returns a useful error", func() {
|
||||
_, err := libcni.LoadConf(configDir, "some-plugin")
|
||||
Expect(err).To(MatchError("no net configurations found"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the config file is .json extension instead of .conf", func() {
|
||||
BeforeEach(func() {
|
||||
Expect(os.Remove(configDir + "/50-whatever.conf")).To(Succeed())
|
||||
pluginConfig = []byte(`{ "name": "some-plugin", "some-key": "some-value" }`)
|
||||
Expect(ioutil.WriteFile(filepath.Join(configDir, "50-whatever.json"), pluginConfig, 0600)).To(Succeed())
|
||||
})
|
||||
It("finds the network config file for the plugin of the given type", func() {
|
||||
netConfig, err := libcni.LoadConf(configDir, "some-plugin")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(netConfig).To(Equal(&libcni.NetworkConfig{
|
||||
Network: &types.NetConf{Name: "some-plugin"},
|
||||
Bytes: pluginConfig,
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when there is no config for the desired plugin", func() {
|
||||
It("returns a useful error", func() {
|
||||
_, err := libcni.LoadConf(configDir, "some-other-plugin")
|
||||
Expect(err).To(MatchError(ContainSubstring(`no net configuration with name "some-other-plugin" in`)))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when a config file is malformed", func() {
|
||||
BeforeEach(func() {
|
||||
Expect(ioutil.WriteFile(filepath.Join(configDir, "00-bad.conf"), []byte(`{`), 0600)).To(Succeed())
|
||||
})
|
||||
|
||||
It("returns a useful error", func() {
|
||||
_, err := libcni.LoadConf(configDir, "some-plugin")
|
||||
Expect(err).To(MatchError(`error parsing configuration: unexpected end of JSON input`))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the config is in a nested subdir", func() {
|
||||
BeforeEach(func() {
|
||||
subdir := filepath.Join(configDir, "subdir1", "subdir2")
|
||||
Expect(os.MkdirAll(subdir, 0700)).To(Succeed())
|
||||
|
||||
pluginConfig = []byte(`{ "name": "deep", "some-key": "some-value" }`)
|
||||
Expect(ioutil.WriteFile(filepath.Join(subdir, "90-deep.conf"), pluginConfig, 0600)).To(Succeed())
|
||||
})
|
||||
|
||||
It("will not find the config", func() {
|
||||
_, err := libcni.LoadConf(configDir, "deep")
|
||||
Expect(err).To(MatchError(HavePrefix("no net configuration with name")))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("ConfFromFile", func() {
|
||||
Context("when the file cannot be opened", func() {
|
||||
It("returns a useful error", func() {
|
||||
_, err := libcni.ConfFromFile("/tmp/nope/not-here")
|
||||
Expect(err).To(MatchError(HavePrefix(`error reading /tmp/nope/not-here: open /tmp/nope/not-here`)))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("InjectConf", func() {
|
||||
Context("when function parameters are incorrect", func() {
|
||||
It("returns unmarshal error", func() {
|
||||
conf := &libcni.NetworkConfig{Network: &types.NetConf{Name: "some-plugin"},
|
||||
Bytes: []byte(`{ cc cc cc}`)}
|
||||
|
||||
_, err := libcni.InjectConf(conf, "", nil)
|
||||
Expect(err).To(MatchError(HavePrefix(`unmarshal existing network bytes`)))
|
||||
})
|
||||
|
||||
It("returns key error", func() {
|
||||
_, err := libcni.InjectConf(testNetConfig, "", nil)
|
||||
Expect(err).To(MatchError(HavePrefix(`key value can not be empty`)))
|
||||
})
|
||||
|
||||
It("returns newValue error", func() {
|
||||
_, err := libcni.InjectConf(testNetConfig, "test", nil)
|
||||
Expect(err).To(MatchError(HavePrefix(`newValue must be specified`)))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when new string value added", func() {
|
||||
It("adds the new key & value to the config", func() {
|
||||
newPluginConfig := []byte(`{"name":"some-plugin","test":"test"}`)
|
||||
|
||||
resultConfig, err := libcni.InjectConf(testNetConfig, "test", "test")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(resultConfig).To(Equal(&libcni.NetworkConfig{
|
||||
Network: &types.NetConf{Name: "some-plugin"},
|
||||
Bytes: newPluginConfig,
|
||||
}))
|
||||
})
|
||||
|
||||
It("adds the new value for exiting key", func() {
|
||||
newPluginConfig := []byte(`{"name":"some-plugin","test":"changedValue"}`)
|
||||
|
||||
resultConfig, err := libcni.InjectConf(testNetConfig, "test", "test")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
resultConfig, err = libcni.InjectConf(resultConfig, "test", "changedValue")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(resultConfig).To(Equal(&libcni.NetworkConfig{
|
||||
Network: &types.NetConf{Name: "some-plugin"},
|
||||
Bytes: newPluginConfig,
|
||||
}))
|
||||
})
|
||||
|
||||
It("adds existing key & value", func() {
|
||||
newPluginConfig := []byte(`{"name":"some-plugin","test":"test"}`)
|
||||
|
||||
resultConfig, err := libcni.InjectConf(testNetConfig, "test", "test")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
resultConfig, err = libcni.InjectConf(resultConfig, "test", "test")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(resultConfig).To(Equal(&libcni.NetworkConfig{
|
||||
Network: &types.NetConf{Name: "some-plugin"},
|
||||
Bytes: newPluginConfig,
|
||||
}))
|
||||
})
|
||||
|
||||
It("adds sub-fields of NetworkConfig.Network to the config", func() {
|
||||
|
||||
expectedPluginConfig := []byte(`{"dns":{"domain":"local","nameservers":["server1","server2"]},"name":"some-plugin","type":"bridge"}`)
|
||||
servers := []string{"server1", "server2"}
|
||||
newDNS := &types.DNS{Nameservers: servers, Domain: "local"}
|
||||
|
||||
// inject DNS
|
||||
resultConfig, err := libcni.InjectConf(testNetConfig, "dns", newDNS)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// inject type
|
||||
resultConfig, err = libcni.InjectConf(resultConfig, "type", "bridge")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(resultConfig).To(Equal(&libcni.NetworkConfig{
|
||||
Network: &types.NetConf{Name: "some-plugin", Type: "bridge", DNS: types.DNS{Nameservers: servers, Domain: "local"}},
|
||||
Bytes: expectedPluginConfig,
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
67
vendor/github.com/containernetworking/cni/libcni/libcni_suite_test.go
generated
vendored
Normal file
67
vendor/github.com/containernetworking/cni/libcni/libcni_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package libcni_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/gexec"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLibcni(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Libcni Suite")
|
||||
}
|
||||
|
||||
var plugins = map[string]string{
|
||||
"noop": "github.com/containernetworking/cni/plugins/test/noop",
|
||||
"ptp": "github.com/containernetworking/cni/plugins/main/ptp",
|
||||
"host-local": "github.com/containernetworking/cni/plugins/ipam/host-local",
|
||||
}
|
||||
|
||||
var pluginPaths map[string]string
|
||||
var pluginDirs []string // array of plugin dirs
|
||||
|
||||
var _ = SynchronizedBeforeSuite(func() []byte {
|
||||
dirs := make([]string, 0, len(plugins))
|
||||
|
||||
for name, packagePath := range plugins {
|
||||
execPath, err := gexec.Build(packagePath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
dirs = append(dirs, fmt.Sprintf("%s=%s", name, execPath))
|
||||
}
|
||||
|
||||
return []byte(strings.Join(dirs, ":"))
|
||||
}, func(crossNodeData []byte) {
|
||||
pluginPaths = make(map[string]string)
|
||||
for _, str := range strings.Split(string(crossNodeData), ":") {
|
||||
kvs := strings.SplitN(str, "=", 2)
|
||||
if len(kvs) != 2 {
|
||||
Fail("Invalid inter-node data...")
|
||||
}
|
||||
pluginPaths[kvs[0]] = kvs[1]
|
||||
pluginDirs = append(pluginDirs, filepath.Dir(kvs[1]))
|
||||
}
|
||||
})
|
||||
|
||||
var _ = SynchronizedAfterSuite(func() {}, func() {
|
||||
gexec.CleanupBuildArtifacts()
|
||||
})
|
152
vendor/github.com/containernetworking/cni/pkg/invoke/exec_test.go
generated
vendored
Normal file
152
vendor/github.com/containernetworking/cni/pkg/invoke/exec_test.go
generated
vendored
Normal file
|
@ -0,0 +1,152 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package invoke_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/invoke"
|
||||
"github.com/containernetworking/cni/pkg/invoke/fakes"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Executing a plugin, unit tests", func() {
|
||||
var (
|
||||
pluginExec *invoke.PluginExec
|
||||
rawExec *fakes.RawExec
|
||||
versionDecoder *fakes.VersionDecoder
|
||||
|
||||
pluginPath string
|
||||
netconf []byte
|
||||
cniargs *fakes.CNIArgs
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
rawExec = &fakes.RawExec{}
|
||||
rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "ip4": { "ip": "1.2.3.4/24" } }`)
|
||||
|
||||
versionDecoder = &fakes.VersionDecoder{}
|
||||
versionDecoder.DecodeCall.Returns.PluginInfo = version.PluginSupports("0.42.0")
|
||||
|
||||
pluginExec = &invoke.PluginExec{
|
||||
RawExec: rawExec,
|
||||
VersionDecoder: versionDecoder,
|
||||
}
|
||||
pluginPath = "/some/plugin/path"
|
||||
netconf = []byte(`{ "some": "stdin", "cniVersion": "0.2.0" }`)
|
||||
cniargs = &fakes.CNIArgs{}
|
||||
cniargs.AsEnvCall.Returns.Env = []string{"SOME=ENV"}
|
||||
})
|
||||
|
||||
Describe("returning a result", func() {
|
||||
It("unmarshals the result bytes into the Result type", func() {
|
||||
result, err := pluginExec.WithResult(pluginPath, netconf, cniargs)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(result.IP4.IP.IP.String()).To(Equal("1.2.3.4"))
|
||||
})
|
||||
|
||||
It("passes its arguments through to the rawExec", func() {
|
||||
pluginExec.WithResult(pluginPath, netconf, cniargs)
|
||||
Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
|
||||
Expect(rawExec.ExecPluginCall.Received.StdinData).To(Equal(netconf))
|
||||
Expect(rawExec.ExecPluginCall.Received.Environ).To(Equal([]string{"SOME=ENV"}))
|
||||
})
|
||||
|
||||
Context("when the rawExec fails", func() {
|
||||
BeforeEach(func() {
|
||||
rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
|
||||
})
|
||||
It("returns the error", func() {
|
||||
_, err := pluginExec.WithResult(pluginPath, netconf, cniargs)
|
||||
Expect(err).To(MatchError("banana"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("without returning a result", func() {
|
||||
It("passes its arguments through to the rawExec", func() {
|
||||
pluginExec.WithoutResult(pluginPath, netconf, cniargs)
|
||||
Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
|
||||
Expect(rawExec.ExecPluginCall.Received.StdinData).To(Equal(netconf))
|
||||
Expect(rawExec.ExecPluginCall.Received.Environ).To(Equal([]string{"SOME=ENV"}))
|
||||
})
|
||||
|
||||
Context("when the rawExec fails", func() {
|
||||
BeforeEach(func() {
|
||||
rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
|
||||
})
|
||||
It("returns the error", func() {
|
||||
err := pluginExec.WithoutResult(pluginPath, netconf, cniargs)
|
||||
Expect(err).To(MatchError("banana"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("discovering the plugin version", func() {
|
||||
BeforeEach(func() {
|
||||
rawExec.ExecPluginCall.Returns.ResultBytes = []byte(`{ "some": "version-info" }`)
|
||||
})
|
||||
|
||||
It("execs the plugin with the command VERSION", func() {
|
||||
pluginExec.GetVersionInfo(pluginPath)
|
||||
Expect(rawExec.ExecPluginCall.Received.PluginPath).To(Equal(pluginPath))
|
||||
Expect(rawExec.ExecPluginCall.Received.Environ).To(ContainElement("CNI_COMMAND=VERSION"))
|
||||
expectedStdin, _ := json.Marshal(map[string]string{"cniVersion": version.Current()})
|
||||
Expect(rawExec.ExecPluginCall.Received.StdinData).To(MatchJSON(expectedStdin))
|
||||
})
|
||||
|
||||
It("decodes and returns the version info", func() {
|
||||
versionInfo, err := pluginExec.GetVersionInfo(pluginPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(versionInfo.SupportedVersions()).To(Equal([]string{"0.42.0"}))
|
||||
Expect(versionDecoder.DecodeCall.Received.JSONBytes).To(MatchJSON(`{ "some": "version-info" }`))
|
||||
})
|
||||
|
||||
Context("when the rawExec fails", func() {
|
||||
BeforeEach(func() {
|
||||
rawExec.ExecPluginCall.Returns.Error = errors.New("banana")
|
||||
})
|
||||
It("returns the error", func() {
|
||||
_, err := pluginExec.GetVersionInfo(pluginPath)
|
||||
Expect(err).To(MatchError("banana"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the plugin is too old to recognize the VERSION command", func() {
|
||||
BeforeEach(func() {
|
||||
rawExec.ExecPluginCall.Returns.Error = errors.New("unknown CNI_COMMAND: VERSION")
|
||||
})
|
||||
|
||||
It("interprets the error as a 0.1.0 version", func() {
|
||||
versionInfo, err := pluginExec.GetVersionInfo(pluginPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(versionInfo.SupportedVersions()).To(ConsistOf("0.1.0"))
|
||||
})
|
||||
|
||||
It("sets dummy values for env vars required by very old plugins", func() {
|
||||
pluginExec.GetVersionInfo(pluginPath)
|
||||
|
||||
env := rawExec.ExecPluginCall.Received.Environ
|
||||
Expect(env).To(ContainElement("CNI_NETNS=dummy"))
|
||||
Expect(env).To(ContainElement("CNI_IFNAME=dummy"))
|
||||
Expect(env).To(ContainElement("CNI_PATH=dummy"))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
27
vendor/github.com/containernetworking/cni/pkg/invoke/fakes/cni_args.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/pkg/invoke/fakes/cni_args.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package fakes
|
||||
|
||||
type CNIArgs struct {
|
||||
AsEnvCall struct {
|
||||
Returns struct {
|
||||
Env []string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (a *CNIArgs) AsEnv() []string {
|
||||
return a.AsEnvCall.Returns.Env
|
||||
}
|
36
vendor/github.com/containernetworking/cni/pkg/invoke/fakes/raw_exec.go
generated
vendored
Normal file
36
vendor/github.com/containernetworking/cni/pkg/invoke/fakes/raw_exec.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package fakes
|
||||
|
||||
type RawExec struct {
|
||||
ExecPluginCall struct {
|
||||
Received struct {
|
||||
PluginPath string
|
||||
StdinData []byte
|
||||
Environ []string
|
||||
}
|
||||
Returns struct {
|
||||
ResultBytes []byte
|
||||
Error error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *RawExec) ExecPlugin(pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
|
||||
e.ExecPluginCall.Received.PluginPath = pluginPath
|
||||
e.ExecPluginCall.Received.StdinData = stdinData
|
||||
e.ExecPluginCall.Received.Environ = environ
|
||||
return e.ExecPluginCall.Returns.ResultBytes, e.ExecPluginCall.Returns.Error
|
||||
}
|
34
vendor/github.com/containernetworking/cni/pkg/invoke/fakes/version_decoder.go
generated
vendored
Normal file
34
vendor/github.com/containernetworking/cni/pkg/invoke/fakes/version_decoder.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package fakes
|
||||
|
||||
import "github.com/containernetworking/cni/pkg/version"
|
||||
|
||||
type VersionDecoder struct {
|
||||
DecodeCall struct {
|
||||
Received struct {
|
||||
JSONBytes []byte
|
||||
}
|
||||
Returns struct {
|
||||
PluginInfo version.PluginInfo
|
||||
Error error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *VersionDecoder) Decode(jsonData []byte) (version.PluginInfo, error) {
|
||||
e.DecodeCall.Received.JSONBytes = jsonData
|
||||
return e.DecodeCall.Returns.PluginInfo, e.DecodeCall.Returns.Error
|
||||
}
|
78
vendor/github.com/containernetworking/cni/pkg/invoke/find_test.go
generated
vendored
Normal file
78
vendor/github.com/containernetworking/cni/pkg/invoke/find_test.go
generated
vendored
Normal file
|
@ -0,0 +1,78 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package invoke_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/invoke"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("FindInPath", func() {
|
||||
var (
|
||||
multiplePaths []string
|
||||
pluginName string
|
||||
pluginDir string
|
||||
anotherTempDir string
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
tempDir, err := ioutil.TempDir("", "cni-find")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
plugin, err := ioutil.TempFile(tempDir, "a-cni-plugin")
|
||||
|
||||
anotherTempDir, err = ioutil.TempDir("", "nothing-here")
|
||||
|
||||
multiplePaths = []string{anotherTempDir, tempDir}
|
||||
pluginDir, pluginName = filepath.Split(plugin.Name())
|
||||
})
|
||||
|
||||
Context("when multiple paths are provided", func() {
|
||||
It("returns only the path to the plugin", func() {
|
||||
pluginPath, err := invoke.FindInPath(pluginName, multiplePaths)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(pluginPath).To(Equal(filepath.Join(pluginDir, pluginName)))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when an error occurs", func() {
|
||||
Context("when no paths are provided", func() {
|
||||
It("returns an error noting no paths were provided", func() {
|
||||
_, err := invoke.FindInPath(pluginName, []string{})
|
||||
Expect(err).To(MatchError("no paths provided"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when no plugin is provided", func() {
|
||||
It("returns an error noting the plugin name wasn't found", func() {
|
||||
_, err := invoke.FindInPath("", multiplePaths)
|
||||
Expect(err).To(MatchError("no plugin name provided"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the plugin cannot be found", func() {
|
||||
It("returns an error noting the path", func() {
|
||||
pathsWithNothing := []string{anotherTempDir}
|
||||
_, err := invoke.FindInPath(pluginName, pathsWithNothing)
|
||||
Expect(err).To(MatchError(fmt.Sprintf("failed to find plugin %q in path %s", pluginName, pathsWithNothing)))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
107
vendor/github.com/containernetworking/cni/pkg/invoke/get_version_integration_test.go
generated
vendored
Normal file
107
vendor/github.com/containernetworking/cni/pkg/invoke/get_version_integration_test.go
generated
vendored
Normal file
|
@ -0,0 +1,107 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package invoke_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/invoke"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
"github.com/containernetworking/cni/pkg/version/testhelpers"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/extensions/table"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("GetVersion, integration tests", func() {
|
||||
var (
|
||||
pluginDir string
|
||||
pluginPath string
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
pluginDir, err := ioutil.TempDir("", "plugins")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
pluginPath = filepath.Join(pluginDir, "test-plugin")
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(os.RemoveAll(pluginDir)).To(Succeed())
|
||||
})
|
||||
|
||||
DescribeTable("correctly reporting plugin versions",
|
||||
func(gitRef string, pluginSource string, expectedVersions version.PluginInfo) {
|
||||
Expect(testhelpers.BuildAt([]byte(pluginSource), gitRef, pluginPath)).To(Succeed())
|
||||
versionInfo, err := invoke.GetVersionInfo(pluginPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(versionInfo.SupportedVersions()).To(ConsistOf(expectedVersions.SupportedVersions()))
|
||||
},
|
||||
|
||||
Entry("historical: before VERSION was introduced",
|
||||
git_ref_v010, plugin_source_no_custom_versions,
|
||||
version.PluginSupports("0.1.0"),
|
||||
),
|
||||
|
||||
Entry("historical: when VERSION was introduced but plugins couldn't customize it",
|
||||
git_ref_v020_no_custom_versions, plugin_source_no_custom_versions,
|
||||
version.PluginSupports("0.1.0", "0.2.0"),
|
||||
),
|
||||
|
||||
Entry("historical: when plugins started reporting their own version list",
|
||||
git_ref_v020_custom_versions, plugin_source_v020_custom_versions,
|
||||
version.PluginSupports("0.2.0", "0.999.0"),
|
||||
),
|
||||
|
||||
// this entry tracks the current behavior. Before you change it, ensure
|
||||
// that its previous behavior is captured in the most recent "historical" entry
|
||||
Entry("current",
|
||||
"HEAD", plugin_source_v020_custom_versions,
|
||||
version.PluginSupports("0.2.0", "0.999.0"),
|
||||
),
|
||||
)
|
||||
})
|
||||
|
||||
// a 0.2.0 plugin that can report its own versions
|
||||
const plugin_source_v020_custom_versions = `package main
|
||||
|
||||
import (
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func c(_ *skel.CmdArgs) error { fmt.Println("{}"); return nil }
|
||||
|
||||
func main() { skel.PluginMain(c, c, version.PluginSupports("0.2.0", "0.999.0")) }
|
||||
`
|
||||
const git_ref_v020_custom_versions = "bf31ed15"
|
||||
|
||||
// a minimal 0.1.0 / 0.2.0 plugin that cannot report it's own version support
|
||||
const plugin_source_no_custom_versions = `package main
|
||||
|
||||
import "github.com/containernetworking/cni/pkg/skel"
|
||||
import "fmt"
|
||||
|
||||
func c(_ *skel.CmdArgs) error { fmt.Println("{}"); return nil }
|
||||
|
||||
func main() { skel.PluginMain(c, c) }
|
||||
`
|
||||
|
||||
const git_ref_v010 = "2c482f4"
|
||||
const git_ref_v020_no_custom_versions = "349d66d"
|
45
vendor/github.com/containernetworking/cni/pkg/invoke/invoke_suite_test.go
generated
vendored
Normal file
45
vendor/github.com/containernetworking/cni/pkg/invoke/invoke_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package invoke_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/gexec"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestInvoke(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Invoke Suite")
|
||||
}
|
||||
|
||||
const packagePath = "github.com/containernetworking/cni/plugins/test/noop"
|
||||
|
||||
var pathToPlugin string
|
||||
|
||||
var _ = SynchronizedBeforeSuite(func() []byte {
|
||||
var err error
|
||||
pathToPlugin, err = gexec.Build(packagePath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return []byte(pathToPlugin)
|
||||
}, func(crossNodeData []byte) {
|
||||
pathToPlugin = string(crossNodeData)
|
||||
})
|
||||
|
||||
var _ = SynchronizedAfterSuite(func() {}, func() {
|
||||
gexec.CleanupBuildArtifacts()
|
||||
})
|
123
vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec_test.go
generated
vendored
Normal file
123
vendor/github.com/containernetworking/cni/pkg/invoke/raw_exec_test.go
generated
vendored
Normal file
|
@ -0,0 +1,123 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package invoke_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/invoke"
|
||||
|
||||
noop_debug "github.com/containernetworking/cni/plugins/test/noop/debug"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("RawExec", func() {
|
||||
var (
|
||||
debugFileName string
|
||||
debug *noop_debug.Debug
|
||||
environ []string
|
||||
stdin []byte
|
||||
execer *invoke.RawExec
|
||||
)
|
||||
|
||||
const reportResult = `{ "some": "result" }`
|
||||
|
||||
BeforeEach(func() {
|
||||
debugFile, err := ioutil.TempFile("", "cni_debug")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(debugFile.Close()).To(Succeed())
|
||||
debugFileName = debugFile.Name()
|
||||
|
||||
debug = &noop_debug.Debug{
|
||||
ReportResult: reportResult,
|
||||
ReportStderr: "some stderr message",
|
||||
}
|
||||
Expect(debug.WriteDebug(debugFileName)).To(Succeed())
|
||||
|
||||
environ = []string{
|
||||
"CNI_COMMAND=ADD",
|
||||
"CNI_CONTAINERID=some-container-id",
|
||||
"CNI_ARGS=DEBUG=" + debugFileName,
|
||||
"CNI_NETNS=/some/netns/path",
|
||||
"CNI_PATH=/some/bin/path",
|
||||
"CNI_IFNAME=some-eth0",
|
||||
}
|
||||
stdin = []byte(`{"some":"stdin-json", "cniVersion": "0.2.0"}`)
|
||||
execer = &invoke.RawExec{}
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(os.Remove(debugFileName)).To(Succeed())
|
||||
})
|
||||
|
||||
It("runs the plugin with the given stdin and environment", func() {
|
||||
_, err := execer.ExecPlugin(pathToPlugin, stdin, environ)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
debug, err := noop_debug.ReadDebug(debugFileName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(debug.Command).To(Equal("ADD"))
|
||||
Expect(debug.CmdArgs.StdinData).To(Equal(stdin))
|
||||
Expect(debug.CmdArgs.Netns).To(Equal("/some/netns/path"))
|
||||
})
|
||||
|
||||
It("returns the resulting stdout as bytes", func() {
|
||||
resultBytes, err := execer.ExecPlugin(pathToPlugin, stdin, environ)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(resultBytes).To(BeEquivalentTo(reportResult))
|
||||
})
|
||||
|
||||
Context("when the Stderr writer is set", func() {
|
||||
var stderrBuffer *bytes.Buffer
|
||||
|
||||
BeforeEach(func() {
|
||||
stderrBuffer = &bytes.Buffer{}
|
||||
execer.Stderr = stderrBuffer
|
||||
})
|
||||
|
||||
It("forwards any stderr bytes to the Stderr writer", func() {
|
||||
_, err := execer.ExecPlugin(pathToPlugin, stdin, environ)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(stderrBuffer.String()).To(Equal("some stderr message"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the plugin errors", func() {
|
||||
BeforeEach(func() {
|
||||
debug.ReportError = "banana"
|
||||
Expect(debug.WriteDebug(debugFileName)).To(Succeed())
|
||||
})
|
||||
|
||||
It("wraps and returns the error", func() {
|
||||
_, err := execer.ExecPlugin(pathToPlugin, stdin, environ)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(MatchError("banana"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the system is unable to execute the plugin", func() {
|
||||
It("returns the error", func() {
|
||||
_, err := execer.ExecPlugin("/tmp/some/invalid/plugin/path", stdin, environ)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(MatchError(ContainSubstring("/tmp/some/invalid/plugin/path")))
|
||||
})
|
||||
})
|
||||
})
|
51
vendor/github.com/containernetworking/cni/pkg/ip/cidr.go
generated
vendored
Normal file
51
vendor/github.com/containernetworking/cni/pkg/ip/cidr.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ip
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"net"
|
||||
)
|
||||
|
||||
// NextIP returns IP incremented by 1
|
||||
func NextIP(ip net.IP) net.IP {
|
||||
i := ipToInt(ip)
|
||||
return intToIP(i.Add(i, big.NewInt(1)))
|
||||
}
|
||||
|
||||
// PrevIP returns IP decremented by 1
|
||||
func PrevIP(ip net.IP) net.IP {
|
||||
i := ipToInt(ip)
|
||||
return intToIP(i.Sub(i, big.NewInt(1)))
|
||||
}
|
||||
|
||||
func ipToInt(ip net.IP) *big.Int {
|
||||
if v := ip.To4(); v != nil {
|
||||
return big.NewInt(0).SetBytes(v)
|
||||
}
|
||||
return big.NewInt(0).SetBytes(ip.To16())
|
||||
}
|
||||
|
||||
func intToIP(i *big.Int) net.IP {
|
||||
return net.IP(i.Bytes())
|
||||
}
|
||||
|
||||
// Network masks off the host portion of the IP
|
||||
func Network(ipn *net.IPNet) *net.IPNet {
|
||||
return &net.IPNet{
|
||||
IP: ipn.IP.Mask(ipn.Mask),
|
||||
Mask: ipn.Mask,
|
||||
}
|
||||
}
|
27
vendor/github.com/containernetworking/cni/pkg/ip/ip_suite_test.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/pkg/ip/ip_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ip_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIp(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Ip Suite")
|
||||
}
|
31
vendor/github.com/containernetworking/cni/pkg/ip/ipforward.go
generated
vendored
Normal file
31
vendor/github.com/containernetworking/cni/pkg/ip/ipforward.go
generated
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ip
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
func EnableIP4Forward() error {
|
||||
return echo1("/proc/sys/net/ipv4/ip_forward")
|
||||
}
|
||||
|
||||
func EnableIP6Forward() error {
|
||||
return echo1("/proc/sys/net/ipv6/conf/all/forwarding")
|
||||
}
|
||||
|
||||
func echo1(f string) error {
|
||||
return ioutil.WriteFile(f, []byte("1"), 0644)
|
||||
}
|
66
vendor/github.com/containernetworking/cni/pkg/ip/ipmasq.go
generated
vendored
Normal file
66
vendor/github.com/containernetworking/cni/pkg/ip/ipmasq.go
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ip
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/coreos/go-iptables/iptables"
|
||||
)
|
||||
|
||||
// SetupIPMasq installs iptables rules to masquerade traffic
|
||||
// coming from ipn and going outside of it
|
||||
func SetupIPMasq(ipn *net.IPNet, chain string, comment string) error {
|
||||
ipt, err := iptables.New()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to locate iptables: %v", err)
|
||||
}
|
||||
|
||||
if err = ipt.NewChain("nat", chain); err != nil {
|
||||
if err.(*iptables.Error).ExitStatus() != 1 {
|
||||
// TODO(eyakubovich): assumes exit status 1 implies chain exists
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err = ipt.AppendUnique("nat", chain, "-d", ipn.String(), "-j", "ACCEPT", "-m", "comment", "--comment", comment); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = ipt.AppendUnique("nat", chain, "!", "-d", "224.0.0.0/4", "-j", "MASQUERADE", "-m", "comment", "--comment", comment); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ipt.AppendUnique("nat", "POSTROUTING", "-s", ipn.String(), "-j", chain, "-m", "comment", "--comment", comment)
|
||||
}
|
||||
|
||||
// TeardownIPMasq undoes the effects of SetupIPMasq
|
||||
func TeardownIPMasq(ipn *net.IPNet, chain string, comment string) error {
|
||||
ipt, err := iptables.New()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to locate iptables: %v", err)
|
||||
}
|
||||
|
||||
if err = ipt.Delete("nat", "POSTROUTING", "-s", ipn.String(), "-j", chain, "-m", "comment", "--comment", comment); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = ipt.ClearChain("nat", chain); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ipt.DeleteChain("nat", chain)
|
||||
}
|
192
vendor/github.com/containernetworking/cni/pkg/ip/link.go
generated
vendored
Normal file
192
vendor/github.com/containernetworking/cni/pkg/ip/link.go
generated
vendored
Normal file
|
@ -0,0 +1,192 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ip
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/ns"
|
||||
"github.com/containernetworking/cni/pkg/utils/hwaddr"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func makeVethPair(name, peer string, mtu int) (netlink.Link, error) {
|
||||
veth := &netlink.Veth{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: name,
|
||||
Flags: net.FlagUp,
|
||||
MTU: mtu,
|
||||
},
|
||||
PeerName: peer,
|
||||
}
|
||||
if err := netlink.LinkAdd(veth); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return veth, nil
|
||||
}
|
||||
|
||||
func peerExists(name string) bool {
|
||||
if _, err := netlink.LinkByName(name); err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func makeVeth(name string, mtu int) (peerName string, veth netlink.Link, err error) {
|
||||
for i := 0; i < 10; i++ {
|
||||
peerName, err = RandomVethName()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
veth, err = makeVethPair(name, peerName, mtu)
|
||||
switch {
|
||||
case err == nil:
|
||||
return
|
||||
|
||||
case os.IsExist(err):
|
||||
if peerExists(peerName) {
|
||||
continue
|
||||
}
|
||||
err = fmt.Errorf("container veth name provided (%v) already exists", name)
|
||||
return
|
||||
|
||||
default:
|
||||
err = fmt.Errorf("failed to make veth pair: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// should really never be hit
|
||||
err = fmt.Errorf("failed to find a unique veth name")
|
||||
return
|
||||
}
|
||||
|
||||
// RandomVethName returns string "veth" with random prefix (hashed from entropy)
|
||||
func RandomVethName() (string, error) {
|
||||
entropy := make([]byte, 4)
|
||||
_, err := rand.Reader.Read(entropy)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to generate random veth name: %v", err)
|
||||
}
|
||||
|
||||
// NetworkManager (recent versions) will ignore veth devices that start with "veth"
|
||||
return fmt.Sprintf("veth%x", entropy), nil
|
||||
}
|
||||
|
||||
// SetupVeth sets up a virtual ethernet link.
|
||||
// Should be in container netns, and will switch back to hostNS to set the host
|
||||
// veth end up.
|
||||
func SetupVeth(contVethName string, mtu int, hostNS ns.NetNS) (hostVeth, contVeth netlink.Link, err error) {
|
||||
var hostVethName string
|
||||
hostVethName, contVeth, err = makeVeth(contVethName, mtu)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if err = netlink.LinkSetUp(contVeth); err != nil {
|
||||
err = fmt.Errorf("failed to set %q up: %v", contVethName, err)
|
||||
return
|
||||
}
|
||||
|
||||
hostVeth, err = netlink.LinkByName(hostVethName)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to lookup %q: %v", hostVethName, err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = netlink.LinkSetNsFd(hostVeth, int(hostNS.Fd())); err != nil {
|
||||
err = fmt.Errorf("failed to move veth to host netns: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = hostNS.Do(func(_ ns.NetNS) error {
|
||||
hostVeth, err = netlink.LinkByName(hostVethName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Path(), err)
|
||||
}
|
||||
|
||||
if err = netlink.LinkSetUp(hostVeth); err != nil {
|
||||
return fmt.Errorf("failed to set %q up: %v", hostVethName, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// DelLinkByName removes an interface link.
|
||||
func DelLinkByName(ifName string) error {
|
||||
iface, err := netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup %q: %v", ifName, err)
|
||||
}
|
||||
|
||||
if err = netlink.LinkDel(iface); err != nil {
|
||||
return fmt.Errorf("failed to delete %q: %v", ifName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DelLinkByNameAddr remove an interface returns its IP address
|
||||
// of the specified family
|
||||
func DelLinkByNameAddr(ifName string, family int) (*net.IPNet, error) {
|
||||
iface, err := netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to lookup %q: %v", ifName, err)
|
||||
}
|
||||
|
||||
addrs, err := netlink.AddrList(iface, family)
|
||||
if err != nil || len(addrs) == 0 {
|
||||
return nil, fmt.Errorf("failed to get IP addresses for %q: %v", ifName, err)
|
||||
}
|
||||
|
||||
if err = netlink.LinkDel(iface); err != nil {
|
||||
return nil, fmt.Errorf("failed to delete %q: %v", ifName, err)
|
||||
}
|
||||
|
||||
return addrs[0].IPNet, nil
|
||||
}
|
||||
|
||||
func SetHWAddrByIP(ifName string, ip4 net.IP, ip6 net.IP) error {
|
||||
iface, err := netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup %q: %v", ifName, err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case ip4 == nil && ip6 == nil:
|
||||
return fmt.Errorf("neither ip4 or ip6 specified")
|
||||
|
||||
case ip4 != nil:
|
||||
{
|
||||
hwAddr, err := hwaddr.GenerateHardwareAddr4(ip4, hwaddr.PrivateMACPrefix)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to generate hardware addr: %v", err)
|
||||
}
|
||||
if err = netlink.LinkSetHardwareAddr(iface, hwAddr); err != nil {
|
||||
return fmt.Errorf("failed to add hardware addr to %q: %v", ifName, err)
|
||||
}
|
||||
}
|
||||
case ip6 != nil:
|
||||
// TODO: IPv6
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
259
vendor/github.com/containernetworking/cni/pkg/ip/link_test.go
generated
vendored
Normal file
259
vendor/github.com/containernetworking/cni/pkg/ip/link_test.go
generated
vendored
Normal file
|
@ -0,0 +1,259 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ip_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/ip"
|
||||
"github.com/containernetworking/cni/pkg/ns"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
"github.com/vishvananda/netlink/nl"
|
||||
)
|
||||
|
||||
func getHwAddr(linkname string) string {
|
||||
veth, err := netlink.LinkByName(linkname)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return fmt.Sprintf("%s", veth.Attrs().HardwareAddr)
|
||||
}
|
||||
|
||||
var _ = Describe("Link", func() {
|
||||
const (
|
||||
ifaceFormatString string = "i%d"
|
||||
mtu int = 1400
|
||||
ip4onehwaddr = "0a:58:01:01:01:01"
|
||||
)
|
||||
var (
|
||||
hostNetNS ns.NetNS
|
||||
containerNetNS ns.NetNS
|
||||
ifaceCounter int = 0
|
||||
hostVeth netlink.Link
|
||||
containerVeth netlink.Link
|
||||
hostVethName string
|
||||
containerVethName string
|
||||
|
||||
ip4one = net.ParseIP("1.1.1.1")
|
||||
ip4two = net.ParseIP("1.1.1.2")
|
||||
originalRandReader = rand.Reader
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
|
||||
hostNetNS, err = ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
containerNetNS, err = ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
fakeBytes := make([]byte, 20)
|
||||
//to be reset in AfterEach block
|
||||
rand.Reader = bytes.NewReader(fakeBytes)
|
||||
|
||||
_ = containerNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
hostVeth, containerVeth, err = ip.SetupVeth(fmt.Sprintf(ifaceFormatString, ifaceCounter), mtu, hostNetNS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
hostVethName = hostVeth.Attrs().Name
|
||||
containerVethName = containerVeth.Attrs().Name
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(containerNetNS.Close()).To(Succeed())
|
||||
Expect(hostNetNS.Close()).To(Succeed())
|
||||
ifaceCounter++
|
||||
rand.Reader = originalRandReader
|
||||
})
|
||||
|
||||
It("SetupVeth must put the veth endpoints into the separate namespaces", func() {
|
||||
_ = containerNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
containerVethFromName, err := netlink.LinkByName(containerVethName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(containerVethFromName.Attrs().Index).To(Equal(containerVeth.Attrs().Index))
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
_ = hostNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
hostVethFromName, err := netlink.LinkByName(hostVethName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(hostVethFromName.Attrs().Index).To(Equal(hostVeth.Attrs().Index))
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
Context("when container already has an interface with the same name", func() {
|
||||
It("returns useful error", func() {
|
||||
_ = containerNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, _, err := ip.SetupVeth(containerVethName, mtu, hostNetNS)
|
||||
Expect(err.Error()).To(Equal(fmt.Sprintf("container veth name provided (%s) already exists", containerVethName)))
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("when there is no name available for the host-side", func() {
|
||||
BeforeEach(func() {
|
||||
//adding different interface to container ns
|
||||
containerVethName += "0"
|
||||
})
|
||||
It("returns useful error", func() {
|
||||
_ = containerNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, _, err := ip.SetupVeth(containerVethName, mtu, hostNetNS)
|
||||
Expect(err.Error()).To(Equal("failed to move veth to host netns: file exists"))
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("when there is no name conflict for the host or container interfaces", func() {
|
||||
BeforeEach(func() {
|
||||
//adding different interface to container and host ns
|
||||
containerVethName += "0"
|
||||
rand.Reader = originalRandReader
|
||||
})
|
||||
It("successfully creates the second veth pair", func() {
|
||||
_ = containerNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
hostVeth, _, err := ip.SetupVeth(containerVethName, mtu, hostNetNS)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
hostVethName = hostVeth.Attrs().Name
|
||||
return nil
|
||||
})
|
||||
|
||||
//verify veths are in different namespaces
|
||||
_ = containerNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, err := netlink.LinkByName(containerVethName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
_ = hostNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, err := netlink.LinkByName(hostVethName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
It("DelLinkByName must delete the veth endpoints", func() {
|
||||
_ = containerNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
// this will delete the host endpoint too
|
||||
err := ip.DelLinkByName(containerVethName)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, err = netlink.LinkByName(containerVethName)
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
_ = hostNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, err := netlink.LinkByName(hostVethName)
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
It("DelLinkByNameAddr must throw an error for configured interfaces", func() {
|
||||
_ = containerNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
// this will delete the host endpoint too
|
||||
addr, err := ip.DelLinkByNameAddr(containerVethName, nl.FAMILY_V4)
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
var ipNetNil *net.IPNet
|
||||
Expect(addr).To(Equal(ipNetNil))
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
It("SetHWAddrByIP must change the interface hwaddr and be predictable", func() {
|
||||
|
||||
_ = containerNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
var err error
|
||||
hwaddrBefore := getHwAddr(containerVethName)
|
||||
|
||||
err = ip.SetHWAddrByIP(containerVethName, ip4one, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
hwaddrAfter1 := getHwAddr(containerVethName)
|
||||
|
||||
Expect(hwaddrBefore).NotTo(Equal(hwaddrAfter1))
|
||||
Expect(hwaddrAfter1).To(Equal(ip4onehwaddr))
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
It("SetHWAddrByIP must be injective", func() {
|
||||
|
||||
_ = containerNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
err := ip.SetHWAddrByIP(containerVethName, ip4one, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
hwaddrAfter1 := getHwAddr(containerVethName)
|
||||
|
||||
err = ip.SetHWAddrByIP(containerVethName, ip4two, nil)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
hwaddrAfter2 := getHwAddr(containerVethName)
|
||||
|
||||
Expect(hwaddrAfter1).NotTo(Equal(hwaddrAfter2))
|
||||
return nil
|
||||
})
|
||||
})
|
||||
})
|
47
vendor/github.com/containernetworking/cni/pkg/ip/route.go
generated
vendored
Normal file
47
vendor/github.com/containernetworking/cni/pkg/ip/route.go
generated
vendored
Normal file
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ip
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
// AddDefaultRoute sets the default route on the given gateway.
|
||||
func AddDefaultRoute(gw net.IP, dev netlink.Link) error {
|
||||
_, defNet, _ := net.ParseCIDR("0.0.0.0/0")
|
||||
return AddRoute(defNet, gw, dev)
|
||||
}
|
||||
|
||||
// AddRoute adds a universally-scoped route to a device.
|
||||
func AddRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error {
|
||||
return netlink.RouteAdd(&netlink.Route{
|
||||
LinkIndex: dev.Attrs().Index,
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
Dst: ipn,
|
||||
Gw: gw,
|
||||
})
|
||||
}
|
||||
|
||||
// AddHostRoute adds a host-scoped route to a device.
|
||||
func AddHostRoute(ipn *net.IPNet, gw net.IP, dev netlink.Link) error {
|
||||
return netlink.RouteAdd(&netlink.Route{
|
||||
LinkIndex: dev.Attrs().Index,
|
||||
Scope: netlink.SCOPE_HOST,
|
||||
Dst: ipn,
|
||||
Gw: gw,
|
||||
})
|
||||
}
|
68
vendor/github.com/containernetworking/cni/pkg/ipam/ipam.go
generated
vendored
Normal file
68
vendor/github.com/containernetworking/cni/pkg/ipam/ipam.go
generated
vendored
Normal file
|
@ -0,0 +1,68 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ipam
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/invoke"
|
||||
"github.com/containernetworking/cni/pkg/ip"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func ExecAdd(plugin string, netconf []byte) (*types.Result, error) {
|
||||
return invoke.DelegateAdd(plugin, netconf)
|
||||
}
|
||||
|
||||
func ExecDel(plugin string, netconf []byte) error {
|
||||
return invoke.DelegateDel(plugin, netconf)
|
||||
}
|
||||
|
||||
// ConfigureIface takes the result of IPAM plugin and
|
||||
// applies to the ifName interface
|
||||
func ConfigureIface(ifName string, res *types.Result) error {
|
||||
link, err := netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup %q: %v", ifName, err)
|
||||
}
|
||||
|
||||
if err := netlink.LinkSetUp(link); err != nil {
|
||||
return fmt.Errorf("failed to set %q UP: %v", ifName, err)
|
||||
}
|
||||
|
||||
// TODO(eyakubovich): IPv6
|
||||
addr := &netlink.Addr{IPNet: &res.IP4.IP, Label: ""}
|
||||
if err = netlink.AddrAdd(link, addr); err != nil {
|
||||
return fmt.Errorf("failed to add IP addr to %q: %v", ifName, err)
|
||||
}
|
||||
|
||||
for _, r := range res.IP4.Routes {
|
||||
gw := r.GW
|
||||
if gw == nil {
|
||||
gw = res.IP4.Gateway
|
||||
}
|
||||
if err = ip.AddRoute(&r.Dst, gw, link); err != nil {
|
||||
// we skip over duplicate routes as we assume the first one wins
|
||||
if !os.IsExist(err) {
|
||||
return fmt.Errorf("failed to add route '%v via %v dev %v': %v", r.Dst, gw, ifName, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
27
vendor/github.com/containernetworking/cni/pkg/ipam/ipam_suite_test.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/pkg/ipam/ipam_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ipam_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIpam(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Ipam Suite")
|
||||
}
|
34
vendor/github.com/containernetworking/cni/pkg/ns/ns_suite_test.go
generated
vendored
Normal file
34
vendor/github.com/containernetworking/cni/pkg/ns/ns_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ns_test
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"runtime"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
"github.com/onsi/ginkgo/config"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNs(t *testing.T) {
|
||||
rand.Seed(config.GinkgoConfig.RandomSeed)
|
||||
runtime.LockOSThread()
|
||||
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "pkg/ns Suite")
|
||||
}
|
252
vendor/github.com/containernetworking/cni/pkg/ns/ns_test.go
generated
vendored
Normal file
252
vendor/github.com/containernetworking/cni/pkg/ns/ns_test.go
generated
vendored
Normal file
|
@ -0,0 +1,252 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ns_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/ns"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func getInodeCurNetNS() (uint64, error) {
|
||||
curNS, err := ns.GetCurrentNS()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer curNS.Close()
|
||||
return getInodeNS(curNS)
|
||||
}
|
||||
|
||||
func getInodeNS(netns ns.NetNS) (uint64, error) {
|
||||
return getInodeFd(int(netns.Fd()))
|
||||
}
|
||||
|
||||
func getInode(path string) (uint64, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
defer file.Close()
|
||||
return getInodeFd(int(file.Fd()))
|
||||
}
|
||||
|
||||
func getInodeFd(fd int) (uint64, error) {
|
||||
stat := &unix.Stat_t{}
|
||||
err := unix.Fstat(fd, stat)
|
||||
return stat.Ino, err
|
||||
}
|
||||
|
||||
var _ = Describe("Linux namespace operations", func() {
|
||||
Describe("WithNetNS", func() {
|
||||
var (
|
||||
originalNetNS ns.NetNS
|
||||
targetNetNS ns.NetNS
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
var err error
|
||||
|
||||
originalNetNS, err = ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
targetNetNS, err = ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(targetNetNS.Close()).To(Succeed())
|
||||
Expect(originalNetNS.Close()).To(Succeed())
|
||||
})
|
||||
|
||||
It("executes the callback within the target network namespace", func() {
|
||||
expectedInode, err := getInodeNS(targetNetNS)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = targetNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
actualInode, err := getInodeCurNetNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(actualInode).To(Equal(expectedInode))
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("provides the original namespace as the argument to the callback", func() {
|
||||
// Ensure we start in originalNetNS
|
||||
err := originalNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
origNSInode, err := getInodeNS(originalNetNS)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = targetNetNS.Do(func(hostNS ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
hostNSInode, err := getInodeNS(hostNS)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(hostNSInode).To(Equal(origNSInode))
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
Context("when the callback returns an error", func() {
|
||||
It("restores the calling thread to the original namespace before returning", func() {
|
||||
err := originalNetNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
preTestInode, err := getInodeCurNetNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_ = targetNetNS.Do(func(ns.NetNS) error {
|
||||
return errors.New("potato")
|
||||
})
|
||||
|
||||
postTestInode, err := getInodeCurNetNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(postTestInode).To(Equal(preTestInode))
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("returns the error from the callback", func() {
|
||||
err := targetNetNS.Do(func(ns.NetNS) error {
|
||||
return errors.New("potato")
|
||||
})
|
||||
Expect(err).To(MatchError("potato"))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("validating inode mapping to namespaces", func() {
|
||||
It("checks that different namespaces have different inodes", func() {
|
||||
origNSInode, err := getInodeNS(originalNetNS)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
testNsInode, err := getInodeNS(targetNetNS)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(testNsInode).NotTo(Equal(0))
|
||||
Expect(testNsInode).NotTo(Equal(origNSInode))
|
||||
})
|
||||
|
||||
It("should not leak a closed netns onto any threads in the process", func() {
|
||||
By("creating a new netns")
|
||||
createdNetNS, err := ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
By("discovering the inode of the created netns")
|
||||
createdNetNSInode, err := getInodeNS(createdNetNS)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
createdNetNS.Close()
|
||||
|
||||
By("comparing against the netns inode of every thread in the process")
|
||||
for _, netnsPath := range allNetNSInCurrentProcess() {
|
||||
netnsInode, err := getInode(netnsPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(netnsInode).NotTo(Equal(createdNetNSInode))
|
||||
}
|
||||
})
|
||||
|
||||
It("fails when the path is not a namespace", func() {
|
||||
tempFile, err := ioutil.TempFile("", "nstest")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer tempFile.Close()
|
||||
|
||||
nspath := tempFile.Name()
|
||||
defer os.Remove(nspath)
|
||||
|
||||
_, err = ns.GetNS(nspath)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(BeAssignableToTypeOf(ns.NSPathNotNSErr{}))
|
||||
Expect(err).NotTo(BeAssignableToTypeOf(ns.NSPathNotExistErr{}))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("closing a network namespace", func() {
|
||||
It("should prevent further operations", func() {
|
||||
createdNetNS, err := ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = createdNetNS.Close()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = createdNetNS.Do(func(ns.NetNS) error { return nil })
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
err = createdNetNS.Set()
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should only work once", func() {
|
||||
createdNetNS, err := ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = createdNetNS.Close()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = createdNetNS.Close()
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Describe("IsNSorErr", func() {
|
||||
It("should detect a namespace", func() {
|
||||
createdNetNS, err := ns.NewNS()
|
||||
err = ns.IsNSorErr(createdNetNS.Path())
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should refuse other paths", func() {
|
||||
tempFile, err := ioutil.TempFile("", "nstest")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer tempFile.Close()
|
||||
|
||||
nspath := tempFile.Name()
|
||||
defer os.Remove(nspath)
|
||||
|
||||
err = ns.IsNSorErr(nspath)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(BeAssignableToTypeOf(ns.NSPathNotNSErr{}))
|
||||
Expect(err).NotTo(BeAssignableToTypeOf(ns.NSPathNotExistErr{}))
|
||||
})
|
||||
|
||||
It("should error on non-existing paths", func() {
|
||||
err := ns.IsNSorErr("/tmp/IDoNotExist")
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(err).To(BeAssignableToTypeOf(ns.NSPathNotExistErr{}))
|
||||
Expect(err).NotTo(BeAssignableToTypeOf(ns.NSPathNotNSErr{}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func allNetNSInCurrentProcess() []string {
|
||||
pid := unix.Getpid()
|
||||
paths, err := filepath.Glob(fmt.Sprintf("/proc/%d/task/*/ns/net", pid))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return paths
|
||||
}
|
213
vendor/github.com/containernetworking/cni/pkg/skel/skel.go
generated
vendored
Normal file
213
vendor/github.com/containernetworking/cni/pkg/skel/skel.go
generated
vendored
Normal file
|
@ -0,0 +1,213 @@
|
|||
// Copyright 2014-2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package skel provides skeleton code for a CNI plugin.
|
||||
// In particular, it implements argument parsing and validation.
|
||||
package skel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
)
|
||||
|
||||
// CmdArgs captures all the arguments passed in to the plugin
|
||||
// via both env vars and stdin
|
||||
type CmdArgs struct {
|
||||
ContainerID string
|
||||
Netns string
|
||||
IfName string
|
||||
Args string
|
||||
Path string
|
||||
StdinData []byte
|
||||
}
|
||||
|
||||
type dispatcher struct {
|
||||
Getenv func(string) string
|
||||
Stdin io.Reader
|
||||
Stdout io.Writer
|
||||
Stderr io.Writer
|
||||
|
||||
ConfVersionDecoder version.ConfigDecoder
|
||||
VersionReconciler version.Reconciler
|
||||
}
|
||||
|
||||
type reqForCmdEntry map[string]bool
|
||||
|
||||
func (t *dispatcher) getCmdArgsFromEnv() (string, *CmdArgs, error) {
|
||||
var cmd, contID, netns, ifName, args, path string
|
||||
|
||||
vars := []struct {
|
||||
name string
|
||||
val *string
|
||||
reqForCmd reqForCmdEntry
|
||||
}{
|
||||
{
|
||||
"CNI_COMMAND",
|
||||
&cmd,
|
||||
reqForCmdEntry{
|
||||
"ADD": true,
|
||||
"DEL": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
"CNI_CONTAINERID",
|
||||
&contID,
|
||||
reqForCmdEntry{
|
||||
"ADD": false,
|
||||
"DEL": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
"CNI_NETNS",
|
||||
&netns,
|
||||
reqForCmdEntry{
|
||||
"ADD": true,
|
||||
"DEL": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
"CNI_IFNAME",
|
||||
&ifName,
|
||||
reqForCmdEntry{
|
||||
"ADD": true,
|
||||
"DEL": true,
|
||||
},
|
||||
},
|
||||
{
|
||||
"CNI_ARGS",
|
||||
&args,
|
||||
reqForCmdEntry{
|
||||
"ADD": false,
|
||||
"DEL": false,
|
||||
},
|
||||
},
|
||||
{
|
||||
"CNI_PATH",
|
||||
&path,
|
||||
reqForCmdEntry{
|
||||
"ADD": true,
|
||||
"DEL": true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
argsMissing := false
|
||||
for _, v := range vars {
|
||||
*v.val = t.Getenv(v.name)
|
||||
if *v.val == "" {
|
||||
if v.reqForCmd[cmd] || v.name == "CNI_COMMAND" {
|
||||
fmt.Fprintf(t.Stderr, "%v env variable missing\n", v.name)
|
||||
argsMissing = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if argsMissing {
|
||||
return "", nil, fmt.Errorf("required env variables missing")
|
||||
}
|
||||
|
||||
stdinData, err := ioutil.ReadAll(t.Stdin)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("error reading from stdin: %v", err)
|
||||
}
|
||||
|
||||
cmdArgs := &CmdArgs{
|
||||
ContainerID: contID,
|
||||
Netns: netns,
|
||||
IfName: ifName,
|
||||
Args: args,
|
||||
Path: path,
|
||||
StdinData: stdinData,
|
||||
}
|
||||
return cmd, cmdArgs, nil
|
||||
}
|
||||
|
||||
func createTypedError(f string, args ...interface{}) *types.Error {
|
||||
return &types.Error{
|
||||
Code: 100,
|
||||
Msg: fmt.Sprintf(f, args...),
|
||||
}
|
||||
}
|
||||
|
||||
func (t *dispatcher) checkVersionAndCall(cmdArgs *CmdArgs, pluginVersionInfo version.PluginInfo, toCall func(*CmdArgs) error) error {
|
||||
configVersion, err := t.ConfVersionDecoder.Decode(cmdArgs.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
verErr := t.VersionReconciler.Check(configVersion, pluginVersionInfo)
|
||||
if verErr != nil {
|
||||
return &types.Error{
|
||||
Code: types.ErrIncompatibleCNIVersion,
|
||||
Msg: "incompatible CNI versions",
|
||||
Details: verErr.Details(),
|
||||
}
|
||||
}
|
||||
return toCall(cmdArgs)
|
||||
}
|
||||
|
||||
func (t *dispatcher) pluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo) *types.Error {
|
||||
cmd, cmdArgs, err := t.getCmdArgsFromEnv()
|
||||
if err != nil {
|
||||
return createTypedError(err.Error())
|
||||
}
|
||||
|
||||
switch cmd {
|
||||
case "ADD":
|
||||
err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdAdd)
|
||||
case "DEL":
|
||||
err = t.checkVersionAndCall(cmdArgs, versionInfo, cmdDel)
|
||||
case "VERSION":
|
||||
err = versionInfo.Encode(t.Stdout)
|
||||
default:
|
||||
return createTypedError("unknown CNI_COMMAND: %v", cmd)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if e, ok := err.(*types.Error); ok {
|
||||
// don't wrap Error in Error
|
||||
return e
|
||||
}
|
||||
return createTypedError(err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PluginMain is the "main" for a plugin. It accepts
|
||||
// two callback functions for add and del commands.
|
||||
func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error, versionInfo version.PluginInfo) {
|
||||
caller := dispatcher{
|
||||
Getenv: os.Getenv,
|
||||
Stdin: os.Stdin,
|
||||
Stdout: os.Stdout,
|
||||
Stderr: os.Stderr,
|
||||
}
|
||||
|
||||
err := caller.pluginMain(cmdAdd, cmdDel, versionInfo)
|
||||
if err != nil {
|
||||
dieErr(err)
|
||||
}
|
||||
}
|
||||
|
||||
func dieErr(e *types.Error) {
|
||||
if err := e.Print(); err != nil {
|
||||
log.Print("Error writing error JSON to stdout: ", err)
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
27
vendor/github.com/containernetworking/cni/pkg/skel/skel_suite_test.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/pkg/skel/skel_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package skel
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSkel(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Skel Suite")
|
||||
}
|
346
vendor/github.com/containernetworking/cni/pkg/skel/skel_test.go
generated
vendored
Normal file
346
vendor/github.com/containernetworking/cni/pkg/skel/skel_test.go
generated
vendored
Normal file
|
@ -0,0 +1,346 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package skel
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/testutils"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/extensions/table"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
type fakeCmd struct {
|
||||
CallCount int
|
||||
Returns struct {
|
||||
Error error
|
||||
}
|
||||
Received struct {
|
||||
CmdArgs *CmdArgs
|
||||
}
|
||||
}
|
||||
|
||||
func (c *fakeCmd) Func(args *CmdArgs) error {
|
||||
c.CallCount++
|
||||
c.Received.CmdArgs = args
|
||||
return c.Returns.Error
|
||||
}
|
||||
|
||||
var _ = Describe("dispatching to the correct callback", func() {
|
||||
var (
|
||||
environment map[string]string
|
||||
stdinData string
|
||||
stdout, stderr *bytes.Buffer
|
||||
cmdAdd, cmdDel *fakeCmd
|
||||
dispatch *dispatcher
|
||||
expectedCmdArgs *CmdArgs
|
||||
versionInfo version.PluginInfo
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
environment = map[string]string{
|
||||
"CNI_COMMAND": "ADD",
|
||||
"CNI_CONTAINERID": "some-container-id",
|
||||
"CNI_NETNS": "/some/netns/path",
|
||||
"CNI_IFNAME": "eth0",
|
||||
"CNI_ARGS": "some;extra;args",
|
||||
"CNI_PATH": "/some/cni/path",
|
||||
}
|
||||
|
||||
stdinData = `{ "some": "config", "cniVersion": "9.8.7" }`
|
||||
stdout = &bytes.Buffer{}
|
||||
stderr = &bytes.Buffer{}
|
||||
versionInfo = version.PluginSupports("9.8.7")
|
||||
dispatch = &dispatcher{
|
||||
Getenv: func(key string) string { return environment[key] },
|
||||
Stdin: strings.NewReader(stdinData),
|
||||
Stdout: stdout,
|
||||
Stderr: stderr,
|
||||
}
|
||||
cmdAdd = &fakeCmd{}
|
||||
cmdDel = &fakeCmd{}
|
||||
expectedCmdArgs = &CmdArgs{
|
||||
ContainerID: "some-container-id",
|
||||
Netns: "/some/netns/path",
|
||||
IfName: "eth0",
|
||||
Args: "some;extra;args",
|
||||
Path: "/some/cni/path",
|
||||
StdinData: []byte(stdinData),
|
||||
}
|
||||
})
|
||||
|
||||
var envVarChecker = func(envVar string, isRequired bool) {
|
||||
delete(environment, envVar)
|
||||
|
||||
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
if isRequired {
|
||||
Expect(err).To(Equal(&types.Error{
|
||||
Code: 100,
|
||||
Msg: "required env variables missing",
|
||||
}))
|
||||
Expect(stderr.String()).To(ContainSubstring(envVar + " env variable missing\n"))
|
||||
} else {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
}
|
||||
}
|
||||
|
||||
Context("when the CNI_COMMAND is ADD", func() {
|
||||
It("extracts env vars and stdin data and calls cmdAdd", func() {
|
||||
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(cmdAdd.CallCount).To(Equal(1))
|
||||
Expect(cmdDel.CallCount).To(Equal(0))
|
||||
Expect(cmdAdd.Received.CmdArgs).To(Equal(expectedCmdArgs))
|
||||
})
|
||||
|
||||
It("does not call cmdDel", func() {
|
||||
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(cmdDel.CallCount).To(Equal(0))
|
||||
})
|
||||
|
||||
DescribeTable("required / optional env vars", envVarChecker,
|
||||
Entry("command", "CNI_COMMAND", true),
|
||||
Entry("container id", "CNI_CONTAINERID", false),
|
||||
Entry("net ns", "CNI_NETNS", true),
|
||||
Entry("if name", "CNI_IFNAME", true),
|
||||
Entry("args", "CNI_ARGS", false),
|
||||
Entry("path", "CNI_PATH", true),
|
||||
)
|
||||
|
||||
Context("when multiple required env vars are missing", func() {
|
||||
BeforeEach(func() {
|
||||
delete(environment, "CNI_NETNS")
|
||||
delete(environment, "CNI_IFNAME")
|
||||
delete(environment, "CNI_PATH")
|
||||
})
|
||||
|
||||
It("reports that all of them are missing, not just the first", func() {
|
||||
Expect(dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)).NotTo(Succeed())
|
||||
log := stderr.String()
|
||||
Expect(log).To(ContainSubstring("CNI_NETNS env variable missing\n"))
|
||||
Expect(log).To(ContainSubstring("CNI_IFNAME env variable missing\n"))
|
||||
Expect(log).To(ContainSubstring("CNI_PATH env variable missing\n"))
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the stdin data is missing the required cniVersion config", func() {
|
||||
BeforeEach(func() {
|
||||
dispatch.Stdin = strings.NewReader(`{ "some": "config" }`)
|
||||
})
|
||||
|
||||
Context("when the plugin supports version 0.1.0", func() {
|
||||
BeforeEach(func() {
|
||||
versionInfo = version.PluginSupports("0.1.0")
|
||||
expectedCmdArgs.StdinData = []byte(`{ "some": "config" }`)
|
||||
})
|
||||
|
||||
It("infers the config is 0.1.0 and calls the cmdAdd callback", func() {
|
||||
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(cmdAdd.CallCount).To(Equal(1))
|
||||
Expect(cmdAdd.Received.CmdArgs).To(Equal(expectedCmdArgs))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the plugin does not support 0.1.0", func() {
|
||||
BeforeEach(func() {
|
||||
versionInfo = version.PluginSupports("4.3.2")
|
||||
})
|
||||
|
||||
It("immediately returns a useful error", func() {
|
||||
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
Expect(err.Code).To(Equal(types.ErrIncompatibleCNIVersion)) // see https://github.com/containernetworking/cni/blob/master/SPEC.md#well-known-error-codes
|
||||
Expect(err.Msg).To(Equal("incompatible CNI versions"))
|
||||
Expect(err.Details).To(Equal(`config is "0.1.0", plugin supports ["4.3.2"]`))
|
||||
})
|
||||
|
||||
It("does not call either callback", func() {
|
||||
dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
Expect(cmdAdd.CallCount).To(Equal(0))
|
||||
Expect(cmdDel.CallCount).To(Equal(0))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the CNI_COMMAND is DEL", func() {
|
||||
BeforeEach(func() {
|
||||
environment["CNI_COMMAND"] = "DEL"
|
||||
})
|
||||
|
||||
It("calls cmdDel with the env vars and stdin data", func() {
|
||||
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(cmdDel.CallCount).To(Equal(1))
|
||||
Expect(cmdDel.Received.CmdArgs).To(Equal(expectedCmdArgs))
|
||||
})
|
||||
|
||||
It("does not call cmdAdd", func() {
|
||||
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(cmdAdd.CallCount).To(Equal(0))
|
||||
})
|
||||
|
||||
DescribeTable("required / optional env vars", envVarChecker,
|
||||
Entry("command", "CNI_COMMAND", true),
|
||||
Entry("container id", "CNI_CONTAINERID", false),
|
||||
Entry("net ns", "CNI_NETNS", false),
|
||||
Entry("if name", "CNI_IFNAME", true),
|
||||
Entry("args", "CNI_ARGS", false),
|
||||
Entry("path", "CNI_PATH", true),
|
||||
)
|
||||
})
|
||||
|
||||
Context("when the CNI_COMMAND is VERSION", func() {
|
||||
BeforeEach(func() {
|
||||
environment["CNI_COMMAND"] = "VERSION"
|
||||
})
|
||||
|
||||
It("prints the version to stdout", func() {
|
||||
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(stdout).To(MatchJSON(`{
|
||||
"cniVersion": "0.2.0",
|
||||
"supportedVersions": ["9.8.7"]
|
||||
}`))
|
||||
})
|
||||
|
||||
It("does not call cmdAdd or cmdDel", func() {
|
||||
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(cmdAdd.CallCount).To(Equal(0))
|
||||
Expect(cmdDel.CallCount).To(Equal(0))
|
||||
})
|
||||
|
||||
DescribeTable("VERSION does not need the usual env vars", envVarChecker,
|
||||
Entry("command", "CNI_COMMAND", true),
|
||||
Entry("container id", "CNI_CONTAINERID", false),
|
||||
Entry("net ns", "CNI_NETNS", false),
|
||||
Entry("if name", "CNI_IFNAME", false),
|
||||
Entry("args", "CNI_ARGS", false),
|
||||
Entry("path", "CNI_PATH", false),
|
||||
)
|
||||
|
||||
Context("when the stdin is empty", func() {
|
||||
BeforeEach(func() {
|
||||
dispatch.Stdin = strings.NewReader("")
|
||||
})
|
||||
|
||||
It("succeeds without error", func() {
|
||||
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(stdout).To(MatchJSON(`{
|
||||
"cniVersion": "0.2.0",
|
||||
"supportedVersions": ["9.8.7"]
|
||||
}`))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the CNI_COMMAND is unrecognized", func() {
|
||||
BeforeEach(func() {
|
||||
environment["CNI_COMMAND"] = "NOPE"
|
||||
})
|
||||
|
||||
It("does not call any cmd callback", func() {
|
||||
dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
|
||||
Expect(cmdAdd.CallCount).To(Equal(0))
|
||||
Expect(cmdDel.CallCount).To(Equal(0))
|
||||
})
|
||||
|
||||
It("returns an error", func() {
|
||||
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
|
||||
Expect(err).To(Equal(&types.Error{
|
||||
Code: 100,
|
||||
Msg: "unknown CNI_COMMAND: NOPE",
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when stdin cannot be read", func() {
|
||||
BeforeEach(func() {
|
||||
dispatch.Stdin = &testutils.BadReader{}
|
||||
})
|
||||
|
||||
It("does not call any cmd callback", func() {
|
||||
dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
|
||||
Expect(cmdAdd.CallCount).To(Equal(0))
|
||||
Expect(cmdDel.CallCount).To(Equal(0))
|
||||
})
|
||||
|
||||
It("wraps and returns the error", func() {
|
||||
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
|
||||
Expect(err).To(Equal(&types.Error{
|
||||
Code: 100,
|
||||
Msg: "error reading from stdin: banana",
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the callback returns an error", func() {
|
||||
Context("when it is a typed Error", func() {
|
||||
BeforeEach(func() {
|
||||
cmdAdd.Returns.Error = &types.Error{
|
||||
Code: 1234,
|
||||
Msg: "insufficient something",
|
||||
}
|
||||
})
|
||||
|
||||
It("returns the error as-is", func() {
|
||||
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
|
||||
Expect(err).To(Equal(&types.Error{
|
||||
Code: 1234,
|
||||
Msg: "insufficient something",
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when it is an unknown error", func() {
|
||||
BeforeEach(func() {
|
||||
cmdAdd.Returns.Error = errors.New("potato")
|
||||
})
|
||||
|
||||
It("wraps and returns the error", func() {
|
||||
err := dispatch.pluginMain(cmdAdd.Func, cmdDel.Func, versionInfo)
|
||||
|
||||
Expect(err).To(Equal(&types.Error{
|
||||
Code: 100,
|
||||
Msg: "potato",
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
33
vendor/github.com/containernetworking/cni/pkg/testutils/bad_reader.go
generated
vendored
Normal file
33
vendor/github.com/containernetworking/cni/pkg/testutils/bad_reader.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package testutils
|
||||
|
||||
import "errors"
|
||||
|
||||
// BadReader is an io.Reader which always errors
|
||||
type BadReader struct {
|
||||
Error error
|
||||
}
|
||||
|
||||
func (r *BadReader) Read(buffer []byte) (int, error) {
|
||||
if r.Error != nil {
|
||||
return 0, r.Error
|
||||
}
|
||||
return 0, errors.New("banana")
|
||||
}
|
||||
|
||||
func (r *BadReader) Close() error {
|
||||
return nil
|
||||
}
|
77
vendor/github.com/containernetworking/cni/pkg/testutils/cmd.go
generated
vendored
Normal file
77
vendor/github.com/containernetworking/cni/pkg/testutils/cmd.go
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package testutils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
|
||||
func envCleanup() {
|
||||
os.Unsetenv("CNI_COMMAND")
|
||||
os.Unsetenv("CNI_PATH")
|
||||
os.Unsetenv("CNI_NETNS")
|
||||
os.Unsetenv("CNI_IFNAME")
|
||||
}
|
||||
|
||||
func CmdAddWithResult(cniNetns, cniIfname string, f func() error) (*types.Result, error) {
|
||||
os.Setenv("CNI_COMMAND", "ADD")
|
||||
os.Setenv("CNI_PATH", os.Getenv("PATH"))
|
||||
os.Setenv("CNI_NETNS", cniNetns)
|
||||
os.Setenv("CNI_IFNAME", cniIfname)
|
||||
defer envCleanup()
|
||||
|
||||
// Redirect stdout to capture plugin result
|
||||
oldStdout := os.Stdout
|
||||
r, w, err := os.Pipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
os.Stdout = w
|
||||
err = f()
|
||||
w.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// parse the result
|
||||
out, err := ioutil.ReadAll(r)
|
||||
os.Stdout = oldStdout
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := types.Result{}
|
||||
err = json.Unmarshal(out, &result)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func CmdDelWithResult(cniNetns, cniIfname string, f func() error) error {
|
||||
os.Setenv("CNI_COMMAND", "DEL")
|
||||
os.Setenv("CNI_PATH", os.Getenv("PATH"))
|
||||
os.Setenv("CNI_NETNS", cniNetns)
|
||||
os.Setenv("CNI_IFNAME", cniIfname)
|
||||
defer envCleanup()
|
||||
|
||||
return f()
|
||||
}
|
121
vendor/github.com/containernetworking/cni/pkg/types/args_test.go
generated
vendored
Normal file
121
vendor/github.com/containernetworking/cni/pkg/types/args_test.go
generated
vendored
Normal file
|
@ -0,0 +1,121 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package types_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
. "github.com/containernetworking/cni/pkg/types"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/extensions/table"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("UnmarshallableBool UnmarshalText", func() {
|
||||
DescribeTable("string to bool detection should succeed in all cases",
|
||||
func(inputs []string, expected bool) {
|
||||
for _, s := range inputs {
|
||||
var ub UnmarshallableBool
|
||||
err := ub.UnmarshalText([]byte(s))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(ub).To(Equal(UnmarshallableBool(expected)))
|
||||
}
|
||||
},
|
||||
Entry("parse to true", []string{"True", "true", "1"}, true),
|
||||
Entry("parse to false", []string{"False", "false", "0"}, false),
|
||||
)
|
||||
|
||||
Context("When passed an invalid value", func() {
|
||||
It("should result in an error", func() {
|
||||
var ub UnmarshallableBool
|
||||
err := ub.UnmarshalText([]byte("invalid"))
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("UnmarshallableString UnmarshalText", func() {
|
||||
DescribeTable("string to string detection should succeed in all cases",
|
||||
func(inputs []string, expected string) {
|
||||
for _, s := range inputs {
|
||||
var us UnmarshallableString
|
||||
err := us.UnmarshalText([]byte(s))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(string(us)).To(Equal(expected))
|
||||
}
|
||||
},
|
||||
Entry("parse empty string", []string{""}, ""),
|
||||
Entry("parse non-empty string", []string{"notempty"}, "notempty"),
|
||||
)
|
||||
})
|
||||
|
||||
var _ = Describe("GetKeyField", func() {
|
||||
type testcontainer struct {
|
||||
Valid string `json:"valid,omitempty"`
|
||||
}
|
||||
var (
|
||||
container = testcontainer{Valid: "valid"}
|
||||
containerInterface = func(i interface{}) interface{} { return i }(&container)
|
||||
containerValue = reflect.ValueOf(containerInterface)
|
||||
)
|
||||
Context("When a valid field is provided", func() {
|
||||
It("should return the correct field", func() {
|
||||
field := GetKeyField("Valid", containerValue)
|
||||
Expect(field.String()).To(Equal("valid"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("LoadArgs", func() {
|
||||
Context("When no arguments are passed", func() {
|
||||
It("LoadArgs should succeed", func() {
|
||||
err := LoadArgs("", struct{}{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("When unknown arguments are passed and ignored", func() {
|
||||
It("LoadArgs should succeed", func() {
|
||||
ca := CommonArgs{}
|
||||
err := LoadArgs("IgnoreUnknown=True;Unk=nown", &ca)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("When unknown arguments are passed and not ignored", func() {
|
||||
It("LoadArgs should fail", func() {
|
||||
ca := CommonArgs{}
|
||||
err := LoadArgs("Unk=nown", &ca)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("When unknown arguments are passed and explicitly not ignored", func() {
|
||||
It("LoadArgs should fail", func() {
|
||||
ca := CommonArgs{}
|
||||
err := LoadArgs("IgnoreUnknown=0, Unk=nown", &ca)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("When known arguments are passed", func() {
|
||||
It("LoadArgs should succeed", func() {
|
||||
ca := CommonArgs{}
|
||||
err := LoadArgs("IgnoreUnknown=1", &ca)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
27
vendor/github.com/containernetworking/cni/pkg/types/types_suite_test.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/pkg/types/types_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package types_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTypes(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Types Suite")
|
||||
}
|
63
vendor/github.com/containernetworking/cni/pkg/utils/hwaddr/hwaddr.go
generated
vendored
Normal file
63
vendor/github.com/containernetworking/cni/pkg/utils/hwaddr/hwaddr.go
generated
vendored
Normal file
|
@ -0,0 +1,63 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hwaddr
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
)
|
||||
|
||||
const (
|
||||
ipRelevantByteLen = 4
|
||||
PrivateMACPrefixString = "0a:58"
|
||||
)
|
||||
|
||||
var (
|
||||
// private mac prefix safe to use
|
||||
PrivateMACPrefix = []byte{0x0a, 0x58}
|
||||
)
|
||||
|
||||
type SupportIp4OnlyErr struct{ msg string }
|
||||
|
||||
func (e SupportIp4OnlyErr) Error() string { return e.msg }
|
||||
|
||||
type MacParseErr struct{ msg string }
|
||||
|
||||
func (e MacParseErr) Error() string { return e.msg }
|
||||
|
||||
type InvalidPrefixLengthErr struct{ msg string }
|
||||
|
||||
func (e InvalidPrefixLengthErr) Error() string { return e.msg }
|
||||
|
||||
// GenerateHardwareAddr4 generates 48 bit virtual mac addresses based on the IP4 input.
|
||||
func GenerateHardwareAddr4(ip net.IP, prefix []byte) (net.HardwareAddr, error) {
|
||||
switch {
|
||||
|
||||
case ip.To4() == nil:
|
||||
return nil, SupportIp4OnlyErr{msg: "GenerateHardwareAddr4 only supports valid IPv4 address as input"}
|
||||
|
||||
case len(prefix) != len(PrivateMACPrefix):
|
||||
return nil, InvalidPrefixLengthErr{msg: fmt.Sprintf(
|
||||
"Prefix has length %d instead of %d", len(prefix), len(PrivateMACPrefix)),
|
||||
}
|
||||
}
|
||||
|
||||
ipByteLen := len(ip)
|
||||
return (net.HardwareAddr)(
|
||||
append(
|
||||
prefix,
|
||||
ip[ipByteLen-ipRelevantByteLen:ipByteLen]...),
|
||||
), nil
|
||||
}
|
27
vendor/github.com/containernetworking/cni/pkg/utils/hwaddr/hwaddr_suite_test.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/pkg/utils/hwaddr/hwaddr_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hwaddr_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHwaddr(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Hwaddr Suite")
|
||||
}
|
74
vendor/github.com/containernetworking/cni/pkg/utils/hwaddr/hwaddr_test.go
generated
vendored
Normal file
74
vendor/github.com/containernetworking/cni/pkg/utils/hwaddr/hwaddr_test.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package hwaddr_test
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/utils/hwaddr"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Hwaddr", func() {
|
||||
Context("Generate Hardware Address", func() {
|
||||
It("generate hardware address based on ipv4 address", func() {
|
||||
testCases := []struct {
|
||||
ip net.IP
|
||||
expectedMAC net.HardwareAddr
|
||||
}{
|
||||
{
|
||||
ip: net.ParseIP("10.0.0.2"),
|
||||
expectedMAC: (net.HardwareAddr)(append(hwaddr.PrivateMACPrefix, 0x0a, 0x00, 0x00, 0x02)),
|
||||
},
|
||||
{
|
||||
ip: net.ParseIP("10.250.0.244"),
|
||||
expectedMAC: (net.HardwareAddr)(append(hwaddr.PrivateMACPrefix, 0x0a, 0xfa, 0x00, 0xf4)),
|
||||
},
|
||||
{
|
||||
ip: net.ParseIP("172.17.0.2"),
|
||||
expectedMAC: (net.HardwareAddr)(append(hwaddr.PrivateMACPrefix, 0xac, 0x11, 0x00, 0x02)),
|
||||
},
|
||||
{
|
||||
ip: net.IPv4(byte(172), byte(17), byte(0), byte(2)),
|
||||
expectedMAC: (net.HardwareAddr)(append(hwaddr.PrivateMACPrefix, 0xac, 0x11, 0x00, 0x02)),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
mac, err := hwaddr.GenerateHardwareAddr4(tc.ip, hwaddr.PrivateMACPrefix)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(mac).To(Equal(tc.expectedMAC))
|
||||
}
|
||||
})
|
||||
|
||||
It("return error if input is not ipv4 address", func() {
|
||||
testCases := []net.IP{
|
||||
net.ParseIP(""),
|
||||
net.ParseIP("2001:db8:0:1:1:1:1:1"),
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
_, err := hwaddr.GenerateHardwareAddr4(tc, hwaddr.PrivateMACPrefix)
|
||||
Expect(err).To(BeAssignableToTypeOf(hwaddr.SupportIp4OnlyErr{}))
|
||||
}
|
||||
})
|
||||
|
||||
It("return error if prefix is invalid", func() {
|
||||
_, err := hwaddr.GenerateHardwareAddr4(net.ParseIP("10.0.0.2"), []byte{0x58})
|
||||
Expect(err).To(BeAssignableToTypeOf(hwaddr.InvalidPrefixLengthErr{}))
|
||||
})
|
||||
})
|
||||
})
|
58
vendor/github.com/containernetworking/cni/pkg/utils/sysctl/sysctl_linux.go
generated
vendored
Normal file
58
vendor/github.com/containernetworking/cni/pkg/utils/sysctl/sysctl_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// build +linux
|
||||
|
||||
package sysctl
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Sysctl provides a method to set/get values from /proc/sys - in linux systems
|
||||
// new interface to set/get values of variables formerly handled by sysctl syscall
|
||||
// If optional `params` have only one string value - this function will
|
||||
// set this value into coresponding sysctl variable
|
||||
func Sysctl(name string, params ...string) (string, error) {
|
||||
if len(params) > 1 {
|
||||
return "", fmt.Errorf("unexcepted additional parameters")
|
||||
} else if len(params) == 1 {
|
||||
return setSysctl(name, params[0])
|
||||
}
|
||||
return getSysctl(name)
|
||||
}
|
||||
|
||||
func getSysctl(name string) (string, error) {
|
||||
fullName := filepath.Join("/proc/sys", strings.Replace(name, ".", "/", -1))
|
||||
fullName = filepath.Clean(fullName)
|
||||
data, err := ioutil.ReadFile(fullName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(data[:len(data)-1]), nil
|
||||
}
|
||||
|
||||
func setSysctl(name, value string) (string, error) {
|
||||
fullName := filepath.Join("/proc/sys", strings.Replace(name, ".", "/", -1))
|
||||
fullName = filepath.Clean(fullName)
|
||||
if err := ioutil.WriteFile(fullName, []byte(value), 0644); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return getSysctl(name)
|
||||
}
|
41
vendor/github.com/containernetworking/cni/pkg/utils/utils.go
generated
vendored
Normal file
41
vendor/github.com/containernetworking/cni/pkg/utils/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/sha512"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const (
|
||||
maxChainLength = 28
|
||||
chainPrefix = "CNI-"
|
||||
prefixLength = len(chainPrefix)
|
||||
)
|
||||
|
||||
// Generates a chain name to be used with iptables.
|
||||
// Ensures that the generated chain name is exactly
|
||||
// maxChainLength chars in length
|
||||
func FormatChainName(name string, id string) string {
|
||||
chainBytes := sha512.Sum512([]byte(name + id))
|
||||
chain := fmt.Sprintf("%s%x", chainPrefix, chainBytes)
|
||||
return chain[:maxChainLength]
|
||||
}
|
||||
|
||||
// FormatComment returns a comment used for easier
|
||||
// rule identification within iptables.
|
||||
func FormatComment(name string, id string) string {
|
||||
return fmt.Sprintf("name: %q id: %q", name, id)
|
||||
}
|
27
vendor/github.com/containernetworking/cni/pkg/utils/utils_suite_test.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/pkg/utils/utils_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package utils_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUtils(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Utils Suite")
|
||||
}
|
51
vendor/github.com/containernetworking/cni/pkg/utils/utils_test.go
generated
vendored
Normal file
51
vendor/github.com/containernetworking/cni/pkg/utils/utils_test.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Utils", func() {
|
||||
It("must format a short name", func() {
|
||||
chain := FormatChainName("test", "1234")
|
||||
Expect(len(chain)).To(Equal(maxChainLength))
|
||||
Expect(chain).To(Equal("CNI-2bbe0c48b91a7d1b8a6753a8"))
|
||||
})
|
||||
|
||||
It("must truncate a long name", func() {
|
||||
chain := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
|
||||
Expect(len(chain)).To(Equal(maxChainLength))
|
||||
Expect(chain).To(Equal("CNI-374f33fe84ab0ed84dcdebe3"))
|
||||
})
|
||||
|
||||
It("must be predictable", func() {
|
||||
chain1 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
|
||||
chain2 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
|
||||
Expect(len(chain1)).To(Equal(maxChainLength))
|
||||
Expect(len(chain2)).To(Equal(maxChainLength))
|
||||
Expect(chain1).To(Equal(chain2))
|
||||
})
|
||||
|
||||
It("must change when a character changes", func() {
|
||||
chain1 := FormatChainName("testalongnamethatdoesnotmakesense", "1234")
|
||||
chain2 := FormatChainName("testalongnamethatdoesnotmakesense", "1235")
|
||||
Expect(len(chain1)).To(Equal(maxChainLength))
|
||||
Expect(len(chain2)).To(Equal(maxChainLength))
|
||||
Expect(chain1).To(Equal("CNI-374f33fe84ab0ed84dcdebe3"))
|
||||
Expect(chain1).NotTo(Equal(chain2))
|
||||
})
|
||||
})
|
69
vendor/github.com/containernetworking/cni/pkg/version/conf_test.go
generated
vendored
Normal file
69
vendor/github.com/containernetworking/cni/pkg/version/conf_test.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package version_test
|
||||
|
||||
import (
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Decoding the version of network config", func() {
|
||||
var (
|
||||
decoder *version.ConfigDecoder
|
||||
configBytes []byte
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
decoder = &version.ConfigDecoder{}
|
||||
configBytes = []byte(`{ "cniVersion": "4.3.2" }`)
|
||||
})
|
||||
|
||||
Context("when the version is explict", func() {
|
||||
It("returns the version", func() {
|
||||
version, err := decoder.Decode(configBytes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(version).To(Equal("4.3.2"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the version is not present in the config", func() {
|
||||
BeforeEach(func() {
|
||||
configBytes = []byte(`{ "not-a-version-field": "foo" }`)
|
||||
})
|
||||
|
||||
It("assumes the config is version 0.1.0", func() {
|
||||
version, err := decoder.Decode(configBytes)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(version).To(Equal("0.1.0"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the config data is malformed", func() {
|
||||
BeforeEach(func() {
|
||||
configBytes = []byte(`{{{`)
|
||||
})
|
||||
|
||||
It("returns a useful error", func() {
|
||||
_, err := decoder.Decode(configBytes)
|
||||
Expect(err).To(MatchError(HavePrefix(
|
||||
"decoding version from network config: invalid character",
|
||||
)))
|
||||
})
|
||||
})
|
||||
})
|
167
vendor/github.com/containernetworking/cni/pkg/version/legacy_examples/example_runtime.go
generated
vendored
Normal file
167
vendor/github.com/containernetworking/cni/pkg/version/legacy_examples/example_runtime.go
generated
vendored
Normal file
|
@ -0,0 +1,167 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package legacy_examples
|
||||
|
||||
// An ExampleRuntime is a small program that uses libcni to invoke a network plugin.
|
||||
// It should call ADD and DELETE, verifying all intermediate steps
|
||||
// and data structures.
|
||||
type ExampleRuntime struct {
|
||||
Example
|
||||
NetConfs []string // The network configuration names to pass
|
||||
}
|
||||
|
||||
// NetConfs are various versioned network configuration files. Examples should
|
||||
// specify which version they expect
|
||||
var NetConfs = map[string]string{
|
||||
"unversioned": `{
|
||||
"name": "default",
|
||||
"type": "ptp",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24"
|
||||
}
|
||||
}`,
|
||||
"0.1.0": `{
|
||||
"cniVersion": "0.1.0",
|
||||
"name": "default",
|
||||
"type": "ptp",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24"
|
||||
}
|
||||
}`,
|
||||
}
|
||||
|
||||
// V010_Runtime creates a simple ptp network configuration, then
|
||||
// executes libcni against the currently-built plugins.
|
||||
var V010_Runtime = ExampleRuntime{
|
||||
NetConfs: []string{"unversioned", "0.1.0"},
|
||||
Example: Example{
|
||||
Name: "example_invoker_v010",
|
||||
CNIRepoGitRef: "c0d34c69", //version with ns.Do
|
||||
PluginSource: `package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/ns"
|
||||
"github.com/containernetworking/cni/libcni"
|
||||
)
|
||||
|
||||
func main(){
|
||||
code := exec()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
func exec() int {
|
||||
confBytes, err := ioutil.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
fmt.Printf("could not read netconfig from stdin: %+v", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
netConf, err := libcni.ConfFromBytes(confBytes)
|
||||
if err != nil {
|
||||
fmt.Printf("could not parse netconfig: %+v", err)
|
||||
return 1
|
||||
}
|
||||
fmt.Printf("Parsed network configuration: %+v\n", netConf.Network)
|
||||
|
||||
if len(os.Args) == 1 {
|
||||
fmt.Printf("Expect CNI plugin paths in argv")
|
||||
return 1
|
||||
}
|
||||
|
||||
targetNs, err := ns.NewNS()
|
||||
if err != nil {
|
||||
fmt.Printf("Could not create ns: %+v", err)
|
||||
return 1
|
||||
}
|
||||
defer targetNs.Close()
|
||||
|
||||
ifName := "eth0"
|
||||
|
||||
runtimeConf := &libcni.RuntimeConf{
|
||||
ContainerID: "some-container-id",
|
||||
NetNS: targetNs.Path(),
|
||||
IfName: ifName,
|
||||
}
|
||||
|
||||
cniConfig := &libcni.CNIConfig{Path: os.Args[1:]}
|
||||
|
||||
result, err := cniConfig.AddNetwork(netConf, runtimeConf)
|
||||
if err != nil {
|
||||
fmt.Printf("AddNetwork failed: %+v", err)
|
||||
return 2
|
||||
}
|
||||
fmt.Printf("AddNetwork result: %+v", result)
|
||||
|
||||
expectedIP := result.IP4.IP
|
||||
|
||||
err = targetNs.Do(func(ns.NetNS) error {
|
||||
netif, err := net.InterfaceByName(ifName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not retrieve interface: %v", err)
|
||||
}
|
||||
|
||||
addrs, err := netif.Addrs()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not retrieve addresses, %+v", err)
|
||||
}
|
||||
|
||||
found := false
|
||||
for _, addr := range addrs {
|
||||
if addr.String() == expectedIP.String() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
return fmt.Errorf("Far-side link did not have expected address %s", expectedIP)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return 4
|
||||
}
|
||||
|
||||
err = cniConfig.DelNetwork(netConf, runtimeConf)
|
||||
if err != nil {
|
||||
fmt.Printf("DelNetwork failed: %v", err)
|
||||
return 5
|
||||
}
|
||||
|
||||
err = targetNs.Do(func(ns.NetNS) error {
|
||||
_, err := net.InterfaceByName(ifName)
|
||||
if err == nil {
|
||||
return fmt.Errorf("interface was not deleted")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return 6
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
`,
|
||||
},
|
||||
}
|
138
vendor/github.com/containernetworking/cni/pkg/version/legacy_examples/examples.go
generated
vendored
Normal file
138
vendor/github.com/containernetworking/cni/pkg/version/legacy_examples/examples.go
generated
vendored
Normal file
|
@ -0,0 +1,138 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package legacy_examples contains sample code from prior versions of
|
||||
// the CNI library, for use in verifying backwards compatibility.
|
||||
package legacy_examples
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/version/testhelpers"
|
||||
)
|
||||
|
||||
// An Example is a Git reference to the CNI repo and a Golang CNI plugin that
|
||||
// builds against that version of the repo.
|
||||
//
|
||||
// By convention, every Example plugin returns an ADD result that is
|
||||
// semantically equivalent to the ExpectedResult.
|
||||
type Example struct {
|
||||
Name string
|
||||
CNIRepoGitRef string
|
||||
PluginSource string
|
||||
}
|
||||
|
||||
var buildDir = ""
|
||||
var buildDirLock sync.Mutex
|
||||
|
||||
func ensureBuildDirExists() error {
|
||||
buildDirLock.Lock()
|
||||
defer buildDirLock.Unlock()
|
||||
|
||||
if buildDir != "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
buildDir, err = ioutil.TempDir("", "cni-example-plugins")
|
||||
return err
|
||||
}
|
||||
|
||||
// Build builds the example, returning the path to the binary
|
||||
func (e Example) Build() (string, error) {
|
||||
if err := ensureBuildDirExists(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
outBinPath := filepath.Join(buildDir, e.Name)
|
||||
|
||||
if err := testhelpers.BuildAt([]byte(e.PluginSource), e.CNIRepoGitRef, outBinPath); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return outBinPath, nil
|
||||
}
|
||||
|
||||
// V010 acts like a CNI plugin from the v0.1.0 era
|
||||
var V010 = Example{
|
||||
Name: "example_v010",
|
||||
CNIRepoGitRef: "2c482f4",
|
||||
PluginSource: `package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
|
||||
var result = types.Result{
|
||||
IP4: &types.IPConfig{
|
||||
IP: net.IPNet{
|
||||
IP: net.ParseIP("10.1.2.3"),
|
||||
Mask: net.CIDRMask(24, 32),
|
||||
},
|
||||
Gateway: net.ParseIP("10.1.2.1"),
|
||||
Routes: []types.Route{
|
||||
types.Route{
|
||||
Dst: net.IPNet{
|
||||
IP: net.ParseIP("0.0.0.0"),
|
||||
Mask: net.CIDRMask(0, 32),
|
||||
},
|
||||
GW: net.ParseIP("10.1.0.1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DNS: types.DNS{
|
||||
Nameservers: []string{"8.8.8.8"},
|
||||
Domain: "example.com",
|
||||
},
|
||||
}
|
||||
|
||||
func c(_ *skel.CmdArgs) error { result.Print(); return nil }
|
||||
|
||||
func main() { skel.PluginMain(c, c) }
|
||||
`,
|
||||
}
|
||||
|
||||
// ExpectedResult is the current representation of the plugin result
|
||||
// that is expected from each of the examples.
|
||||
//
|
||||
// As we change the CNI spec, the Result type and this value may change.
|
||||
// The text of the example plugins should not.
|
||||
var ExpectedResult = &types.Result{
|
||||
IP4: &types.IPConfig{
|
||||
IP: net.IPNet{
|
||||
IP: net.ParseIP("10.1.2.3"),
|
||||
Mask: net.CIDRMask(24, 32),
|
||||
},
|
||||
Gateway: net.ParseIP("10.1.2.1"),
|
||||
Routes: []types.Route{
|
||||
types.Route{
|
||||
Dst: net.IPNet{
|
||||
IP: net.ParseIP("0.0.0.0"),
|
||||
Mask: net.CIDRMask(0, 32),
|
||||
},
|
||||
GW: net.ParseIP("10.1.0.1"),
|
||||
},
|
||||
},
|
||||
},
|
||||
DNS: types.DNS{
|
||||
Nameservers: []string{"8.8.8.8"},
|
||||
Domain: "example.com",
|
||||
},
|
||||
}
|
27
vendor/github.com/containernetworking/cni/pkg/version/legacy_examples/legacy_examples_suite_test.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/pkg/version/legacy_examples/legacy_examples_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package legacy_examples_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestLegacyExamples(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "LegacyExamples Suite")
|
||||
}
|
36
vendor/github.com/containernetworking/cni/pkg/version/legacy_examples/legacy_examples_test.go
generated
vendored
Normal file
36
vendor/github.com/containernetworking/cni/pkg/version/legacy_examples/legacy_examples_test.go
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package legacy_examples_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/version/legacy_examples"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("The v0.1.0 Example", func() {
|
||||
It("builds ok", func() {
|
||||
example := legacy_examples.V010
|
||||
pluginPath, err := example.Build()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(filepath.Base(pluginPath)).To(Equal(example.Name))
|
||||
|
||||
Expect(os.RemoveAll(pluginPath)).To(Succeed())
|
||||
})
|
||||
})
|
85
vendor/github.com/containernetworking/cni/pkg/version/plugin_test.go
generated
vendored
Normal file
85
vendor/github.com/containernetworking/cni/pkg/version/plugin_test.go
generated
vendored
Normal file
|
@ -0,0 +1,85 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package version_test
|
||||
|
||||
import (
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Decoding versions reported by a plugin", func() {
|
||||
var (
|
||||
decoder *version.PluginDecoder
|
||||
versionStdout []byte
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
decoder = &version.PluginDecoder{}
|
||||
versionStdout = []byte(`{
|
||||
"cniVersion": "some-library-version",
|
||||
"supportedVersions": [ "some-version", "some-other-version" ]
|
||||
}`)
|
||||
})
|
||||
|
||||
It("returns a PluginInfo that represents the given json bytes", func() {
|
||||
pluginInfo, err := decoder.Decode(versionStdout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(pluginInfo).NotTo(BeNil())
|
||||
Expect(pluginInfo.SupportedVersions()).To(Equal([]string{
|
||||
"some-version",
|
||||
"some-other-version",
|
||||
}))
|
||||
})
|
||||
|
||||
Context("when the bytes cannot be decoded as json", func() {
|
||||
BeforeEach(func() {
|
||||
versionStdout = []byte(`{{{`)
|
||||
})
|
||||
|
||||
It("returns a meaningful error", func() {
|
||||
_, err := decoder.Decode(versionStdout)
|
||||
Expect(err).To(MatchError("decoding version info: invalid character '{' looking for beginning of object key string"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the json bytes are missing the required CNIVersion field", func() {
|
||||
BeforeEach(func() {
|
||||
versionStdout = []byte(`{ "supportedVersions": [ "foo" ] }`)
|
||||
})
|
||||
|
||||
It("returns a meaningful error", func() {
|
||||
_, err := decoder.Decode(versionStdout)
|
||||
Expect(err).To(MatchError("decoding version info: missing field cniVersion"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when there are no supported versions", func() {
|
||||
BeforeEach(func() {
|
||||
versionStdout = []byte(`{ "cniVersion": "0.2.0" }`)
|
||||
})
|
||||
|
||||
It("assumes that the supported versions are 0.1.0 and 0.2.0", func() {
|
||||
pluginInfo, err := decoder.Decode(versionStdout)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(pluginInfo).NotTo(BeNil())
|
||||
Expect(pluginInfo.SupportedVersions()).To(Equal([]string{
|
||||
"0.1.0",
|
||||
"0.2.0",
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
})
|
51
vendor/github.com/containernetworking/cni/pkg/version/reconcile_test.go
generated
vendored
Normal file
51
vendor/github.com/containernetworking/cni/pkg/version/reconcile_test.go
generated
vendored
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package version_test
|
||||
|
||||
import (
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("Reconcile versions of net config with versions supported by plugins", func() {
|
||||
var (
|
||||
reconciler *version.Reconciler
|
||||
pluginInfo version.PluginInfo
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
reconciler = &version.Reconciler{}
|
||||
pluginInfo = version.PluginSupports("1.2.3", "4.3.2")
|
||||
})
|
||||
|
||||
It("succeeds if the config version is supported by the plugin", func() {
|
||||
err := reconciler.Check("4.3.2", pluginInfo)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
Context("when the config version is not supported by the plugin", func() {
|
||||
It("returns a helpful error", func() {
|
||||
err := reconciler.Check("0.1.0", pluginInfo)
|
||||
|
||||
Expect(err).To(Equal(&version.ErrorIncompatible{
|
||||
Config: "0.1.0",
|
||||
Plugin: []string{"1.2.3", "4.3.2"},
|
||||
}))
|
||||
|
||||
Expect(err.Error()).To(Equal(`incompatible CNI versions: config is "0.1.0", plugin supports ["1.2.3" "4.3.2"]`))
|
||||
})
|
||||
})
|
||||
})
|
156
vendor/github.com/containernetworking/cni/pkg/version/testhelpers/testhelpers.go
generated
vendored
Normal file
156
vendor/github.com/containernetworking/cni/pkg/version/testhelpers/testhelpers.go
generated
vendored
Normal file
|
@ -0,0 +1,156 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package testhelpers supports testing of CNI components of different versions
|
||||
//
|
||||
// For example, to build a plugin against an old version of the CNI library,
|
||||
// we can pass the plugin's source and the old git commit reference to BuildAt.
|
||||
// We could then test how the built binary responds when called by the latest
|
||||
// version of this library.
|
||||
package testhelpers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const packageBaseName = "github.com/containernetworking/cni"
|
||||
|
||||
func run(cmd *exec.Cmd) error {
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
command := strings.Join(cmd.Args, " ")
|
||||
return fmt.Errorf("running %q: %s", command, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func goBuildEnviron(gopath string) []string {
|
||||
environ := os.Environ()
|
||||
for i, kvp := range environ {
|
||||
if strings.HasPrefix(kvp, "GOPATH=") {
|
||||
environ[i] = "GOPATH=" + gopath
|
||||
return environ
|
||||
}
|
||||
}
|
||||
environ = append(environ, "GOPATH="+gopath)
|
||||
return environ
|
||||
}
|
||||
|
||||
func buildGoProgram(gopath, packageName, outputFilePath string) error {
|
||||
cmd := exec.Command("go", "build", "-o", outputFilePath, packageName)
|
||||
cmd.Env = goBuildEnviron(gopath)
|
||||
return run(cmd)
|
||||
}
|
||||
|
||||
func createSingleFilePackage(gopath, packageName string, fileContents []byte) error {
|
||||
dirName := filepath.Join(gopath, "src", packageName)
|
||||
err := os.MkdirAll(dirName, 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ioutil.WriteFile(filepath.Join(dirName, "main.go"), fileContents, 0600)
|
||||
}
|
||||
|
||||
func removePackage(gopath, packageName string) error {
|
||||
dirName := filepath.Join(gopath, "src", packageName)
|
||||
return os.RemoveAll(dirName)
|
||||
}
|
||||
|
||||
func isRepoRoot(path string) bool {
|
||||
_, err := ioutil.ReadDir(filepath.Join(path, ".git"))
|
||||
return (err == nil) && (filepath.Base(path) == "cni")
|
||||
}
|
||||
|
||||
func LocateCurrentGitRepo() (string, error) {
|
||||
dir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
if isRepoRoot(dir) {
|
||||
return dir, nil
|
||||
}
|
||||
|
||||
dir, err = filepath.Abs(filepath.Dir(dir))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("abs(dir(%q)): %s", dir, err)
|
||||
}
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("unable to find cni repo root, landed at %q", dir)
|
||||
}
|
||||
|
||||
func gitCloneThisRepo(cloneDestination string) error {
|
||||
err := os.MkdirAll(cloneDestination, 0700)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentGitRepo, err := LocateCurrentGitRepo()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return run(exec.Command("git", "clone", currentGitRepo, cloneDestination))
|
||||
}
|
||||
|
||||
func gitCheckout(localRepo string, gitRef string) error {
|
||||
return run(exec.Command("git", "-C", localRepo, "checkout", gitRef))
|
||||
}
|
||||
|
||||
// BuildAt builds the go programSource using the version of the CNI library
|
||||
// at gitRef, and saves the resulting binary file at outputFilePath
|
||||
func BuildAt(programSource []byte, gitRef string, outputFilePath string) error {
|
||||
tempGoPath, err := ioutil.TempDir("", "cni-git-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tempGoPath)
|
||||
|
||||
cloneDestination := filepath.Join(tempGoPath, "src", packageBaseName)
|
||||
err = gitCloneThisRepo(cloneDestination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = gitCheckout(cloneDestination, gitRef)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
testPackageName := fmt.Sprintf("test-package-%x", rand.Int31())
|
||||
|
||||
err = createSingleFilePackage(tempGoPath, testPackageName, programSource)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer removePackage(tempGoPath, testPackageName)
|
||||
|
||||
err = buildGoProgram(tempGoPath, testPackageName, outputFilePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
27
vendor/github.com/containernetworking/cni/pkg/version/testhelpers/testhelpers_suite_test.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/pkg/version/testhelpers/testhelpers_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package testhelpers_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTesthelpers(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Testhelpers Suite")
|
||||
}
|
106
vendor/github.com/containernetworking/cni/pkg/version/testhelpers/testhelpers_test.go
generated
vendored
Normal file
106
vendor/github.com/containernetworking/cni/pkg/version/testhelpers/testhelpers_test.go
generated
vendored
Normal file
|
@ -0,0 +1,106 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package testhelpers_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/version/testhelpers"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("BuildAt", func() {
|
||||
var (
|
||||
gitRef string
|
||||
outputFilePath string
|
||||
outputDir string
|
||||
programSource []byte
|
||||
)
|
||||
BeforeEach(func() {
|
||||
programSource = []byte(`package main
|
||||
|
||||
import "github.com/containernetworking/cni/pkg/skel"
|
||||
|
||||
func c(_ *skel.CmdArgs) error { return nil }
|
||||
|
||||
func main() { skel.PluginMain(c, c) }
|
||||
`)
|
||||
gitRef = "f4364185253"
|
||||
|
||||
var err error
|
||||
outputDir, err = ioutil.TempDir("", "bin")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
outputFilePath = filepath.Join(outputDir, "some-binary")
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(os.RemoveAll(outputDir)).To(Succeed())
|
||||
})
|
||||
|
||||
It("builds the provided source code using the CNI library at the given git ref", func() {
|
||||
Expect(outputFilePath).NotTo(BeAnExistingFile())
|
||||
|
||||
err := testhelpers.BuildAt(programSource, gitRef, outputFilePath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(outputFilePath).To(BeAnExistingFile())
|
||||
|
||||
cmd := exec.Command(outputFilePath)
|
||||
cmd.Env = []string{"CNI_COMMAND=VERSION"}
|
||||
output, err := cmd.CombinedOutput()
|
||||
Expect(err).To(BeAssignableToTypeOf(&exec.ExitError{}))
|
||||
Expect(output).To(ContainSubstring("unknown CNI_COMMAND: VERSION"))
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("LocateCurrentGitRepo", func() {
|
||||
It("returns the path to the root of the CNI git repo", func() {
|
||||
path, err := testhelpers.LocateCurrentGitRepo()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
AssertItIsTheCNIRepoRoot(path)
|
||||
})
|
||||
|
||||
Context("when run from a different directory", func() {
|
||||
BeforeEach(func() {
|
||||
os.Chdir("..")
|
||||
})
|
||||
|
||||
It("still finds the CNI repo root", func() {
|
||||
path, err := testhelpers.LocateCurrentGitRepo()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
AssertItIsTheCNIRepoRoot(path)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func AssertItIsTheCNIRepoRoot(path string) {
|
||||
Expect(path).To(BeADirectory())
|
||||
files, err := ioutil.ReadDir(path)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
names := []string{}
|
||||
for _, file := range files {
|
||||
names = append(names, file.Name())
|
||||
}
|
||||
|
||||
Expect(names).To(ContainElement("SPEC.md"))
|
||||
Expect(names).To(ContainElement("libcni"))
|
||||
Expect(names).To(ContainElement("cnitool"))
|
||||
}
|
27
vendor/github.com/containernetworking/cni/pkg/version/version_suite_test.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/pkg/version/version_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package version_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVersion(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Version Suite")
|
||||
}
|
157
vendor/github.com/containernetworking/cni/plugins/ipam/dhcp/daemon.go
generated
vendored
Normal file
157
vendor/github.com/containernetworking/cni/plugins/ipam/dhcp/daemon.go
generated
vendored
Normal file
|
@ -0,0 +1,157 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/rpc"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/coreos/go-systemd/activation"
|
||||
)
|
||||
|
||||
const listenFdsStart = 3
|
||||
const resendCount = 3
|
||||
|
||||
var errNoMoreTries = errors.New("no more tries")
|
||||
|
||||
type DHCP struct {
|
||||
mux sync.Mutex
|
||||
leases map[string]*DHCPLease
|
||||
}
|
||||
|
||||
func newDHCP() *DHCP {
|
||||
return &DHCP{
|
||||
leases: make(map[string]*DHCPLease),
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate acquires an IP from a DHCP server for a specified container.
|
||||
// The acquired lease will be maintained until Release() is called.
|
||||
func (d *DHCP) Allocate(args *skel.CmdArgs, result *types.Result) error {
|
||||
conf := types.NetConf{}
|
||||
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||
return fmt.Errorf("error parsing netconf: %v", err)
|
||||
}
|
||||
|
||||
clientID := args.ContainerID + "/" + conf.Name
|
||||
l, err := AcquireLease(clientID, args.Netns, args.IfName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ipn, err := l.IPNet()
|
||||
if err != nil {
|
||||
l.Stop()
|
||||
return err
|
||||
}
|
||||
|
||||
d.setLease(args.ContainerID, conf.Name, l)
|
||||
|
||||
result.IP4 = &types.IPConfig{
|
||||
IP: *ipn,
|
||||
Gateway: l.Gateway(),
|
||||
Routes: l.Routes(),
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Release stops maintenance of the lease acquired in Allocate()
|
||||
// and sends a release msg to the DHCP server.
|
||||
func (d *DHCP) Release(args *skel.CmdArgs, reply *struct{}) error {
|
||||
conf := types.NetConf{}
|
||||
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||
return fmt.Errorf("error parsing netconf: %v", err)
|
||||
}
|
||||
|
||||
if l := d.getLease(args.ContainerID, conf.Name); l != nil {
|
||||
l.Stop()
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("lease not found: %v/%v", args.ContainerID, conf.Name)
|
||||
}
|
||||
|
||||
func (d *DHCP) getLease(contID, netName string) *DHCPLease {
|
||||
d.mux.Lock()
|
||||
defer d.mux.Unlock()
|
||||
|
||||
// TODO(eyakubovich): hash it to avoid collisions
|
||||
l, ok := d.leases[contID+netName]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (d *DHCP) setLease(contID, netName string, l *DHCPLease) {
|
||||
d.mux.Lock()
|
||||
defer d.mux.Unlock()
|
||||
|
||||
// TODO(eyakubovich): hash it to avoid collisions
|
||||
d.leases[contID+netName] = l
|
||||
}
|
||||
|
||||
func getListener() (net.Listener, error) {
|
||||
l, err := activation.Listeners(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(l) == 0:
|
||||
if err := os.MkdirAll(filepath.Dir(socketPath), 0700); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return net.Listen("unix", socketPath)
|
||||
|
||||
case len(l) == 1:
|
||||
if l[0] == nil {
|
||||
return nil, fmt.Errorf("LISTEN_FDS=1 but no FD found")
|
||||
}
|
||||
return l[0], nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("Too many (%v) FDs passed through socket activation", len(l))
|
||||
}
|
||||
}
|
||||
|
||||
func runDaemon() {
|
||||
// since other goroutines (on separate threads) will change namespaces,
|
||||
// ensure the RPC server does not get scheduled onto those
|
||||
runtime.LockOSThread()
|
||||
|
||||
l, err := getListener()
|
||||
if err != nil {
|
||||
log.Printf("Error getting listener: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
dhcp := newDHCP()
|
||||
rpc.Register(dhcp)
|
||||
rpc.HandleHTTP()
|
||||
http.Serve(l, nil)
|
||||
}
|
337
vendor/github.com/containernetworking/cni/plugins/ipam/dhcp/lease.go
generated
vendored
Normal file
337
vendor/github.com/containernetworking/cni/plugins/ipam/dhcp/lease.go
generated
vendored
Normal file
|
@ -0,0 +1,337 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/d2g/dhcp4"
|
||||
"github.com/d2g/dhcp4client"
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/ns"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
|
||||
// RFC 2131 suggests using exponential backoff, starting with 4sec
|
||||
// and randomized to +/- 1sec
|
||||
const resendDelay0 = 4 * time.Second
|
||||
const resendDelayMax = 32 * time.Second
|
||||
|
||||
const (
|
||||
leaseStateBound = iota
|
||||
leaseStateRenewing
|
||||
leaseStateRebinding
|
||||
)
|
||||
|
||||
// This implementation uses 1 OS thread per lease. This is because
|
||||
// all the network operations have to be done in network namespace
|
||||
// of the interface. This can be improved by switching to the proper
|
||||
// namespace for network ops and using fewer threads. However, this
|
||||
// needs to be done carefully as dhcp4client ops are blocking.
|
||||
|
||||
type DHCPLease struct {
|
||||
clientID string
|
||||
ack *dhcp4.Packet
|
||||
opts dhcp4.Options
|
||||
link netlink.Link
|
||||
renewalTime time.Time
|
||||
rebindingTime time.Time
|
||||
expireTime time.Time
|
||||
stop chan struct{}
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
// AcquireLease gets an DHCP lease and then maintains it in the background
|
||||
// by periodically renewing it. The acquired lease can be released by
|
||||
// calling DHCPLease.Stop()
|
||||
func AcquireLease(clientID, netns, ifName string) (*DHCPLease, error) {
|
||||
errCh := make(chan error, 1)
|
||||
l := &DHCPLease{
|
||||
clientID: clientID,
|
||||
stop: make(chan struct{}),
|
||||
}
|
||||
|
||||
log.Printf("%v: acquiring lease", clientID)
|
||||
|
||||
l.wg.Add(1)
|
||||
go func() {
|
||||
errCh <- ns.WithNetNSPath(netns, func(_ ns.NetNS) error {
|
||||
defer l.wg.Done()
|
||||
|
||||
link, err := netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error looking up %q: %v", ifName, err)
|
||||
}
|
||||
|
||||
l.link = link
|
||||
|
||||
if err = l.acquire(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Printf("%v: lease acquired, expiration is %v", l.clientID, l.expireTime)
|
||||
|
||||
errCh <- nil
|
||||
|
||||
l.maintain()
|
||||
return nil
|
||||
})
|
||||
}()
|
||||
|
||||
if err := <-errCh; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// Stop terminates the background task that maintains the lease
|
||||
// and issues a DHCP Release
|
||||
func (l *DHCPLease) Stop() {
|
||||
close(l.stop)
|
||||
l.wg.Wait()
|
||||
}
|
||||
|
||||
func (l *DHCPLease) acquire() error {
|
||||
c, err := newDHCPClient(l.link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
if (l.link.Attrs().Flags & net.FlagUp) != net.FlagUp {
|
||||
log.Printf("Link %q down. Attempting to set up", l.link.Attrs().Name)
|
||||
if err = netlink.LinkSetUp(l.link); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pkt, err := backoffRetry(func() (*dhcp4.Packet, error) {
|
||||
ok, ack, err := c.Request()
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case !ok:
|
||||
return nil, fmt.Errorf("DHCP server NACK'd own offer")
|
||||
default:
|
||||
return &ack, nil
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return l.commit(pkt)
|
||||
}
|
||||
|
||||
func (l *DHCPLease) commit(ack *dhcp4.Packet) error {
|
||||
opts := ack.ParseOptions()
|
||||
|
||||
leaseTime, err := parseLeaseTime(opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rebindingTime, err := parseRebindingTime(opts)
|
||||
if err != nil || rebindingTime > leaseTime {
|
||||
// Per RFC 2131 Section 4.4.5, it should default to 85% of lease time
|
||||
rebindingTime = leaseTime * 85 / 100
|
||||
}
|
||||
|
||||
renewalTime, err := parseRenewalTime(opts)
|
||||
if err != nil || renewalTime > rebindingTime {
|
||||
// Per RFC 2131 Section 4.4.5, it should default to 50% of lease time
|
||||
renewalTime = leaseTime / 2
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
l.expireTime = now.Add(leaseTime)
|
||||
l.renewalTime = now.Add(renewalTime)
|
||||
l.rebindingTime = now.Add(rebindingTime)
|
||||
l.ack = ack
|
||||
l.opts = opts
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *DHCPLease) maintain() {
|
||||
state := leaseStateBound
|
||||
|
||||
for {
|
||||
var sleepDur time.Duration
|
||||
|
||||
switch state {
|
||||
case leaseStateBound:
|
||||
sleepDur = l.renewalTime.Sub(time.Now())
|
||||
if sleepDur <= 0 {
|
||||
log.Printf("%v: renewing lease", l.clientID)
|
||||
state = leaseStateRenewing
|
||||
continue
|
||||
}
|
||||
|
||||
case leaseStateRenewing:
|
||||
if err := l.renew(); err != nil {
|
||||
log.Printf("%v: %v", l.clientID, err)
|
||||
|
||||
if time.Now().After(l.rebindingTime) {
|
||||
log.Printf("%v: renawal time expired, rebinding", l.clientID)
|
||||
state = leaseStateRebinding
|
||||
}
|
||||
} else {
|
||||
log.Printf("%v: lease renewed, expiration is %v", l.clientID, l.expireTime)
|
||||
state = leaseStateBound
|
||||
}
|
||||
|
||||
case leaseStateRebinding:
|
||||
if err := l.acquire(); err != nil {
|
||||
log.Printf("%v: %v", l.clientID, err)
|
||||
|
||||
if time.Now().After(l.expireTime) {
|
||||
log.Printf("%v: lease expired, bringing interface DOWN", l.clientID)
|
||||
l.downIface()
|
||||
return
|
||||
}
|
||||
} else {
|
||||
log.Printf("%v: lease rebound, expiration is %v", l.clientID, l.expireTime)
|
||||
state = leaseStateBound
|
||||
}
|
||||
}
|
||||
|
||||
select {
|
||||
case <-time.After(sleepDur):
|
||||
|
||||
case <-l.stop:
|
||||
if err := l.release(); err != nil {
|
||||
log.Printf("%v: failed to release DHCP lease: %v", l.clientID, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *DHCPLease) downIface() {
|
||||
if err := netlink.LinkSetDown(l.link); err != nil {
|
||||
log.Printf("%v: failed to bring %v interface DOWN: %v", l.clientID, l.link.Attrs().Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *DHCPLease) renew() error {
|
||||
c, err := newDHCPClient(l.link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
pkt, err := backoffRetry(func() (*dhcp4.Packet, error) {
|
||||
ok, ack, err := c.Renew(*l.ack)
|
||||
switch {
|
||||
case err != nil:
|
||||
return nil, err
|
||||
case !ok:
|
||||
return nil, fmt.Errorf("DHCP server did not renew lease")
|
||||
default:
|
||||
return &ack, nil
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
l.commit(pkt)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *DHCPLease) release() error {
|
||||
log.Printf("%v: releasing lease", l.clientID)
|
||||
|
||||
c, err := newDHCPClient(l.link)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer c.Close()
|
||||
|
||||
if err = c.Release(*l.ack); err != nil {
|
||||
return fmt.Errorf("failed to send DHCPRELEASE")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *DHCPLease) IPNet() (*net.IPNet, error) {
|
||||
mask := parseSubnetMask(l.opts)
|
||||
if mask == nil {
|
||||
return nil, fmt.Errorf("DHCP option Subnet Mask not found in DHCPACK")
|
||||
}
|
||||
|
||||
return &net.IPNet{
|
||||
IP: l.ack.YIAddr(),
|
||||
Mask: mask,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *DHCPLease) Gateway() net.IP {
|
||||
return parseRouter(l.opts)
|
||||
}
|
||||
|
||||
func (l *DHCPLease) Routes() []types.Route {
|
||||
routes := parseRoutes(l.opts)
|
||||
return append(routes, parseCIDRRoutes(l.opts)...)
|
||||
}
|
||||
|
||||
// jitter returns a random value within [-span, span) range
|
||||
func jitter(span time.Duration) time.Duration {
|
||||
return time.Duration(float64(span) * (2.0*rand.Float64() - 1.0))
|
||||
}
|
||||
|
||||
func backoffRetry(f func() (*dhcp4.Packet, error)) (*dhcp4.Packet, error) {
|
||||
var baseDelay time.Duration = resendDelay0
|
||||
|
||||
for i := 0; i < resendCount; i++ {
|
||||
pkt, err := f()
|
||||
if err == nil {
|
||||
return pkt, nil
|
||||
}
|
||||
|
||||
log.Print(err)
|
||||
|
||||
time.Sleep(baseDelay + jitter(time.Second))
|
||||
|
||||
if baseDelay < resendDelayMax {
|
||||
baseDelay *= 2
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errNoMoreTries
|
||||
}
|
||||
|
||||
func newDHCPClient(link netlink.Link) (*dhcp4client.Client, error) {
|
||||
pktsock, err := dhcp4client.NewPacketSock(link.Attrs().Index)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dhcp4client.New(
|
||||
dhcp4client.HardwareAddr(link.Attrs().HardwareAddr),
|
||||
dhcp4client.Timeout(5*time.Second),
|
||||
dhcp4client.Broadcast(false),
|
||||
dhcp4client.Connection(pktsock),
|
||||
)
|
||||
}
|
74
vendor/github.com/containernetworking/cni/plugins/ipam/dhcp/main.go
generated
vendored
Normal file
74
vendor/github.com/containernetworking/cni/plugins/ipam/dhcp/main.go
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/rpc"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
)
|
||||
|
||||
const socketPath = "/run/cni/dhcp.sock"
|
||||
|
||||
func main() {
|
||||
if len(os.Args) > 1 && os.Args[1] == "daemon" {
|
||||
runDaemon()
|
||||
} else {
|
||||
skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
|
||||
}
|
||||
}
|
||||
|
||||
func cmdAdd(args *skel.CmdArgs) error {
|
||||
result := types.Result{}
|
||||
if err := rpcCall("DHCP.Allocate", args, &result); err != nil {
|
||||
return err
|
||||
}
|
||||
return result.Print()
|
||||
}
|
||||
|
||||
func cmdDel(args *skel.CmdArgs) error {
|
||||
result := struct{}{}
|
||||
if err := rpcCall("DHCP.Release", args, &result); err != nil {
|
||||
return fmt.Errorf("error dialing DHCP daemon: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func rpcCall(method string, args *skel.CmdArgs, result interface{}) error {
|
||||
client, err := rpc.DialHTTP("unix", socketPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error dialing DHCP daemon: %v", err)
|
||||
}
|
||||
|
||||
// The daemon may be running under a different working dir
|
||||
// so make sure the netns path is absolute.
|
||||
netns, err := filepath.Abs(args.Netns)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to make %q an absolute path: %v", args.Netns, err)
|
||||
}
|
||||
args.Netns = netns
|
||||
|
||||
err = client.Call(method, args, result)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error calling %v: %v", method, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
139
vendor/github.com/containernetworking/cni/plugins/ipam/dhcp/options.go
generated
vendored
Normal file
139
vendor/github.com/containernetworking/cni/plugins/ipam/dhcp/options.go
generated
vendored
Normal file
|
@ -0,0 +1,139 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/d2g/dhcp4"
|
||||
)
|
||||
|
||||
func parseRouter(opts dhcp4.Options) net.IP {
|
||||
if opts, ok := opts[dhcp4.OptionRouter]; ok {
|
||||
if len(opts) == 4 {
|
||||
return net.IP(opts)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func classfulSubnet(sn net.IP) net.IPNet {
|
||||
return net.IPNet{
|
||||
IP: sn,
|
||||
Mask: sn.DefaultMask(),
|
||||
}
|
||||
}
|
||||
|
||||
func parseRoutes(opts dhcp4.Options) []types.Route {
|
||||
// StaticRoutes format: pairs of:
|
||||
// Dest = 4 bytes; Classful IP subnet
|
||||
// Router = 4 bytes; IP address of router
|
||||
|
||||
routes := []types.Route{}
|
||||
if opt, ok := opts[dhcp4.OptionStaticRoute]; ok {
|
||||
for len(opt) >= 8 {
|
||||
sn := opt[0:4]
|
||||
r := opt[4:8]
|
||||
rt := types.Route{
|
||||
Dst: classfulSubnet(sn),
|
||||
GW: r,
|
||||
}
|
||||
routes = append(routes, rt)
|
||||
opt = opt[8:]
|
||||
}
|
||||
}
|
||||
|
||||
return routes
|
||||
}
|
||||
|
||||
func parseCIDRRoutes(opts dhcp4.Options) []types.Route {
|
||||
// See RFC4332 for format (http://tools.ietf.org/html/rfc3442)
|
||||
|
||||
routes := []types.Route{}
|
||||
if opt, ok := opts[dhcp4.OptionClasslessRouteFormat]; ok {
|
||||
for len(opt) >= 5 {
|
||||
width := int(opt[0])
|
||||
if width > 32 {
|
||||
// error: can't have more than /32
|
||||
return nil
|
||||
}
|
||||
// network bits are compacted to avoid zeros
|
||||
octets := 0
|
||||
if width > 0 {
|
||||
octets = (width-1)/8 + 1
|
||||
}
|
||||
|
||||
if len(opt) < 1+octets+4 {
|
||||
// error: too short
|
||||
return nil
|
||||
}
|
||||
|
||||
sn := make([]byte, 4)
|
||||
copy(sn, opt[1:octets+1])
|
||||
|
||||
gw := net.IP(opt[octets+1 : octets+5])
|
||||
|
||||
rt := types.Route{
|
||||
Dst: net.IPNet{
|
||||
IP: net.IP(sn),
|
||||
Mask: net.CIDRMask(width, 32),
|
||||
},
|
||||
GW: gw,
|
||||
}
|
||||
routes = append(routes, rt)
|
||||
|
||||
opt = opt[octets+5 : len(opt)]
|
||||
}
|
||||
}
|
||||
return routes
|
||||
}
|
||||
|
||||
func parseSubnetMask(opts dhcp4.Options) net.IPMask {
|
||||
mask, ok := opts[dhcp4.OptionSubnetMask]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
return net.IPMask(mask)
|
||||
}
|
||||
|
||||
func parseDuration(opts dhcp4.Options, code dhcp4.OptionCode, optName string) (time.Duration, error) {
|
||||
val, ok := opts[code]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("option %v not found", optName)
|
||||
}
|
||||
if len(val) != 4 {
|
||||
return 0, fmt.Errorf("option %v is not 4 bytes", optName)
|
||||
}
|
||||
|
||||
secs := binary.BigEndian.Uint32(val)
|
||||
return time.Duration(secs) * time.Second, nil
|
||||
}
|
||||
|
||||
func parseLeaseTime(opts dhcp4.Options) (time.Duration, error) {
|
||||
return parseDuration(opts, dhcp4.OptionIPAddressLeaseTime, "LeaseTime")
|
||||
}
|
||||
|
||||
func parseRenewalTime(opts dhcp4.Options) (time.Duration, error) {
|
||||
return parseDuration(opts, dhcp4.OptionRenewalTimeValue, "RenewalTime")
|
||||
}
|
||||
|
||||
func parseRebindingTime(opts dhcp4.Options) (time.Duration, error) {
|
||||
return parseDuration(opts, dhcp4.OptionRebindingTimeValue, "RebindingTime")
|
||||
}
|
75
vendor/github.com/containernetworking/cni/plugins/ipam/dhcp/options_test.go
generated
vendored
Normal file
75
vendor/github.com/containernetworking/cni/plugins/ipam/dhcp/options_test.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/d2g/dhcp4"
|
||||
)
|
||||
|
||||
func validateRoutes(t *testing.T, routes []types.Route) {
|
||||
expected := []types.Route{
|
||||
types.Route{
|
||||
Dst: net.IPNet{
|
||||
IP: net.IPv4(10, 0, 0, 0),
|
||||
Mask: net.CIDRMask(8, 32),
|
||||
},
|
||||
GW: net.IPv4(10, 1, 2, 3),
|
||||
},
|
||||
types.Route{
|
||||
Dst: net.IPNet{
|
||||
IP: net.IPv4(192, 168, 1, 0),
|
||||
Mask: net.CIDRMask(24, 32),
|
||||
},
|
||||
GW: net.IPv4(192, 168, 2, 3),
|
||||
},
|
||||
}
|
||||
|
||||
if len(routes) != len(expected) {
|
||||
t.Fatalf("wrong length slice; expected %v, got %v", len(expected), len(routes))
|
||||
}
|
||||
|
||||
for i := 0; i < len(routes); i++ {
|
||||
a := routes[i]
|
||||
e := expected[i]
|
||||
|
||||
if a.Dst.String() != e.Dst.String() {
|
||||
t.Errorf("route.Dst mismatch: expected %v, got %v", e.Dst, a.Dst)
|
||||
}
|
||||
|
||||
if !a.GW.Equal(e.GW) {
|
||||
t.Errorf("route.GW mismatch: expected %v, got %v", e.GW, a.GW)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseRoutes(t *testing.T) {
|
||||
opts := make(dhcp4.Options)
|
||||
opts[dhcp4.OptionStaticRoute] = []byte{10, 0, 0, 0, 10, 1, 2, 3, 192, 168, 1, 0, 192, 168, 2, 3}
|
||||
routes := parseRoutes(opts)
|
||||
|
||||
validateRoutes(t, routes)
|
||||
}
|
||||
|
||||
func TestParseCIDRRoutes(t *testing.T) {
|
||||
opts := make(dhcp4.Options)
|
||||
opts[dhcp4.OptionClasslessRouteFormat] = []byte{8, 10, 10, 1, 2, 3, 24, 192, 168, 1, 192, 168, 2, 3}
|
||||
routes := parseCIDRRoutes(opts)
|
||||
|
||||
validateRoutes(t, routes)
|
||||
}
|
88
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/README.md
generated
vendored
Normal file
88
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/README.md
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
# host-local IP address manager
|
||||
|
||||
host-local IPAM allocates IPv4 and IPv6 addresses out of a specified address range. Optionally,
|
||||
it can include a DNS configuration from a `resolv.conf` file on the host.
|
||||
|
||||
## Usage
|
||||
|
||||
### Obtain an IP
|
||||
|
||||
Given the following network configuration:
|
||||
|
||||
```
|
||||
{
|
||||
"name": "default",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "203.0.113.0/24"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Using the command line interface
|
||||
|
||||
```
|
||||
$ export CNI_COMMAND=ADD
|
||||
$ export CNI_CONTAINERID=f81d4fae-7dec-11d0-a765-00a0c91e6bf6
|
||||
$ ./host-local < $conf
|
||||
```
|
||||
|
||||
```
|
||||
{
|
||||
"ip4": {
|
||||
"ip": "203.0.113.1/24"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Backends
|
||||
|
||||
By default ipmanager stores IP allocations on the local filesystem using the IP address as the file name and the ID as contents. For example:
|
||||
|
||||
```
|
||||
$ ls /var/lib/cni/networks/default
|
||||
```
|
||||
```
|
||||
203.0.113.1 203.0.113.2
|
||||
```
|
||||
|
||||
```
|
||||
$ cat /var/lib/cni/networks/default/203.0.113.1
|
||||
```
|
||||
```
|
||||
f81d4fae-7dec-11d0-a765-00a0c91e6bf6
|
||||
```
|
||||
|
||||
## Configuration Files
|
||||
|
||||
|
||||
```
|
||||
{
|
||||
"name": "ipv6",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "3ffe:ffff:0:01ff::/64",
|
||||
"rangeStart": "3ffe:ffff:0:01ff::0010",
|
||||
"rangeEnd": "3ffe:ffff:0:01ff::0020",
|
||||
"routes": [
|
||||
{ "dst": "3ffe:ffff:0:01ff::1/64" }
|
||||
],
|
||||
"resolvConf": "/etc/resolv.conf"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
{
|
||||
"name": "ipv4",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "203.0.113.1/24",
|
||||
"rangeStart": "203.0.113.10",
|
||||
"rangeEnd": "203.0.113.20",
|
||||
"routes": [
|
||||
{ "dst": "203.0.113.0/24" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
271
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/allocator/allocator.go
generated
vendored
Normal file
271
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/allocator/allocator.go
generated
vendored
Normal file
|
@ -0,0 +1,271 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package allocator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/ip"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/plugins/ipam/host-local/backend"
|
||||
)
|
||||
|
||||
type IPAllocator struct {
|
||||
// start is inclusive and may be allocated
|
||||
start net.IP
|
||||
// end is inclusive and may be allocated
|
||||
end net.IP
|
||||
conf *IPAMConfig
|
||||
store backend.Store
|
||||
}
|
||||
|
||||
func NewIPAllocator(conf *IPAMConfig, store backend.Store) (*IPAllocator, error) {
|
||||
// Can't create an allocator for a network with no addresses, eg
|
||||
// a /32 or /31
|
||||
ones, masklen := conf.Subnet.Mask.Size()
|
||||
if ones > masklen-2 {
|
||||
return nil, fmt.Errorf("Network %v too small to allocate from", conf.Subnet)
|
||||
}
|
||||
|
||||
var (
|
||||
start net.IP
|
||||
end net.IP
|
||||
err error
|
||||
)
|
||||
start, end, err = networkRange((*net.IPNet)(&conf.Subnet))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// skip the .0 address
|
||||
start = ip.NextIP(start)
|
||||
|
||||
if conf.RangeStart != nil {
|
||||
if err := validateRangeIP(conf.RangeStart, (*net.IPNet)(&conf.Subnet), nil, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
start = conf.RangeStart
|
||||
}
|
||||
if conf.RangeEnd != nil {
|
||||
if err := validateRangeIP(conf.RangeEnd, (*net.IPNet)(&conf.Subnet), start, nil); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
end = conf.RangeEnd
|
||||
}
|
||||
return &IPAllocator{start, end, conf, store}, nil
|
||||
}
|
||||
|
||||
func canonicalizeIP(ip net.IP) (net.IP, error) {
|
||||
if ip.To4() != nil {
|
||||
return ip.To4(), nil
|
||||
} else if ip.To16() != nil {
|
||||
return ip.To16(), nil
|
||||
}
|
||||
return nil, fmt.Errorf("IP %s not v4 nor v6", ip)
|
||||
}
|
||||
|
||||
// Ensures @ip is within @ipnet, and (if given) inclusive of @start and @end
|
||||
func validateRangeIP(ip net.IP, ipnet *net.IPNet, start net.IP, end net.IP) error {
|
||||
var err error
|
||||
|
||||
// Make sure we can compare IPv4 addresses directly
|
||||
ip, err = canonicalizeIP(ip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !ipnet.Contains(ip) {
|
||||
return fmt.Errorf("%s not in network: %s", ip, ipnet)
|
||||
}
|
||||
|
||||
if start != nil {
|
||||
start, err = canonicalizeIP(start)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(ip) != len(start) {
|
||||
return fmt.Errorf("%s %d not same size IP address as start %s %d", ip, len(ip), start, len(start))
|
||||
}
|
||||
for i := 0; i < len(ip); i++ {
|
||||
if ip[i] > start[i] {
|
||||
break
|
||||
} else if ip[i] < start[i] {
|
||||
return fmt.Errorf("%s outside of network %s with start %s", ip, ipnet, start)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if end != nil {
|
||||
end, err = canonicalizeIP(end)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(ip) != len(end) {
|
||||
return fmt.Errorf("%s %d not same size IP address as end %s %d", ip, len(ip), end, len(end))
|
||||
}
|
||||
for i := 0; i < len(ip); i++ {
|
||||
if ip[i] < end[i] {
|
||||
break
|
||||
} else if ip[i] > end[i] {
|
||||
return fmt.Errorf("%s outside of network %s with end %s", ip, ipnet, end)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns newly allocated IP along with its config
|
||||
func (a *IPAllocator) Get(id string) (*types.IPConfig, error) {
|
||||
a.store.Lock()
|
||||
defer a.store.Unlock()
|
||||
|
||||
gw := a.conf.Gateway
|
||||
if gw == nil {
|
||||
gw = ip.NextIP(a.conf.Subnet.IP)
|
||||
}
|
||||
|
||||
var requestedIP net.IP
|
||||
if a.conf.Args != nil {
|
||||
requestedIP = a.conf.Args.IP
|
||||
}
|
||||
|
||||
if requestedIP != nil {
|
||||
if gw != nil && gw.Equal(a.conf.Args.IP) {
|
||||
return nil, fmt.Errorf("requested IP must differ gateway IP")
|
||||
}
|
||||
|
||||
subnet := net.IPNet{
|
||||
IP: a.conf.Subnet.IP,
|
||||
Mask: a.conf.Subnet.Mask,
|
||||
}
|
||||
err := validateRangeIP(requestedIP, &subnet, a.start, a.end)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reserved, err := a.store.Reserve(id, requestedIP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if reserved {
|
||||
return &types.IPConfig{
|
||||
IP: net.IPNet{IP: requestedIP, Mask: a.conf.Subnet.Mask},
|
||||
Gateway: gw,
|
||||
Routes: a.conf.Routes,
|
||||
}, nil
|
||||
}
|
||||
return nil, fmt.Errorf("requested IP address %q is not available in network: %s", requestedIP, a.conf.Name)
|
||||
}
|
||||
|
||||
startIP, endIP := a.getSearchRange()
|
||||
for cur := startIP; ; cur = a.nextIP(cur) {
|
||||
// don't allocate gateway IP
|
||||
if gw != nil && cur.Equal(gw) {
|
||||
continue
|
||||
}
|
||||
|
||||
reserved, err := a.store.Reserve(id, cur)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reserved {
|
||||
return &types.IPConfig{
|
||||
IP: net.IPNet{IP: cur, Mask: a.conf.Subnet.Mask},
|
||||
Gateway: gw,
|
||||
Routes: a.conf.Routes,
|
||||
}, nil
|
||||
}
|
||||
// break here to complete the loop
|
||||
if cur.Equal(endIP) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("no IP addresses available in network: %s", a.conf.Name)
|
||||
}
|
||||
|
||||
// Releases all IPs allocated for the container with given ID
|
||||
func (a *IPAllocator) Release(id string) error {
|
||||
a.store.Lock()
|
||||
defer a.store.Unlock()
|
||||
|
||||
return a.store.ReleaseByID(id)
|
||||
}
|
||||
|
||||
// Return the start and end IP addresses of a given subnet, excluding
|
||||
// the broadcast address (eg, 192.168.1.255)
|
||||
func networkRange(ipnet *net.IPNet) (net.IP, net.IP, error) {
|
||||
if ipnet.IP == nil {
|
||||
return nil, nil, fmt.Errorf("missing field %q in IPAM configuration", "subnet")
|
||||
}
|
||||
ip, err := canonicalizeIP(ipnet.IP)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("IP not v4 nor v6")
|
||||
}
|
||||
|
||||
if len(ip) != len(ipnet.Mask) {
|
||||
return nil, nil, fmt.Errorf("IPNet IP and Mask version mismatch")
|
||||
}
|
||||
|
||||
var end net.IP
|
||||
for i := 0; i < len(ip); i++ {
|
||||
end = append(end, ip[i]|^ipnet.Mask[i])
|
||||
}
|
||||
|
||||
// Exclude the broadcast address for IPv4
|
||||
if ip.To4() != nil {
|
||||
end[3]--
|
||||
}
|
||||
|
||||
return ipnet.IP, end, nil
|
||||
}
|
||||
|
||||
// nextIP returns the next ip of curIP within ipallocator's subnet
|
||||
func (a *IPAllocator) nextIP(curIP net.IP) net.IP {
|
||||
if curIP.Equal(a.end) {
|
||||
return a.start
|
||||
}
|
||||
return ip.NextIP(curIP)
|
||||
}
|
||||
|
||||
// getSearchRange returns the start and end ip based on the last reserved ip
|
||||
func (a *IPAllocator) getSearchRange() (net.IP, net.IP) {
|
||||
var startIP net.IP
|
||||
var endIP net.IP
|
||||
startFromLastReservedIP := false
|
||||
lastReservedIP, err := a.store.LastReservedIP()
|
||||
if err != nil {
|
||||
log.Printf("Error retriving last reserved ip: %v", err)
|
||||
} else if lastReservedIP != nil {
|
||||
subnet := net.IPNet{
|
||||
IP: a.conf.Subnet.IP,
|
||||
Mask: a.conf.Subnet.Mask,
|
||||
}
|
||||
err := validateRangeIP(lastReservedIP, &subnet, a.start, a.end)
|
||||
if err == nil {
|
||||
startFromLastReservedIP = true
|
||||
}
|
||||
}
|
||||
if startFromLastReservedIP {
|
||||
startIP = a.nextIP(lastReservedIP)
|
||||
endIP = lastReservedIP
|
||||
} else {
|
||||
startIP = a.start
|
||||
endIP = a.end
|
||||
}
|
||||
return startIP, endIP
|
||||
}
|
27
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/allocator/allocator_suite_test.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/allocator/allocator_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package allocator_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAllocator(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Allocator Suite")
|
||||
}
|
355
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/allocator/allocator_test.go
generated
vendored
Normal file
355
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/allocator/allocator_test.go
generated
vendored
Normal file
|
@ -0,0 +1,355 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package allocator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
fakestore "github.com/containernetworking/cni/plugins/ipam/host-local/backend/testing"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"net"
|
||||
)
|
||||
|
||||
type AllocatorTestCase struct {
|
||||
subnet string
|
||||
ipmap map[string]string
|
||||
expectResult string
|
||||
lastIP string
|
||||
}
|
||||
|
||||
func (t AllocatorTestCase) run() (*types.IPConfig, error) {
|
||||
subnet, err := types.ParseCIDR(t.subnet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
conf := IPAMConfig{
|
||||
Name: "test",
|
||||
Type: "host-local",
|
||||
Subnet: types.IPNet{IP: subnet.IP, Mask: subnet.Mask},
|
||||
}
|
||||
store := fakestore.NewFakeStore(t.ipmap, net.ParseIP(t.lastIP))
|
||||
alloc, err := NewIPAllocator(&conf, store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := alloc.Get("ID")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
var _ = Describe("host-local ip allocator", func() {
|
||||
Context("when has free ip", func() {
|
||||
It("should allocate ips in round robin", func() {
|
||||
testCases := []AllocatorTestCase{
|
||||
// fresh start
|
||||
{
|
||||
subnet: "10.0.0.0/29",
|
||||
ipmap: map[string]string{},
|
||||
expectResult: "10.0.0.2",
|
||||
lastIP: "",
|
||||
},
|
||||
{
|
||||
subnet: "10.0.0.0/30",
|
||||
ipmap: map[string]string{},
|
||||
expectResult: "10.0.0.2",
|
||||
lastIP: "",
|
||||
},
|
||||
{
|
||||
subnet: "10.0.0.0/29",
|
||||
ipmap: map[string]string{
|
||||
"10.0.0.2": "id",
|
||||
},
|
||||
expectResult: "10.0.0.3",
|
||||
lastIP: "",
|
||||
},
|
||||
// next ip of last reserved ip
|
||||
{
|
||||
subnet: "10.0.0.0/29",
|
||||
ipmap: map[string]string{},
|
||||
expectResult: "10.0.0.6",
|
||||
lastIP: "10.0.0.5",
|
||||
},
|
||||
{
|
||||
subnet: "10.0.0.0/29",
|
||||
ipmap: map[string]string{
|
||||
"10.0.0.4": "id",
|
||||
"10.0.0.5": "id",
|
||||
},
|
||||
expectResult: "10.0.0.6",
|
||||
lastIP: "10.0.0.3",
|
||||
},
|
||||
// round robin to the beginning
|
||||
{
|
||||
subnet: "10.0.0.0/29",
|
||||
ipmap: map[string]string{
|
||||
"10.0.0.6": "id",
|
||||
},
|
||||
expectResult: "10.0.0.2",
|
||||
lastIP: "10.0.0.5",
|
||||
},
|
||||
// lastIP is out of range
|
||||
{
|
||||
subnet: "10.0.0.0/29",
|
||||
ipmap: map[string]string{
|
||||
"10.0.0.2": "id",
|
||||
},
|
||||
expectResult: "10.0.0.3",
|
||||
lastIP: "10.0.0.128",
|
||||
},
|
||||
// wrap around and reserve lastIP
|
||||
{
|
||||
subnet: "10.0.0.0/29",
|
||||
ipmap: map[string]string{
|
||||
"10.0.0.2": "id",
|
||||
"10.0.0.4": "id",
|
||||
"10.0.0.5": "id",
|
||||
"10.0.0.6": "id",
|
||||
},
|
||||
expectResult: "10.0.0.3",
|
||||
lastIP: "10.0.0.3",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
res, err := tc.run()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.IP.IP.String()).To(Equal(tc.expectResult))
|
||||
}
|
||||
})
|
||||
|
||||
It("should not allocate the broadcast address", func() {
|
||||
subnet, err := types.ParseCIDR("192.168.1.0/24")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
conf := IPAMConfig{
|
||||
Name: "test",
|
||||
Type: "host-local",
|
||||
Subnet: types.IPNet{IP: subnet.IP, Mask: subnet.Mask},
|
||||
}
|
||||
store := fakestore.NewFakeStore(map[string]string{}, net.ParseIP(""))
|
||||
alloc, err := NewIPAllocator(&conf, store)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
for i := 1; i < 254; i++ {
|
||||
res, err := alloc.Get("ID")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// i+1 because the gateway address is skipped
|
||||
s := fmt.Sprintf("192.168.1.%d/24", i+1)
|
||||
Expect(s).To(Equal(res.IP.String()))
|
||||
}
|
||||
|
||||
_, err = alloc.Get("ID")
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should allocate RangeStart first", func() {
|
||||
subnet, err := types.ParseCIDR("192.168.1.0/24")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
conf := IPAMConfig{
|
||||
Name: "test",
|
||||
Type: "host-local",
|
||||
Subnet: types.IPNet{IP: subnet.IP, Mask: subnet.Mask},
|
||||
RangeStart: net.ParseIP("192.168.1.10"),
|
||||
}
|
||||
store := fakestore.NewFakeStore(map[string]string{}, net.ParseIP(""))
|
||||
alloc, err := NewIPAllocator(&conf, store)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
res, err := alloc.Get("ID")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.IP.String()).To(Equal("192.168.1.10/24"))
|
||||
|
||||
res, err = alloc.Get("ID")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.IP.String()).To(Equal("192.168.1.11/24"))
|
||||
})
|
||||
|
||||
It("should allocate RangeEnd but not past RangeEnd", func() {
|
||||
subnet, err := types.ParseCIDR("192.168.1.0/24")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
conf := IPAMConfig{
|
||||
Name: "test",
|
||||
Type: "host-local",
|
||||
Subnet: types.IPNet{IP: subnet.IP, Mask: subnet.Mask},
|
||||
RangeEnd: net.ParseIP("192.168.1.5"),
|
||||
}
|
||||
store := fakestore.NewFakeStore(map[string]string{}, net.ParseIP(""))
|
||||
alloc, err := NewIPAllocator(&conf, store)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
for i := 1; i < 5; i++ {
|
||||
res, err := alloc.Get("ID")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
// i+1 because the gateway address is skipped
|
||||
Expect(res.IP.String()).To(Equal(fmt.Sprintf("192.168.1.%d/24", i+1)))
|
||||
}
|
||||
|
||||
_, err = alloc.Get("ID")
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
Context("when requesting a specific IP", func() {
|
||||
It("must allocate the requested IP", func() {
|
||||
subnet, err := types.ParseCIDR("10.0.0.0/29")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
requestedIP := net.ParseIP("10.0.0.2")
|
||||
ipmap := map[string]string{}
|
||||
conf := IPAMConfig{
|
||||
Name: "test",
|
||||
Type: "host-local",
|
||||
Subnet: types.IPNet{IP: subnet.IP, Mask: subnet.Mask},
|
||||
Args: &IPAMArgs{IP: requestedIP},
|
||||
}
|
||||
store := fakestore.NewFakeStore(ipmap, nil)
|
||||
alloc, _ := NewIPAllocator(&conf, store)
|
||||
res, err := alloc.Get("ID")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(res.IP.IP.String()).To(Equal(requestedIP.String()))
|
||||
})
|
||||
|
||||
It("must return an error when the requested IP is after RangeEnd", func() {
|
||||
subnet, err := types.ParseCIDR("192.168.1.0/24")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
ipmap := map[string]string{}
|
||||
conf := IPAMConfig{
|
||||
Name: "test",
|
||||
Type: "host-local",
|
||||
Subnet: types.IPNet{IP: subnet.IP, Mask: subnet.Mask},
|
||||
Args: &IPAMArgs{IP: net.ParseIP("192.168.1.50")},
|
||||
RangeEnd: net.ParseIP("192.168.1.20"),
|
||||
}
|
||||
store := fakestore.NewFakeStore(ipmap, nil)
|
||||
alloc, _ := NewIPAllocator(&conf, store)
|
||||
_, err = alloc.Get("ID")
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("must return an error when the requested IP is before RangeStart", func() {
|
||||
subnet, err := types.ParseCIDR("192.168.1.0/24")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
ipmap := map[string]string{}
|
||||
conf := IPAMConfig{
|
||||
Name: "test",
|
||||
Type: "host-local",
|
||||
Subnet: types.IPNet{IP: subnet.IP, Mask: subnet.Mask},
|
||||
Args: &IPAMArgs{IP: net.ParseIP("192.168.1.3")},
|
||||
RangeStart: net.ParseIP("192.168.1.10"),
|
||||
}
|
||||
store := fakestore.NewFakeStore(ipmap, nil)
|
||||
alloc, _ := NewIPAllocator(&conf, store)
|
||||
_, err = alloc.Get("ID")
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
It("RangeStart must be in the given subnet", func() {
|
||||
subnet, err := types.ParseCIDR("192.168.1.0/24")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
conf := IPAMConfig{
|
||||
Name: "test",
|
||||
Type: "host-local",
|
||||
Subnet: types.IPNet{IP: subnet.IP, Mask: subnet.Mask},
|
||||
RangeStart: net.ParseIP("10.0.0.1"),
|
||||
}
|
||||
store := fakestore.NewFakeStore(map[string]string{}, net.ParseIP(""))
|
||||
_, err = NewIPAllocator(&conf, store)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("RangeEnd must be in the given subnet", func() {
|
||||
subnet, err := types.ParseCIDR("192.168.1.0/24")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
conf := IPAMConfig{
|
||||
Name: "test",
|
||||
Type: "host-local",
|
||||
Subnet: types.IPNet{IP: subnet.IP, Mask: subnet.Mask},
|
||||
RangeEnd: net.ParseIP("10.0.0.1"),
|
||||
}
|
||||
store := fakestore.NewFakeStore(map[string]string{}, net.ParseIP(""))
|
||||
_, err = NewIPAllocator(&conf, store)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("RangeEnd must be after RangeStart in the given subnet", func() {
|
||||
subnet, err := types.ParseCIDR("192.168.1.0/24")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
conf := IPAMConfig{
|
||||
Name: "test",
|
||||
Type: "host-local",
|
||||
Subnet: types.IPNet{IP: subnet.IP, Mask: subnet.Mask},
|
||||
RangeStart: net.ParseIP("192.168.1.10"),
|
||||
RangeEnd: net.ParseIP("192.168.1.3"),
|
||||
}
|
||||
store := fakestore.NewFakeStore(map[string]string{}, net.ParseIP(""))
|
||||
_, err = NewIPAllocator(&conf, store)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
|
||||
Context("when out of ips", func() {
|
||||
It("returns a meaningful error", func() {
|
||||
testCases := []AllocatorTestCase{
|
||||
{
|
||||
subnet: "10.0.0.0/30",
|
||||
ipmap: map[string]string{
|
||||
"10.0.0.2": "id",
|
||||
"10.0.0.3": "id",
|
||||
},
|
||||
},
|
||||
{
|
||||
subnet: "10.0.0.0/29",
|
||||
ipmap: map[string]string{
|
||||
"10.0.0.2": "id",
|
||||
"10.0.0.3": "id",
|
||||
"10.0.0.4": "id",
|
||||
"10.0.0.5": "id",
|
||||
"10.0.0.6": "id",
|
||||
"10.0.0.7": "id",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
_, err := tc.run()
|
||||
Expect(err).To(MatchError("no IP addresses available in network: test"))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Context("when given an invalid subnet", func() {
|
||||
It("returns a meaningful error", func() {
|
||||
subnet, err := types.ParseCIDR("192.168.1.0/31")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
conf := IPAMConfig{
|
||||
Name: "test",
|
||||
Type: "host-local",
|
||||
Subnet: types.IPNet{IP: subnet.IP, Mask: subnet.Mask},
|
||||
}
|
||||
store := fakestore.NewFakeStore(map[string]string{}, net.ParseIP(""))
|
||||
_, err = NewIPAllocator(&conf, store)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
72
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/allocator/config.go
generated
vendored
Normal file
72
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/allocator/config.go
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package allocator
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
|
||||
// IPAMConfig represents the IP related network configuration.
|
||||
type IPAMConfig struct {
|
||||
Name string
|
||||
Type string `json:"type"`
|
||||
RangeStart net.IP `json:"rangeStart"`
|
||||
RangeEnd net.IP `json:"rangeEnd"`
|
||||
Subnet types.IPNet `json:"subnet"`
|
||||
Gateway net.IP `json:"gateway"`
|
||||
Routes []types.Route `json:"routes"`
|
||||
DataDir string `json:"dataDir"`
|
||||
ResolvConf string `json:"resolvConf"`
|
||||
Args *IPAMArgs `json:"-"`
|
||||
}
|
||||
|
||||
type IPAMArgs struct {
|
||||
types.CommonArgs
|
||||
IP net.IP `json:"ip,omitempty"`
|
||||
}
|
||||
|
||||
type Net struct {
|
||||
Name string `json:"name"`
|
||||
IPAM *IPAMConfig `json:"ipam"`
|
||||
}
|
||||
|
||||
// NewIPAMConfig creates a NetworkConfig from the given network name.
|
||||
func LoadIPAMConfig(bytes []byte, args string) (*IPAMConfig, error) {
|
||||
n := Net{}
|
||||
if err := json.Unmarshal(bytes, &n); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if args != "" {
|
||||
n.IPAM.Args = &IPAMArgs{}
|
||||
err := types.LoadArgs(args, n.IPAM.Args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if n.IPAM == nil {
|
||||
return nil, fmt.Errorf("IPAM config missing 'ipam' key")
|
||||
}
|
||||
|
||||
// Copy net name into IPAM so not to drag Net struct around
|
||||
n.IPAM.Name = n.Name
|
||||
|
||||
return n.IPAM, nil
|
||||
}
|
116
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/disk/backend.go
generated
vendored
Normal file
116
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/disk/backend.go
generated
vendored
Normal file
|
@ -0,0 +1,116 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/cni/plugins/ipam/host-local/backend"
|
||||
)
|
||||
|
||||
const lastIPFile = "last_reserved_ip"
|
||||
|
||||
var defaultDataDir = "/var/lib/cni/networks"
|
||||
|
||||
type Store struct {
|
||||
FileLock
|
||||
dataDir string
|
||||
}
|
||||
|
||||
// Store implements the Store interface
|
||||
var _ backend.Store = &Store{}
|
||||
|
||||
func New(network, dataDir string) (*Store, error) {
|
||||
if dataDir == "" {
|
||||
dataDir = defaultDataDir
|
||||
}
|
||||
dir := filepath.Join(dataDir, network)
|
||||
if err := os.MkdirAll(dir, 0644); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
lk, err := NewFileLock(dir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &Store{*lk, dir}, nil
|
||||
}
|
||||
|
||||
func (s *Store) Reserve(id string, ip net.IP) (bool, error) {
|
||||
fname := filepath.Join(s.dataDir, ip.String())
|
||||
f, err := os.OpenFile(fname, os.O_RDWR|os.O_EXCL|os.O_CREATE, 0644)
|
||||
if os.IsExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if _, err := f.WriteString(strings.TrimSpace(id)); err != nil {
|
||||
f.Close()
|
||||
os.Remove(f.Name())
|
||||
return false, err
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
os.Remove(f.Name())
|
||||
return false, err
|
||||
}
|
||||
// store the reserved ip in lastIPFile
|
||||
ipfile := filepath.Join(s.dataDir, lastIPFile)
|
||||
err = ioutil.WriteFile(ipfile, []byte(ip.String()), 0644)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// LastReservedIP returns the last reserved IP if exists
|
||||
func (s *Store) LastReservedIP() (net.IP, error) {
|
||||
ipfile := filepath.Join(s.dataDir, lastIPFile)
|
||||
data, err := ioutil.ReadFile(ipfile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to retrieve last reserved ip: %v", err)
|
||||
}
|
||||
return net.ParseIP(string(data)), nil
|
||||
}
|
||||
|
||||
func (s *Store) Release(ip net.IP) error {
|
||||
return os.Remove(filepath.Join(s.dataDir, ip.String()))
|
||||
}
|
||||
|
||||
// N.B. This function eats errors to be tolerant and
|
||||
// release as much as possible
|
||||
func (s *Store) ReleaseByID(id string) error {
|
||||
err := filepath.Walk(s.dataDir, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil || info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if strings.TrimSpace(string(data)) == strings.TrimSpace(id) {
|
||||
if err := os.Remove(path); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
50
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/disk/lock.go
generated
vendored
Normal file
50
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/disk/lock.go
generated
vendored
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// FileLock wraps os.File to be used as a lock using flock
|
||||
type FileLock struct {
|
||||
f *os.File
|
||||
}
|
||||
|
||||
// NewFileLock opens file/dir at path and returns unlocked FileLock object
|
||||
func NewFileLock(path string) (*FileLock, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &FileLock{f}, nil
|
||||
}
|
||||
|
||||
// Close closes underlying file
|
||||
func (l *FileLock) Close() error {
|
||||
return l.f.Close()
|
||||
}
|
||||
|
||||
// Lock acquires an exclusive lock
|
||||
func (l *FileLock) Lock() error {
|
||||
return syscall.Flock(int(l.f.Fd()), syscall.LOCK_EX)
|
||||
}
|
||||
|
||||
// Unlock releases the lock
|
||||
func (l *FileLock) Unlock() error {
|
||||
return syscall.Flock(int(l.f.Fd()), syscall.LOCK_UN)
|
||||
}
|
27
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/store.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/store.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package backend
|
||||
|
||||
import "net"
|
||||
|
||||
type Store interface {
|
||||
Lock() error
|
||||
Unlock() error
|
||||
Close() error
|
||||
Reserve(id string, ip net.IP) (bool, error)
|
||||
LastReservedIP() (net.IP, error)
|
||||
Release(ip net.IP) error
|
||||
ReleaseByID(id string) error
|
||||
}
|
77
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/testing/fake_store.go
generated
vendored
Normal file
77
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/backend/testing/fake_store.go
generated
vendored
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package testing
|
||||
|
||||
import (
|
||||
"net"
|
||||
|
||||
"github.com/containernetworking/cni/plugins/ipam/host-local/backend"
|
||||
)
|
||||
|
||||
type FakeStore struct {
|
||||
ipMap map[string]string
|
||||
lastReservedIP net.IP
|
||||
}
|
||||
|
||||
// FakeStore implements the Store interface
|
||||
var _ backend.Store = &FakeStore{}
|
||||
|
||||
func NewFakeStore(ipmap map[string]string, lastIP net.IP) *FakeStore {
|
||||
return &FakeStore{ipmap, lastIP}
|
||||
}
|
||||
|
||||
func (s *FakeStore) Lock() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FakeStore) Unlock() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FakeStore) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FakeStore) Reserve(id string, ip net.IP) (bool, error) {
|
||||
key := ip.String()
|
||||
if _, ok := s.ipMap[key]; !ok {
|
||||
s.ipMap[key] = id
|
||||
s.lastReservedIP = ip
|
||||
return true, nil
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (s *FakeStore) LastReservedIP() (net.IP, error) {
|
||||
return s.lastReservedIP, nil
|
||||
}
|
||||
|
||||
func (s *FakeStore) Release(ip net.IP) error {
|
||||
delete(s.ipMap, ip.String())
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *FakeStore) ReleaseByID(id string) error {
|
||||
toDelete := []string{}
|
||||
for k, v := range s.ipMap {
|
||||
if v == id {
|
||||
toDelete = append(toDelete, k)
|
||||
}
|
||||
}
|
||||
for _, ip := range toDelete {
|
||||
delete(s.ipMap, ip)
|
||||
}
|
||||
return nil
|
||||
}
|
64
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/dns.go
generated
vendored
Normal file
64
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/dns.go
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
)
|
||||
|
||||
// parseResolvConf parses an existing resolv.conf in to a DNS struct
|
||||
func parseResolvConf(filename string) (*types.DNS, error) {
|
||||
fp, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dns := types.DNS{}
|
||||
scanner := bufio.NewScanner(fp)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
line = strings.TrimSpace(line)
|
||||
|
||||
// Skip comments, empty lines
|
||||
if len(line) == 0 || line[0] == '#' || line[0] == ';' {
|
||||
continue
|
||||
}
|
||||
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) < 2 {
|
||||
continue
|
||||
}
|
||||
switch fields[0] {
|
||||
case "nameserver":
|
||||
dns.Nameservers = append(dns.Nameservers, fields[1])
|
||||
case "domain":
|
||||
dns.Domain = fields[1]
|
||||
case "search":
|
||||
dns.Search = append(dns.Search, fields[1:]...)
|
||||
case "options":
|
||||
dns.Options = append(dns.Options, fields[1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &dns, nil
|
||||
}
|
80
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/dns_test.go
generated
vendored
Normal file
80
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/dns_test.go
generated
vendored
Normal file
|
@ -0,0 +1,80 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("parsing resolv.conf", func() {
|
||||
It("parses a simple resolv.conf file", func() {
|
||||
contents := `
|
||||
nameserver 192.0.2.0
|
||||
nameserver 192.0.2.1
|
||||
`
|
||||
dns, err := parse(contents)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(*dns).Should(Equal(types.DNS{Nameservers: []string{"192.0.2.0", "192.0.2.1"}}))
|
||||
})
|
||||
It("ignores comments", func() {
|
||||
dns, err := parse(`
|
||||
nameserver 192.0.2.0
|
||||
;nameserver 192.0.2.1
|
||||
`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(*dns).Should(Equal(types.DNS{Nameservers: []string{"192.0.2.0"}}))
|
||||
})
|
||||
It("parses all fields", func() {
|
||||
dns, err := parse(`
|
||||
nameserver 192.0.2.0
|
||||
nameserver 192.0.2.2
|
||||
domain example.com
|
||||
;nameserver comment
|
||||
#nameserver comment
|
||||
search example.net example.org
|
||||
search example.gov
|
||||
options one two three
|
||||
options four
|
||||
`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(*dns).Should(Equal(types.DNS{
|
||||
Nameservers: []string{"192.0.2.0", "192.0.2.2"},
|
||||
Domain: "example.com",
|
||||
Search: []string{"example.net", "example.org", "example.gov"},
|
||||
Options: []string{"one", "two", "three", "four"},
|
||||
}))
|
||||
})
|
||||
})
|
||||
|
||||
func parse(contents string) (*types.DNS, error) {
|
||||
f, err := ioutil.TempFile("", "host_local_resolv")
|
||||
defer f.Close()
|
||||
defer os.Remove(f.Name())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if _, err := f.WriteString(contents); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return parseResolvConf(f.Name())
|
||||
}
|
146
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/host_local_test.go
generated
vendored
Normal file
146
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/host_local_test.go
generated
vendored
Normal file
|
@ -0,0 +1,146 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/testutils"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("host-local Operations", func() {
|
||||
It("allocates and releases an address with ADD/DEL", func() {
|
||||
const ifname string = "eth0"
|
||||
const nspath string = "/some/where"
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "host_local_artifacts")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
err = ioutil.WriteFile(filepath.Join(tmpDir, "resolv.conf"), []byte("nameserver 192.0.2.3"), 0644)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
conf := fmt.Sprintf(`{
|
||||
"cniVersion": "0.2.0",
|
||||
"name": "mynet",
|
||||
"type": "ipvlan",
|
||||
"master": "foo0",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24",
|
||||
"dataDir": "%s",
|
||||
"resolvConf": "%s/resolv.conf"
|
||||
}
|
||||
}`, tmpDir, tmpDir)
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: nspath,
|
||||
IfName: ifname,
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
|
||||
// Allocate the IP
|
||||
result, err := testutils.CmdAddWithResult(nspath, ifname, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
expectedAddress, err := types.ParseCIDR("10.1.2.2/24")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
expectedAddress.IP = expectedAddress.IP.To16()
|
||||
Expect(result.IP4.IP).To(Equal(*expectedAddress))
|
||||
|
||||
Expect(result.IP4.Gateway).To(Equal(net.ParseIP("10.1.2.1")))
|
||||
|
||||
ipFilePath := filepath.Join(tmpDir, "mynet", "10.1.2.2")
|
||||
contents, err := ioutil.ReadFile(ipFilePath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(string(contents)).To(Equal("dummy"))
|
||||
|
||||
lastFilePath := filepath.Join(tmpDir, "mynet", "last_reserved_ip")
|
||||
contents, err = ioutil.ReadFile(lastFilePath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(string(contents)).To(Equal("10.1.2.2"))
|
||||
|
||||
Expect(result.DNS).To(Equal(types.DNS{Nameservers: []string{"192.0.2.3"}}))
|
||||
|
||||
// Release the IP
|
||||
err = testutils.CmdDelWithResult(nspath, ifname, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, err = os.Stat(ipFilePath)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("ignores whitespace in disk files", func() {
|
||||
const ifname string = "eth0"
|
||||
const nspath string = "/some/where"
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "host_local_artifacts")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
conf := fmt.Sprintf(`{
|
||||
"cniVersion": "0.2.0",
|
||||
"name": "mynet",
|
||||
"type": "ipvlan",
|
||||
"master": "foo0",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24",
|
||||
"dataDir": "%s"
|
||||
}
|
||||
}`, tmpDir)
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: " dummy\n ",
|
||||
Netns: nspath,
|
||||
IfName: ifname,
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
|
||||
// Allocate the IP
|
||||
result, err := testutils.CmdAddWithResult(nspath, ifname, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
ipFilePath := filepath.Join(tmpDir, "mynet", result.IP4.IP.IP.String())
|
||||
contents, err := ioutil.ReadFile(ipFilePath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(string(contents)).To(Equal("dummy"))
|
||||
|
||||
// Release the IP
|
||||
err = testutils.CmdDelWithResult(nspath, ifname, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
_, err = os.Stat(ipFilePath)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
})
|
84
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/main.go
generated
vendored
Normal file
84
vendor/github.com/containernetworking/cni/plugins/ipam/host-local/main.go
generated
vendored
Normal file
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/containernetworking/cni/plugins/ipam/host-local/backend/allocator"
|
||||
"github.com/containernetworking/cni/plugins/ipam/host-local/backend/disk"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
)
|
||||
|
||||
func main() {
|
||||
skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
|
||||
}
|
||||
|
||||
func cmdAdd(args *skel.CmdArgs) error {
|
||||
ipamConf, err := allocator.LoadIPAMConfig(args.StdinData, args.Args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := types.Result{}
|
||||
|
||||
if ipamConf.ResolvConf != "" {
|
||||
dns, err := parseResolvConf(ipamConf.ResolvConf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.DNS = *dns
|
||||
}
|
||||
|
||||
store, err := disk.New(ipamConf.Name, ipamConf.DataDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer store.Close()
|
||||
|
||||
allocator, err := allocator.NewIPAllocator(ipamConf, store)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ipConf, err := allocator.Get(args.ContainerID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
r.IP4 = ipConf
|
||||
|
||||
return r.Print()
|
||||
}
|
||||
|
||||
func cmdDel(args *skel.CmdArgs) error {
|
||||
ipamConf, err := allocator.LoadIPAMConfig(args.StdinData, args.Args)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
store, err := disk.New(ipamConf.Name, ipamConf.DataDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer store.Close()
|
||||
|
||||
ipAllocator, err := allocator.NewIPAllocator(ipamConf, store)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ipAllocator.Release(args.ContainerID)
|
||||
}
|
359
vendor/github.com/containernetworking/cni/plugins/main/bridge/bridge.go
generated
vendored
Normal file
359
vendor/github.com/containernetworking/cni/plugins/main/bridge/bridge.go
generated
vendored
Normal file
|
@ -0,0 +1,359 @@
|
|||
// Copyright 2014 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"runtime"
|
||||
"syscall"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/ip"
|
||||
"github.com/containernetworking/cni/pkg/ipam"
|
||||
"github.com/containernetworking/cni/pkg/ns"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/utils"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
const defaultBrName = "cni0"
|
||||
|
||||
type NetConf struct {
|
||||
types.NetConf
|
||||
BrName string `json:"bridge"`
|
||||
IsGW bool `json:"isGateway"`
|
||||
IsDefaultGW bool `json:"isDefaultGateway"`
|
||||
ForceAddress bool `json:"forceAddress"`
|
||||
IPMasq bool `json:"ipMasq"`
|
||||
MTU int `json:"mtu"`
|
||||
HairpinMode bool `json:"hairpinMode"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
// this ensures that main runs only on main thread (thread group leader).
|
||||
// since namespace ops (unshare, setns) are done for a single thread, we
|
||||
// must ensure that the goroutine does not jump from OS thread to thread
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
func loadNetConf(bytes []byte) (*NetConf, error) {
|
||||
n := &NetConf{
|
||||
BrName: defaultBrName,
|
||||
}
|
||||
if err := json.Unmarshal(bytes, n); err != nil {
|
||||
return nil, fmt.Errorf("failed to load netconf: %v", err)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func ensureBridgeAddr(br *netlink.Bridge, ipn *net.IPNet, forceAddress bool) error {
|
||||
addrs, err := netlink.AddrList(br, syscall.AF_INET)
|
||||
if err != nil && err != syscall.ENOENT {
|
||||
return fmt.Errorf("could not get list of IP addresses: %v", err)
|
||||
}
|
||||
|
||||
// if there're no addresses on the bridge, it's ok -- we'll add one
|
||||
if len(addrs) > 0 {
|
||||
ipnStr := ipn.String()
|
||||
for _, a := range addrs {
|
||||
// string comp is actually easiest for doing IPNet comps
|
||||
if a.IPNet.String() == ipnStr {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If forceAddress is set to true then reconfigure IP address otherwise throw error
|
||||
if forceAddress {
|
||||
if err = deleteBridgeAddr(br, a.IPNet); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
return fmt.Errorf("%q already has an IP address different from %v", br.Name, ipn.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addr := &netlink.Addr{IPNet: ipn, Label: ""}
|
||||
if err := netlink.AddrAdd(br, addr); err != nil {
|
||||
return fmt.Errorf("could not add IP address to %q: %v", br.Name, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteBridgeAddr(br *netlink.Bridge, ipn *net.IPNet) error {
|
||||
addr := &netlink.Addr{IPNet: ipn, Label: ""}
|
||||
|
||||
if err := netlink.LinkSetDown(br); err != nil {
|
||||
return fmt.Errorf("could not set down bridge %q: %v", br.Name, err)
|
||||
}
|
||||
|
||||
if err := netlink.AddrDel(br, addr); err != nil {
|
||||
return fmt.Errorf("could not remove IP address from %q: %v", br.Name, err)
|
||||
}
|
||||
|
||||
if err := netlink.LinkSetUp(br); err != nil {
|
||||
return fmt.Errorf("could not set up bridge %q: %v", br.Name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func bridgeByName(name string) (*netlink.Bridge, error) {
|
||||
l, err := netlink.LinkByName(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not lookup %q: %v", name, err)
|
||||
}
|
||||
br, ok := l.(*netlink.Bridge)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%q already exists but is not a bridge", name)
|
||||
}
|
||||
return br, nil
|
||||
}
|
||||
|
||||
func ensureBridge(brName string, mtu int) (*netlink.Bridge, error) {
|
||||
br := &netlink.Bridge{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: brName,
|
||||
MTU: mtu,
|
||||
// Let kernel use default txqueuelen; leaving it unset
|
||||
// means 0, and a zero-length TX queue messes up FIFO
|
||||
// traffic shapers which use TX queue length as the
|
||||
// default packet limit
|
||||
TxQLen: -1,
|
||||
},
|
||||
}
|
||||
|
||||
if err := netlink.LinkAdd(br); err != nil {
|
||||
if err != syscall.EEXIST {
|
||||
return nil, fmt.Errorf("could not add %q: %v", brName, err)
|
||||
}
|
||||
|
||||
// it's ok if the device already exists as long as config is similar
|
||||
br, err = bridgeByName(brName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := netlink.LinkSetUp(br); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return br, nil
|
||||
}
|
||||
|
||||
func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairpinMode bool) error {
|
||||
var hostVethName string
|
||||
|
||||
err := netns.Do(func(hostNS ns.NetNS) error {
|
||||
// create the veth pair in the container and move host end into host netns
|
||||
hostVeth, _, err := ip.SetupVeth(ifName, mtu, hostNS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostVethName = hostVeth.Attrs().Name
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// need to lookup hostVeth again as its index has changed during ns move
|
||||
hostVeth, err := netlink.LinkByName(hostVethName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup %q: %v", hostVethName, err)
|
||||
}
|
||||
|
||||
// connect host veth end to the bridge
|
||||
if err = netlink.LinkSetMaster(hostVeth, br); err != nil {
|
||||
return fmt.Errorf("failed to connect %q to bridge %v: %v", hostVethName, br.Attrs().Name, err)
|
||||
}
|
||||
|
||||
// set hairpin mode
|
||||
if err = netlink.LinkSetHairpin(hostVeth, hairpinMode); err != nil {
|
||||
return fmt.Errorf("failed to setup hairpin mode for %v: %v", hostVethName, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func calcGatewayIP(ipn *net.IPNet) net.IP {
|
||||
nid := ipn.IP.Mask(ipn.Mask)
|
||||
return ip.NextIP(nid)
|
||||
}
|
||||
|
||||
func setupBridge(n *NetConf) (*netlink.Bridge, error) {
|
||||
// create bridge if necessary
|
||||
br, err := ensureBridge(n.BrName, n.MTU)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create bridge %q: %v", n.BrName, err)
|
||||
}
|
||||
|
||||
return br, nil
|
||||
}
|
||||
|
||||
func cmdAdd(args *skel.CmdArgs) error {
|
||||
n, err := loadNetConf(args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n.IsDefaultGW {
|
||||
n.IsGW = true
|
||||
}
|
||||
|
||||
br, err := setupBridge(n)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
netns, err := ns.GetNS(args.Netns)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open netns %q: %v", args.Netns, err)
|
||||
}
|
||||
defer netns.Close()
|
||||
|
||||
if err = setupVeth(netns, br, args.IfName, n.MTU, n.HairpinMode); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// run the IPAM plugin and get back the config to apply
|
||||
result, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: make this optional when IPv6 is supported
|
||||
if result.IP4 == nil {
|
||||
return errors.New("IPAM plugin returned missing IPv4 config")
|
||||
}
|
||||
|
||||
if result.IP4.Gateway == nil && n.IsGW {
|
||||
result.IP4.Gateway = calcGatewayIP(&result.IP4.IP)
|
||||
}
|
||||
|
||||
if err := netns.Do(func(_ ns.NetNS) error {
|
||||
// set the default gateway if requested
|
||||
if n.IsDefaultGW {
|
||||
_, defaultNet, err := net.ParseCIDR("0.0.0.0/0")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, route := range result.IP4.Routes {
|
||||
if defaultNet.String() == route.Dst.String() {
|
||||
if route.GW != nil && !route.GW.Equal(result.IP4.Gateway) {
|
||||
return fmt.Errorf(
|
||||
"isDefaultGateway ineffective because IPAM sets default route via %q",
|
||||
route.GW,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.IP4.Routes = append(
|
||||
result.IP4.Routes,
|
||||
types.Route{Dst: *defaultNet, GW: result.IP4.Gateway},
|
||||
)
|
||||
|
||||
// TODO: IPV6
|
||||
}
|
||||
|
||||
if err := ipam.ConfigureIface(args.IfName, result); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ip.SetHWAddrByIP(args.IfName, result.IP4.IP.IP, nil /* TODO IPv6 */); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n.IsGW {
|
||||
gwn := &net.IPNet{
|
||||
IP: result.IP4.Gateway,
|
||||
Mask: result.IP4.IP.Mask,
|
||||
}
|
||||
|
||||
if err = ensureBridgeAddr(br, gwn, n.ForceAddress); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ip.SetHWAddrByIP(n.BrName, gwn.IP, nil /* TODO IPv6 */); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ip.EnableIP4Forward(); err != nil {
|
||||
return fmt.Errorf("failed to enable forwarding: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if n.IPMasq {
|
||||
chain := utils.FormatChainName(n.Name, args.ContainerID)
|
||||
comment := utils.FormatComment(n.Name, args.ContainerID)
|
||||
if err = ip.SetupIPMasq(ip.Network(&result.IP4.IP), chain, comment); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
result.DNS = n.DNS
|
||||
return result.Print()
|
||||
}
|
||||
|
||||
func cmdDel(args *skel.CmdArgs) error {
|
||||
n, err := loadNetConf(args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := ipam.ExecDel(n.IPAM.Type, args.StdinData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if args.Netns == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var ipn *net.IPNet
|
||||
err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
||||
var err error
|
||||
ipn, err = ip.DelLinkByNameAddr(args.IfName, netlink.FAMILY_V4)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n.IPMasq {
|
||||
chain := utils.FormatChainName(n.Name, args.ContainerID)
|
||||
comment := utils.FormatComment(n.Name, args.ContainerID)
|
||||
if err = ip.TeardownIPMasq(ipn, chain, comment); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
|
||||
}
|
27
vendor/github.com/containernetworking/cni/plugins/main/bridge/bridge_suite_test.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/plugins/main/bridge/bridge_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBridge(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "bridge Suite")
|
||||
}
|
326
vendor/github.com/containernetworking/cni/plugins/main/bridge/bridge_test.go
generated
vendored
Normal file
326
vendor/github.com/containernetworking/cni/plugins/main/bridge/bridge_test.go
generated
vendored
Normal file
|
@ -0,0 +1,326 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"syscall"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/ns"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/testutils"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/utils/hwaddr"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
var _ = Describe("bridge Operations", func() {
|
||||
var originalNS ns.NetNS
|
||||
|
||||
BeforeEach(func() {
|
||||
// Create a new NetNS so we don't modify the host
|
||||
var err error
|
||||
originalNS, err = ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(originalNS.Close()).To(Succeed())
|
||||
})
|
||||
|
||||
It("creates a bridge", func() {
|
||||
const IFNAME = "bridge0"
|
||||
|
||||
conf := &NetConf{
|
||||
NetConf: types.NetConf{
|
||||
CNIVersion: "0.2.0",
|
||||
Name: "testConfig",
|
||||
Type: "bridge",
|
||||
},
|
||||
BrName: IFNAME,
|
||||
IsGW: false,
|
||||
IPMasq: false,
|
||||
MTU: 5000,
|
||||
}
|
||||
|
||||
err := originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
bridge, err := setupBridge(conf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(bridge.Attrs().Name).To(Equal(IFNAME))
|
||||
|
||||
// Double check that the link was added
|
||||
link, err := netlink.LinkByName(IFNAME)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().Name).To(Equal(IFNAME))
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("handles an existing bridge", func() {
|
||||
const IFNAME = "bridge0"
|
||||
|
||||
err := originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
err := netlink.LinkAdd(&netlink.Bridge{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: IFNAME,
|
||||
},
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
link, err := netlink.LinkByName(IFNAME)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().Name).To(Equal(IFNAME))
|
||||
ifindex := link.Attrs().Index
|
||||
|
||||
conf := &NetConf{
|
||||
NetConf: types.NetConf{
|
||||
CNIVersion: "0.2.0",
|
||||
Name: "testConfig",
|
||||
Type: "bridge",
|
||||
},
|
||||
BrName: IFNAME,
|
||||
IsGW: false,
|
||||
IPMasq: false,
|
||||
}
|
||||
|
||||
bridge, err := setupBridge(conf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(bridge.Attrs().Name).To(Equal(IFNAME))
|
||||
Expect(bridge.Attrs().Index).To(Equal(ifindex))
|
||||
|
||||
// Double check that the link has the same ifindex
|
||||
link, err = netlink.LinkByName(IFNAME)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().Name).To(Equal(IFNAME))
|
||||
Expect(link.Attrs().Index).To(Equal(ifindex))
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("configures and deconfigures a bridge and veth with default route with ADD/DEL", func() {
|
||||
const BRNAME = "cni0"
|
||||
const IFNAME = "eth0"
|
||||
|
||||
gwaddr, subnet, err := net.ParseCIDR("10.1.2.1/24")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
conf := fmt.Sprintf(`{
|
||||
"cniVersion": "0.2.0",
|
||||
"name": "mynet",
|
||||
"type": "bridge",
|
||||
"bridge": "%s",
|
||||
"isDefaultGateway": true,
|
||||
"ipMasq": false,
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "%s"
|
||||
}
|
||||
}`, BRNAME, subnet.String())
|
||||
|
||||
targetNs, err := ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer targetNs.Close()
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: targetNs.Path(),
|
||||
IfName: IFNAME,
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, err := testutils.CmdAddWithResult(targetNs.Path(), IFNAME, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Make sure bridge link exists
|
||||
link, err := netlink.LinkByName(BRNAME)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().Name).To(Equal(BRNAME))
|
||||
|
||||
// Ensure bridge has gateway address
|
||||
addrs, err := netlink.AddrList(link, syscall.AF_INET)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(len(addrs)).To(BeNumerically(">", 0))
|
||||
found := false
|
||||
subnetPrefix, subnetBits := subnet.Mask.Size()
|
||||
for _, a := range addrs {
|
||||
aPrefix, aBits := a.IPNet.Mask.Size()
|
||||
if a.IPNet.IP.Equal(gwaddr) && aPrefix == subnetPrefix && aBits == subnetBits {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
Expect(found).To(Equal(true))
|
||||
|
||||
// Check for the veth link in the main namespace
|
||||
links, err := netlink.LinkList()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(len(links)).To(Equal(3)) // Bridge, veth, and loopback
|
||||
for _, l := range links {
|
||||
switch {
|
||||
case l.Attrs().Name == BRNAME:
|
||||
{
|
||||
_, isBridge := l.(*netlink.Bridge)
|
||||
Expect(isBridge).To(Equal(true))
|
||||
hwAddr := fmt.Sprintf("%s", l.Attrs().HardwareAddr)
|
||||
Expect(hwAddr).To(HavePrefix(hwaddr.PrivateMACPrefixString))
|
||||
}
|
||||
case l.Attrs().Name != BRNAME && l.Attrs().Name != "lo":
|
||||
{
|
||||
_, isVeth := l.(*netlink.Veth)
|
||||
Expect(isVeth).To(Equal(true))
|
||||
}
|
||||
}
|
||||
}
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Find the veth peer in the container namespace and the default route
|
||||
err = targetNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
link, err := netlink.LinkByName(IFNAME)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().Name).To(Equal(IFNAME))
|
||||
|
||||
hwAddr := fmt.Sprintf("%s", link.Attrs().HardwareAddr)
|
||||
Expect(hwAddr).To(HavePrefix(hwaddr.PrivateMACPrefixString))
|
||||
|
||||
// Ensure the default route
|
||||
routes, err := netlink.RouteList(link, 0)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
var defaultRouteFound bool
|
||||
for _, route := range routes {
|
||||
defaultRouteFound = (route.Dst == nil && route.Src == nil && route.Gw.Equal(gwaddr))
|
||||
if defaultRouteFound {
|
||||
break
|
||||
}
|
||||
}
|
||||
Expect(defaultRouteFound).To(Equal(true))
|
||||
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
err := testutils.CmdDelWithResult(targetNs.Path(), IFNAME, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Make sure macvlan link has been deleted
|
||||
err = targetNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
link, err := netlink.LinkByName(IFNAME)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(link).To(BeNil())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("ensure bridge address", func() {
|
||||
const IFNAME = "bridge0"
|
||||
const EXPECTED_IP = "10.0.0.0/8"
|
||||
const CHANGED_EXPECTED_IP = "10.1.2.3/16"
|
||||
|
||||
conf := &NetConf{
|
||||
NetConf: types.NetConf{
|
||||
CNIVersion: "0.2.0",
|
||||
Name: "testConfig",
|
||||
Type: "bridge",
|
||||
},
|
||||
BrName: IFNAME,
|
||||
IsGW: true,
|
||||
IPMasq: false,
|
||||
MTU: 5000,
|
||||
}
|
||||
|
||||
gwnFirst := &net.IPNet{
|
||||
IP: net.IPv4(10, 0, 0, 0),
|
||||
Mask: net.CIDRMask(8, 32),
|
||||
}
|
||||
|
||||
gwnSecond := &net.IPNet{
|
||||
IP: net.IPv4(10, 1, 2, 3),
|
||||
Mask: net.CIDRMask(16, 32),
|
||||
}
|
||||
|
||||
err := originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
bridge, err := setupBridge(conf)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
// Check if ForceAddress has default value
|
||||
Expect(conf.ForceAddress).To(Equal(false))
|
||||
|
||||
err = ensureBridgeAddr(bridge, gwnFirst, conf.ForceAddress)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
//Check if IP address is set correctly
|
||||
addrs, err := netlink.AddrList(bridge, syscall.AF_INET)
|
||||
Expect(len(addrs)).To(Equal(1))
|
||||
addr := addrs[0].IPNet.String()
|
||||
Expect(addr).To(Equal(EXPECTED_IP))
|
||||
|
||||
//The bridge IP address has been changed. Error expected when ForceAddress is set to false.
|
||||
err = ensureBridgeAddr(bridge, gwnSecond, false)
|
||||
Expect(err).To(HaveOccurred())
|
||||
|
||||
//The IP address should stay the same.
|
||||
addrs, err = netlink.AddrList(bridge, syscall.AF_INET)
|
||||
Expect(len(addrs)).To(Equal(1))
|
||||
addr = addrs[0].IPNet.String()
|
||||
Expect(addr).To(Equal(EXPECTED_IP))
|
||||
|
||||
//Reconfigure IP when ForceAddress is set to true and IP address has been changed.
|
||||
err = ensureBridgeAddr(bridge, gwnSecond, true)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
//Retrieve the IP address after reconfiguration
|
||||
addrs, err = netlink.AddrList(bridge, syscall.AF_INET)
|
||||
Expect(len(addrs)).To(Equal(1))
|
||||
addr = addrs[0].IPNet.String()
|
||||
Expect(addr).To(Equal(CHANGED_EXPECTED_IP))
|
||||
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
178
vendor/github.com/containernetworking/cni/plugins/main/ipvlan/ipvlan.go
generated
vendored
Normal file
178
vendor/github.com/containernetworking/cni/plugins/main/ipvlan/ipvlan.go
generated
vendored
Normal file
|
@ -0,0 +1,178 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/ip"
|
||||
"github.com/containernetworking/cni/pkg/ipam"
|
||||
"github.com/containernetworking/cni/pkg/ns"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
type NetConf struct {
|
||||
types.NetConf
|
||||
Master string `json:"master"`
|
||||
Mode string `json:"mode"`
|
||||
MTU int `json:"mtu"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
// this ensures that main runs only on main thread (thread group leader).
|
||||
// since namespace ops (unshare, setns) are done for a single thread, we
|
||||
// must ensure that the goroutine does not jump from OS thread to thread
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
func loadConf(bytes []byte) (*NetConf, error) {
|
||||
n := &NetConf{}
|
||||
if err := json.Unmarshal(bytes, n); err != nil {
|
||||
return nil, fmt.Errorf("failed to load netconf: %v", err)
|
||||
}
|
||||
if n.Master == "" {
|
||||
return nil, fmt.Errorf(`"master" field is required. It specifies the host interface name to virtualize`)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func modeFromString(s string) (netlink.IPVlanMode, error) {
|
||||
switch s {
|
||||
case "", "l2":
|
||||
return netlink.IPVLAN_MODE_L2, nil
|
||||
case "l3":
|
||||
return netlink.IPVLAN_MODE_L3, nil
|
||||
case "l3s":
|
||||
return netlink.IPVLAN_MODE_L3S, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown ipvlan mode: %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
func createIpvlan(conf *NetConf, ifName string, netns ns.NetNS) error {
|
||||
mode, err := modeFromString(conf.Mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := netlink.LinkByName(conf.Master)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup master %q: %v", conf.Master, err)
|
||||
}
|
||||
|
||||
// due to kernel bug we have to create with tmpname or it might
|
||||
// collide with the name on the host and error out
|
||||
tmpName, err := ip.RandomVethName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mv := &netlink.IPVlan{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
MTU: conf.MTU,
|
||||
Name: tmpName,
|
||||
ParentIndex: m.Attrs().Index,
|
||||
Namespace: netlink.NsFd(int(netns.Fd())),
|
||||
},
|
||||
Mode: mode,
|
||||
}
|
||||
|
||||
if err := netlink.LinkAdd(mv); err != nil {
|
||||
return fmt.Errorf("failed to create ipvlan: %v", err)
|
||||
}
|
||||
|
||||
return netns.Do(func(_ ns.NetNS) error {
|
||||
err := renameLink(tmpName, ifName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to rename ipvlan to %q: %v", ifName, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func cmdAdd(args *skel.CmdArgs) error {
|
||||
n, err := loadConf(args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
netns, err := ns.GetNS(args.Netns)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open netns %q: %v", args.Netns, err)
|
||||
}
|
||||
defer netns.Close()
|
||||
|
||||
if err = createIpvlan(n, args.IfName, netns); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// run the IPAM plugin and get back the config to apply
|
||||
result, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.IP4 == nil {
|
||||
return errors.New("IPAM plugin returned missing IPv4 config")
|
||||
}
|
||||
|
||||
err = netns.Do(func(_ ns.NetNS) error {
|
||||
return ipam.ConfigureIface(args.IfName, result)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result.DNS = n.DNS
|
||||
return result.Print()
|
||||
}
|
||||
|
||||
func cmdDel(args *skel.CmdArgs) error {
|
||||
n, err := loadConf(args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ipam.ExecDel(n.IPAM.Type, args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if args.Netns == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
||||
return ip.DelLinkByName(args.IfName)
|
||||
})
|
||||
}
|
||||
|
||||
func renameLink(curName, newName string) error {
|
||||
link, err := netlink.LinkByName(curName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return netlink.LinkSetName(link, newName)
|
||||
}
|
||||
|
||||
func main() {
|
||||
skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
|
||||
}
|
27
vendor/github.com/containernetworking/cni/plugins/main/ipvlan/ipvlan_suite_test.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/plugins/main/ipvlan/ipvlan_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIpvlan(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "ipvlan Suite")
|
||||
}
|
170
vendor/github.com/containernetworking/cni/plugins/main/ipvlan/ipvlan_test.go
generated
vendored
Normal file
170
vendor/github.com/containernetworking/cni/plugins/main/ipvlan/ipvlan_test.go
generated
vendored
Normal file
|
@ -0,0 +1,170 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/ns"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/testutils"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const MASTER_NAME = "eth0"
|
||||
|
||||
var _ = Describe("ipvlan Operations", func() {
|
||||
var originalNS ns.NetNS
|
||||
|
||||
BeforeEach(func() {
|
||||
// Create a new NetNS so we don't modify the host
|
||||
var err error
|
||||
originalNS, err = ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
// Add master
|
||||
err = netlink.LinkAdd(&netlink.Dummy{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: MASTER_NAME,
|
||||
},
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = netlink.LinkByName(MASTER_NAME)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(originalNS.Close()).To(Succeed())
|
||||
})
|
||||
|
||||
It("creates an ipvlan link in a non-default namespace", func() {
|
||||
conf := &NetConf{
|
||||
NetConf: types.NetConf{
|
||||
CNIVersion: "0.2.0",
|
||||
Name: "testConfig",
|
||||
Type: "ipvlan",
|
||||
},
|
||||
Master: MASTER_NAME,
|
||||
Mode: "l2",
|
||||
MTU: 1500,
|
||||
}
|
||||
|
||||
// Create ipvlan in other namespace
|
||||
targetNs, err := ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer targetNs.Close()
|
||||
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
err := createIpvlan(conf, "foobar0", targetNs)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Make sure ipvlan link exists in the target namespace
|
||||
err = targetNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
link, err := netlink.LinkByName("foobar0")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().Name).To(Equal("foobar0"))
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("configures and deconfigures an iplvan link with ADD/DEL", func() {
|
||||
const IFNAME = "ipvl0"
|
||||
|
||||
conf := fmt.Sprintf(`{
|
||||
"cniVersion": "0.2.0",
|
||||
"name": "mynet",
|
||||
"type": "ipvlan",
|
||||
"master": "%s",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24"
|
||||
}
|
||||
}`, MASTER_NAME)
|
||||
|
||||
targetNs, err := ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer targetNs.Close()
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: targetNs.Path(),
|
||||
IfName: IFNAME,
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, err := testutils.CmdAddWithResult(targetNs.Path(), IFNAME, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Make sure ipvlan link exists in the target namespace
|
||||
err = targetNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
link, err := netlink.LinkByName(IFNAME)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().Name).To(Equal(IFNAME))
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
err = testutils.CmdDelWithResult(targetNs.Path(), IFNAME, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Make sure ipvlan link has been deleted
|
||||
err = targetNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
link, err := netlink.LinkByName(IFNAME)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(link).To(BeNil())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
72
vendor/github.com/containernetworking/cni/plugins/main/loopback/loopback.go
generated
vendored
Normal file
72
vendor/github.com/containernetworking/cni/plugins/main/loopback/loopback.go
generated
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/containernetworking/cni/pkg/ns"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
func cmdAdd(args *skel.CmdArgs) error {
|
||||
args.IfName = "lo" // ignore config, this only works for loopback
|
||||
err := ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
||||
link, err := netlink.LinkByName(args.IfName)
|
||||
if err != nil {
|
||||
return err // not tested
|
||||
}
|
||||
|
||||
err = netlink.LinkSetUp(link)
|
||||
if err != nil {
|
||||
return err // not tested
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err // not tested
|
||||
}
|
||||
|
||||
result := types.Result{}
|
||||
return result.Print()
|
||||
}
|
||||
|
||||
func cmdDel(args *skel.CmdArgs) error {
|
||||
args.IfName = "lo" // ignore config, this only works for loopback
|
||||
err := ns.WithNetNSPath(args.Netns, func(ns.NetNS) error {
|
||||
link, err := netlink.LinkByName(args.IfName)
|
||||
if err != nil {
|
||||
return err // not tested
|
||||
}
|
||||
|
||||
err = netlink.LinkSetDown(link)
|
||||
if err != nil {
|
||||
return err // not tested
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err // not tested
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
|
||||
}
|
41
vendor/github.com/containernetworking/cni/plugins/main/loopback/loopback_suite_test.go
generated
vendored
Normal file
41
vendor/github.com/containernetworking/cni/plugins/main/loopback/loopback_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"github.com/onsi/gomega/gexec"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
var pathToLoPlugin string
|
||||
|
||||
func TestLoopback(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Loopback Suite")
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
var err error
|
||||
pathToLoPlugin, err = gexec.Build("github.com/containernetworking/cni/plugins/main/loopback")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
gexec.CleanupBuildArtifacts()
|
||||
})
|
100
vendor/github.com/containernetworking/cni/plugins/main/loopback/loopback_test.go
generated
vendored
Normal file
100
vendor/github.com/containernetworking/cni/plugins/main/loopback/loopback_test.go
generated
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/ns"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/onsi/gomega/gbytes"
|
||||
"github.com/onsi/gomega/gexec"
|
||||
)
|
||||
|
||||
var _ = Describe("Loopback", func() {
|
||||
var (
|
||||
networkNS ns.NetNS
|
||||
containerID string
|
||||
command *exec.Cmd
|
||||
environ []string
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
command = exec.Command(pathToLoPlugin)
|
||||
|
||||
var err error
|
||||
networkNS, err = ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
environ = []string{
|
||||
fmt.Sprintf("CNI_CONTAINERID=%s", containerID),
|
||||
fmt.Sprintf("CNI_NETNS=%s", networkNS.Path()),
|
||||
fmt.Sprintf("CNI_IFNAME=%s", "this is ignored"),
|
||||
fmt.Sprintf("CNI_ARGS=%s", "none"),
|
||||
fmt.Sprintf("CNI_PATH=%s", "/some/test/path"),
|
||||
}
|
||||
command.Stdin = strings.NewReader(`{ "cniVersion": "0.1.0" }`)
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(networkNS.Close()).To(Succeed())
|
||||
})
|
||||
|
||||
Context("when given a network namespace", func() {
|
||||
It("sets the lo device to UP", func() {
|
||||
command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "ADD"))
|
||||
|
||||
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Eventually(session).Should(gbytes.Say(`{.*}`))
|
||||
Eventually(session).Should(gexec.Exit(0))
|
||||
|
||||
var lo *net.Interface
|
||||
err = networkNS.Do(func(ns.NetNS) error {
|
||||
var err error
|
||||
lo, err = net.InterfaceByName("lo")
|
||||
return err
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(lo.Flags & net.FlagUp).To(Equal(net.FlagUp))
|
||||
})
|
||||
|
||||
It("sets the lo device to DOWN", func() {
|
||||
command.Env = append(environ, fmt.Sprintf("CNI_COMMAND=%s", "DEL"))
|
||||
|
||||
session, err := gexec.Start(command, GinkgoWriter, GinkgoWriter)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Eventually(session).Should(gbytes.Say(``))
|
||||
Eventually(session).Should(gexec.Exit(0))
|
||||
|
||||
var lo *net.Interface
|
||||
err = networkNS.Do(func(ns.NetNS) error {
|
||||
var err error
|
||||
lo, err = net.InterfaceByName("lo")
|
||||
return err
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(lo.Flags & net.FlagUp).NotTo(Equal(net.FlagUp))
|
||||
})
|
||||
})
|
||||
})
|
198
vendor/github.com/containernetworking/cni/plugins/main/macvlan/macvlan.go
generated
vendored
Normal file
198
vendor/github.com/containernetworking/cni/plugins/main/macvlan/macvlan.go
generated
vendored
Normal file
|
@ -0,0 +1,198 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/ip"
|
||||
"github.com/containernetworking/cni/pkg/ipam"
|
||||
"github.com/containernetworking/cni/pkg/ns"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/utils/sysctl"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
const (
|
||||
IPv4InterfaceArpProxySysctlTemplate = "net.ipv4.conf.%s.proxy_arp"
|
||||
)
|
||||
|
||||
type NetConf struct {
|
||||
types.NetConf
|
||||
Master string `json:"master"`
|
||||
Mode string `json:"mode"`
|
||||
MTU int `json:"mtu"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
// this ensures that main runs only on main thread (thread group leader).
|
||||
// since namespace ops (unshare, setns) are done for a single thread, we
|
||||
// must ensure that the goroutine does not jump from OS thread to thread
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
func loadConf(bytes []byte) (*NetConf, error) {
|
||||
n := &NetConf{}
|
||||
if err := json.Unmarshal(bytes, n); err != nil {
|
||||
return nil, fmt.Errorf("failed to load netconf: %v", err)
|
||||
}
|
||||
if n.Master == "" {
|
||||
return nil, fmt.Errorf(`"master" field is required. It specifies the host interface name to virtualize`)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func modeFromString(s string) (netlink.MacvlanMode, error) {
|
||||
switch s {
|
||||
case "", "bridge":
|
||||
return netlink.MACVLAN_MODE_BRIDGE, nil
|
||||
case "private":
|
||||
return netlink.MACVLAN_MODE_PRIVATE, nil
|
||||
case "vepa":
|
||||
return netlink.MACVLAN_MODE_VEPA, nil
|
||||
case "passthru":
|
||||
return netlink.MACVLAN_MODE_PASSTHRU, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unknown macvlan mode: %q", s)
|
||||
}
|
||||
}
|
||||
|
||||
func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) error {
|
||||
mode, err := modeFromString(conf.Mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m, err := netlink.LinkByName(conf.Master)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup master %q: %v", conf.Master, err)
|
||||
}
|
||||
|
||||
// due to kernel bug we have to create with tmpName or it might
|
||||
// collide with the name on the host and error out
|
||||
tmpName, err := ip.RandomVethName()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mv := &netlink.Macvlan{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
MTU: conf.MTU,
|
||||
Name: tmpName,
|
||||
ParentIndex: m.Attrs().Index,
|
||||
Namespace: netlink.NsFd(int(netns.Fd())),
|
||||
},
|
||||
Mode: mode,
|
||||
}
|
||||
|
||||
if err := netlink.LinkAdd(mv); err != nil {
|
||||
return fmt.Errorf("failed to create macvlan: %v", err)
|
||||
}
|
||||
|
||||
return netns.Do(func(_ ns.NetNS) error {
|
||||
// TODO: duplicate following lines for ipv6 support, when it will be added in other places
|
||||
ipv4SysctlValueName := fmt.Sprintf(IPv4InterfaceArpProxySysctlTemplate, tmpName)
|
||||
if _, err := sysctl.Sysctl(ipv4SysctlValueName, "1"); err != nil {
|
||||
// remove the newly added link and ignore errors, because we already are in a failed state
|
||||
_ = netlink.LinkDel(mv)
|
||||
return fmt.Errorf("failed to set proxy_arp on newly added interface %q: %v", tmpName, err)
|
||||
}
|
||||
|
||||
err := renameLink(tmpName, ifName)
|
||||
if err != nil {
|
||||
_ = netlink.LinkDel(mv)
|
||||
return fmt.Errorf("failed to rename macvlan to %q: %v", ifName, err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func cmdAdd(args *skel.CmdArgs) error {
|
||||
n, err := loadConf(args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
netns, err := ns.GetNS(args.Netns)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open netns %q: %v", netns, err)
|
||||
}
|
||||
defer netns.Close()
|
||||
|
||||
if err = createMacvlan(n, args.IfName, netns); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// run the IPAM plugin and get back the config to apply
|
||||
result, err := ipam.ExecAdd(n.IPAM.Type, args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.IP4 == nil {
|
||||
return errors.New("IPAM plugin returned missing IPv4 config")
|
||||
}
|
||||
|
||||
err = netns.Do(func(_ ns.NetNS) error {
|
||||
if err := ip.SetHWAddrByIP(args.IfName, result.IP4.IP.IP, nil /* TODO IPv6 */); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ipam.ConfigureIface(args.IfName, result)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
result.DNS = n.DNS
|
||||
return result.Print()
|
||||
}
|
||||
|
||||
func cmdDel(args *skel.CmdArgs) error {
|
||||
n, err := loadConf(args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = ipam.ExecDel(n.IPAM.Type, args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if args.Netns == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
||||
return ip.DelLinkByName(args.IfName)
|
||||
})
|
||||
}
|
||||
|
||||
func renameLink(curName, newName string) error {
|
||||
link, err := netlink.LinkByName(curName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return netlink.LinkSetName(link, newName)
|
||||
}
|
||||
|
||||
func main() {
|
||||
skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
|
||||
}
|
27
vendor/github.com/containernetworking/cni/plugins/main/macvlan/macvlan_suite_test.go
generated
vendored
Normal file
27
vendor/github.com/containernetworking/cni/plugins/main/macvlan/macvlan_suite_test.go
generated
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Copyright 2016 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMacvlan(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "macvlan Suite")
|
||||
}
|
175
vendor/github.com/containernetworking/cni/plugins/main/macvlan/macvlan_test.go
generated
vendored
Normal file
175
vendor/github.com/containernetworking/cni/plugins/main/macvlan/macvlan_test.go
generated
vendored
Normal file
|
@ -0,0 +1,175 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/ns"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/testutils"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/utils/hwaddr"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
const MASTER_NAME = "eth0"
|
||||
|
||||
var _ = Describe("macvlan Operations", func() {
|
||||
var originalNS ns.NetNS
|
||||
|
||||
BeforeEach(func() {
|
||||
// Create a new NetNS so we don't modify the host
|
||||
var err error
|
||||
originalNS, err = ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
// Add master
|
||||
err = netlink.LinkAdd(&netlink.Dummy{
|
||||
LinkAttrs: netlink.LinkAttrs{
|
||||
Name: MASTER_NAME,
|
||||
},
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = netlink.LinkByName(MASTER_NAME)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
Expect(originalNS.Close()).To(Succeed())
|
||||
})
|
||||
|
||||
It("creates an macvlan link in a non-default namespace", func() {
|
||||
conf := &NetConf{
|
||||
NetConf: types.NetConf{
|
||||
CNIVersion: "0.2.0",
|
||||
Name: "testConfig",
|
||||
Type: "macvlan",
|
||||
},
|
||||
Master: MASTER_NAME,
|
||||
Mode: "bridge",
|
||||
MTU: 1500,
|
||||
}
|
||||
|
||||
targetNs, err := ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer targetNs.Close()
|
||||
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
err = createMacvlan(conf, "foobar0", targetNs)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Make sure macvlan link exists in the target namespace
|
||||
err = targetNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
link, err := netlink.LinkByName("foobar0")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().Name).To(Equal("foobar0"))
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
It("configures and deconfigures a macvlan link with ADD/DEL", func() {
|
||||
const IFNAME = "macvl0"
|
||||
|
||||
conf := fmt.Sprintf(`{
|
||||
"cniVersion": "0.2.0",
|
||||
"name": "mynet",
|
||||
"type": "macvlan",
|
||||
"master": "%s",
|
||||
"ipam": {
|
||||
"type": "host-local",
|
||||
"subnet": "10.1.2.0/24"
|
||||
}
|
||||
}`, MASTER_NAME)
|
||||
|
||||
targetNs, err := ns.NewNS()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer targetNs.Close()
|
||||
|
||||
args := &skel.CmdArgs{
|
||||
ContainerID: "dummy",
|
||||
Netns: targetNs.Path(),
|
||||
IfName: IFNAME,
|
||||
StdinData: []byte(conf),
|
||||
}
|
||||
|
||||
// Make sure macvlan link exists in the target namespace
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
_, err := testutils.CmdAddWithResult(targetNs.Path(), IFNAME, func() error {
|
||||
return cmdAdd(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Make sure macvlan link exists in the target namespace
|
||||
err = targetNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
link, err := netlink.LinkByName(IFNAME)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(link.Attrs().Name).To(Equal(IFNAME))
|
||||
|
||||
hwAddr := fmt.Sprintf("%s", link.Attrs().HardwareAddr)
|
||||
Expect(hwAddr).To(HavePrefix(hwaddr.PrivateMACPrefixString))
|
||||
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = originalNS.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
err := testutils.CmdDelWithResult(targetNs.Path(), IFNAME, func() error {
|
||||
return cmdDel(args)
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Make sure macvlan link has been deleted
|
||||
err = targetNs.Do(func(ns.NetNS) error {
|
||||
defer GinkgoRecover()
|
||||
|
||||
link, err := netlink.LinkByName(IFNAME)
|
||||
Expect(err).To(HaveOccurred())
|
||||
Expect(link).To(BeNil())
|
||||
return nil
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
241
vendor/github.com/containernetworking/cni/plugins/main/ptp/ptp.go
generated
vendored
Normal file
241
vendor/github.com/containernetworking/cni/plugins/main/ptp/ptp.go
generated
vendored
Normal file
|
@ -0,0 +1,241 @@
|
|||
// Copyright 2015 CNI authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
|
||||
"github.com/containernetworking/cni/pkg/ip"
|
||||
"github.com/containernetworking/cni/pkg/ipam"
|
||||
"github.com/containernetworking/cni/pkg/ns"
|
||||
"github.com/containernetworking/cni/pkg/skel"
|
||||
"github.com/containernetworking/cni/pkg/types"
|
||||
"github.com/containernetworking/cni/pkg/utils"
|
||||
"github.com/containernetworking/cni/pkg/version"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// this ensures that main runs only on main thread (thread group leader).
|
||||
// since namespace ops (unshare, setns) are done for a single thread, we
|
||||
// must ensure that the goroutine does not jump from OS thread to thread
|
||||
runtime.LockOSThread()
|
||||
}
|
||||
|
||||
type NetConf struct {
|
||||
types.NetConf
|
||||
IPMasq bool `json:"ipMasq"`
|
||||
MTU int `json:"mtu"`
|
||||
}
|
||||
|
||||
func setupContainerVeth(netns, ifName string, mtu int, pr *types.Result) (string, error) {
|
||||
// The IPAM result will be something like IP=192.168.3.5/24, GW=192.168.3.1.
|
||||
// What we want is really a point-to-point link but veth does not support IFF_POINTOPONT.
|
||||
// Next best thing would be to let it ARP but set interface to 192.168.3.5/32 and
|
||||
// add a route like "192.168.3.0/24 via 192.168.3.1 dev $ifName".
|
||||
// Unfortunately that won't work as the GW will be outside the interface's subnet.
|
||||
|
||||
// Our solution is to configure the interface with 192.168.3.5/24, then delete the
|
||||
// "192.168.3.0/24 dev $ifName" route that was automatically added. Then we add
|
||||
// "192.168.3.1/32 dev $ifName" and "192.168.3.0/24 via 192.168.3.1 dev $ifName".
|
||||
// In other words we force all traffic to ARP via the gateway except for GW itself.
|
||||
|
||||
var hostVethName string
|
||||
err := ns.WithNetNSPath(netns, func(hostNS ns.NetNS) error {
|
||||
hostVeth, _, err := ip.SetupVeth(ifName, mtu, hostNS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hostNS.Do(func(_ ns.NetNS) error {
|
||||
hostVethName = hostVeth.Attrs().Name
|
||||
if err := ip.SetHWAddrByIP(hostVethName, pr.IP4.IP.IP, nil /* TODO IPv6 */); err != nil {
|
||||
return fmt.Errorf("failed to set hardware addr by IP: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err = ipam.ConfigureIface(ifName, pr); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
contVeth, err := netlink.LinkByName(ifName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to look up %q: %v", ifName, err)
|
||||
}
|
||||
|
||||
if err := ip.SetHWAddrByIP(contVeth.Attrs().Name, pr.IP4.IP.IP, nil /* TODO IPv6 */); err != nil {
|
||||
return fmt.Errorf("failed to set hardware addr by IP: %v", err)
|
||||
}
|
||||
|
||||
// Delete the route that was automatically added
|
||||
route := netlink.Route{
|
||||
LinkIndex: contVeth.Attrs().Index,
|
||||
Dst: &net.IPNet{
|
||||
IP: pr.IP4.IP.IP.Mask(pr.IP4.IP.Mask),
|
||||
Mask: pr.IP4.IP.Mask,
|
||||
},
|
||||
Scope: netlink.SCOPE_NOWHERE,
|
||||
}
|
||||
|
||||
if err := netlink.RouteDel(&route); err != nil {
|
||||
return fmt.Errorf("failed to delete route %v: %v", route, err)
|
||||
}
|
||||
|
||||
for _, r := range []netlink.Route{
|
||||
netlink.Route{
|
||||
LinkIndex: contVeth.Attrs().Index,
|
||||
Dst: &net.IPNet{
|
||||
IP: pr.IP4.Gateway,
|
||||
Mask: net.CIDRMask(32, 32),
|
||||
},
|
||||
Scope: netlink.SCOPE_LINK,
|
||||
Src: pr.IP4.IP.IP,
|
||||
},
|
||||
netlink.Route{
|
||||
LinkIndex: contVeth.Attrs().Index,
|
||||
Dst: &net.IPNet{
|
||||
IP: pr.IP4.IP.IP.Mask(pr.IP4.IP.Mask),
|
||||
Mask: pr.IP4.IP.Mask,
|
||||
},
|
||||
Scope: netlink.SCOPE_UNIVERSE,
|
||||
Gw: pr.IP4.Gateway,
|
||||
Src: pr.IP4.IP.IP,
|
||||
},
|
||||
} {
|
||||
if err := netlink.RouteAdd(&r); err != nil {
|
||||
return fmt.Errorf("failed to add route %v: %v", r, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return hostVethName, err
|
||||
}
|
||||
|
||||
func setupHostVeth(vethName string, ipConf *types.IPConfig) error {
|
||||
// hostVeth moved namespaces and may have a new ifindex
|
||||
veth, err := netlink.LinkByName(vethName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to lookup %q: %v", vethName, err)
|
||||
}
|
||||
|
||||
// TODO(eyakubovich): IPv6
|
||||
ipn := &net.IPNet{
|
||||
IP: ipConf.Gateway,
|
||||
Mask: net.CIDRMask(32, 32),
|
||||
}
|
||||
addr := &netlink.Addr{IPNet: ipn, Label: ""}
|
||||
if err = netlink.AddrAdd(veth, addr); err != nil {
|
||||
return fmt.Errorf("failed to add IP addr (%#v) to veth: %v", ipn, err)
|
||||
}
|
||||
|
||||
ipn = &net.IPNet{
|
||||
IP: ipConf.IP.IP,
|
||||
Mask: net.CIDRMask(32, 32),
|
||||
}
|
||||
// dst happens to be the same as IP/net of host veth
|
||||
if err = ip.AddHostRoute(ipn, nil, veth); err != nil && !os.IsExist(err) {
|
||||
return fmt.Errorf("failed to add route on host: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func cmdAdd(args *skel.CmdArgs) error {
|
||||
conf := NetConf{}
|
||||
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||
return fmt.Errorf("failed to load netconf: %v", err)
|
||||
}
|
||||
|
||||
if err := ip.EnableIP4Forward(); err != nil {
|
||||
return fmt.Errorf("failed to enable forwarding: %v", err)
|
||||
}
|
||||
|
||||
// run the IPAM plugin and get back the config to apply
|
||||
result, err := ipam.ExecAdd(conf.IPAM.Type, args.StdinData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if result.IP4 == nil {
|
||||
return errors.New("IPAM plugin returned missing IPv4 config")
|
||||
}
|
||||
|
||||
hostVethName, err := setupContainerVeth(args.Netns, args.IfName, conf.MTU, result)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = setupHostVeth(hostVethName, result.IP4); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if conf.IPMasq {
|
||||
chain := utils.FormatChainName(conf.Name, args.ContainerID)
|
||||
comment := utils.FormatComment(conf.Name, args.ContainerID)
|
||||
if err = ip.SetupIPMasq(&result.IP4.IP, chain, comment); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
result.DNS = conf.DNS
|
||||
return result.Print()
|
||||
}
|
||||
|
||||
func cmdDel(args *skel.CmdArgs) error {
|
||||
conf := NetConf{}
|
||||
if err := json.Unmarshal(args.StdinData, &conf); err != nil {
|
||||
return fmt.Errorf("failed to load netconf: %v", err)
|
||||
}
|
||||
|
||||
if err := ipam.ExecDel(conf.IPAM.Type, args.StdinData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if args.Netns == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
var ipn *net.IPNet
|
||||
err := ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
||||
var err error
|
||||
ipn, err = ip.DelLinkByNameAddr(args.IfName, netlink.FAMILY_V4)
|
||||
return err
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if conf.IPMasq {
|
||||
chain := utils.FormatChainName(conf.Name, args.ContainerID)
|
||||
comment := utils.FormatComment(conf.Name, args.ContainerID)
|
||||
if err = ip.TeardownIPMasq(ipn, chain, comment); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
skel.PluginMain(cmdAdd, cmdDel, version.Legacy)
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue