Compare commits
8 Commits
v0.3.0-rc2
...
v0.2.3
Author | SHA1 | Date | |
---|---|---|---|
b634ff6bcd | |||
8c51016e19 | |||
320f810cfb | |||
19bb4a15bb | |||
a721ce6bbf | |||
5ab94d6e50 | |||
2ea9379fa4 | |||
cf43d2f78f |
@ -5,7 +5,7 @@ pull requests. This document outlines some of the conventions on development
|
|||||||
workflow, commit message formatting, contact points and other resources to make
|
workflow, commit message formatting, contact points and other resources to make
|
||||||
it easier to get your contribution accepted.
|
it easier to get your contribution accepted.
|
||||||
|
|
||||||
We welcome improvements to documentation as well as to code.
|
For more information on the policy for accepting contributions, see [POLICY](POLICY.md)
|
||||||
|
|
||||||
# Certificate of Origin
|
# Certificate of Origin
|
||||||
|
|
||||||
@ -16,9 +16,9 @@ contribution. See the [DCO](DCO) file for details.
|
|||||||
|
|
||||||
# Email and Chat
|
# Email and Chat
|
||||||
|
|
||||||
The project uses the the cni-dev email list and IRC chat:
|
The project uses the the cni-dev email list and #appc on Freenode for chat:
|
||||||
- Email: [cni-dev](https://groups.google.com/forum/#!forum/cni-dev)
|
- Email: [cni-dev](https://groups.google.com/forum/#!forum/cni-dev)
|
||||||
- IRC: #[containernetworking](irc://irc.freenode.org:6667/#containernetworking) channel on freenode.org
|
- IRC: #[appc](irc://irc.freenode.org:6667/#appc) IRC channel on freenode.org
|
||||||
|
|
||||||
Please avoid emailing maintainers found in the MAINTAINERS file directly. They
|
Please avoid emailing maintainers found in the MAINTAINERS file directly. They
|
||||||
are very busy and read the mailing lists.
|
are very busy and read the mailing lists.
|
||||||
@ -27,18 +27,17 @@ are very busy and read the mailing lists.
|
|||||||
|
|
||||||
- Fork the repository on GitHub
|
- Fork the repository on GitHub
|
||||||
- Read the [README](README.md) for build and test instructions
|
- Read the [README](README.md) for build and test instructions
|
||||||
- Play with the project, submit bugs, submit pull requests!
|
- Play with the project, submit bugs, submit patches!
|
||||||
|
|
||||||
## Contribution workflow
|
## Contribution Flow
|
||||||
|
|
||||||
This is a rough outline of how to prepare a contribution:
|
This is a rough outline of what a contributor's workflow looks like:
|
||||||
|
|
||||||
- Create a topic branch from where you want to base your work (usually branched from master).
|
- Create a topic branch from where you want to base your work (usually master).
|
||||||
- Make commits of logical units.
|
- Make commits of logical units.
|
||||||
- Make sure your commit messages are in the proper format (see below).
|
- 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.
|
- Push your changes to a topic branch in your fork of the repository.
|
||||||
- If you changed code, make sure the tests pass, and add any new tests as appropriate.
|
- Make sure the tests pass, and add any new tests as appropriate.
|
||||||
- Make sure any new code files have a license header.
|
|
||||||
- Submit a pull request to the original repository.
|
- Submit a pull request to the original repository.
|
||||||
|
|
||||||
Thanks for your contributions!
|
Thanks for your contributions!
|
||||||
|
@ -17,9 +17,8 @@ If the bridge is missing, the plugin will create one on first use and, if gatewa
|
|||||||
"name": "mynet",
|
"name": "mynet",
|
||||||
"type": "bridge",
|
"type": "bridge",
|
||||||
"bridge": "mynet0",
|
"bridge": "mynet0",
|
||||||
"isDefaultGateway": true,
|
"isGateway": true,
|
||||||
"ipMasq": true,
|
"ipMasq": true,
|
||||||
"hairpinMode": true,
|
|
||||||
"ipam": {
|
"ipam": {
|
||||||
"type": "host-local",
|
"type": "host-local",
|
||||||
"subnet": "10.10.0.0/16"
|
"subnet": "10.10.0.0/16"
|
||||||
@ -33,8 +32,6 @@ If the bridge is missing, the plugin will create one on first use and, if gatewa
|
|||||||
* `type` (string, required): "bridge".
|
* `type` (string, required): "bridge".
|
||||||
* `bridge` (string, optional): name of the bridge to use/create. Defaults to "cni0".
|
* `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.
|
* `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.
|
|
||||||
* `ipMasq` (boolean, optional): set up IP Masquerade on the host for traffic originating from this network and destined outside of it. 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.
|
* `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.
|
* `ipam` (dictionary, required): IPAM configuration to be used for this network.
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
With dhcp plugin the containers can get an IP allocated by a DHCP server already running on your network.
|
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).
|
This can be especially useful with plugin types such as [macvlan](https://github.com/appc/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.
|
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.
|
The same plugin binary can also be run in the daemon mode.
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ It stores the state locally on the host filesystem, therefore ensuring uniquenes
|
|||||||
* `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.
|
* `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.
|
||||||
|
|
||||||
## Supported arguments
|
## Supported arguments
|
||||||
The following [CNI_ARGS](https://github.com/containernetworking/cni/blob/master/SPEC.md#parameters) are supported:
|
The following [CNI_ARGS](https://github.com/appc/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
|
* `ip`: request a specific IP address from the subnet. If it's not available, the plugin will exit with an error
|
||||||
|
|
||||||
|
2
Godeps/Godeps.json
generated
2
Godeps/Godeps.json
generated
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"ImportPath": "github.com/containernetworking/cni",
|
"ImportPath": "github.com/appc/cni",
|
||||||
"GoVersion": "go1.6",
|
"GoVersion": "go1.6",
|
||||||
"Packages": [
|
"Packages": [
|
||||||
"./..."
|
"./..."
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
Gabe Rosenhouse <grosenhouse@pivotal.io> (@rosenhouse)
|
|
||||||
Michael Bridgen <michael@weave.works> (@squaremo)
|
Michael Bridgen <michael@weave.works> (@squaremo)
|
||||||
Stefan Junker <stefan.junker@coreos.com> (@steveeJ)
|
Stefan Junker <stefan.junker@coreos.com> (@steveeJ)
|
||||||
Tom Denham <tom.denham@metaswitch.com> (@tomdee)
|
|
||||||
Zach Gershman <zachgersh@gmail.com> (@zachgersh)
|
Zach Gershman <zachgersh@gmail.com> (@zachgersh)
|
||||||
|
41
README.md
41
README.md
@ -1,25 +1,22 @@
|
|||||||
[](https://travis-ci.org/containernetworking/cni)
|
[](https://travis-ci.org/appc/cni)
|
||||||
[](https://coveralls.io/github/containernetworking/cni?branch=master)
|
[](https://coveralls.io/github/appc/cni?branch=master)
|
||||||
|
|
||||||
# CNI - the Container Network Interface
|
# CNI - the Container Network Interface
|
||||||
|
|
||||||
## What is CNI?
|
## 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, the _Container Network Interface_, is a proposed standard for configuring network interfaces for Linux application containers.
|
||||||
CNI concerns itself only with network connectivity of containers and removing allocated resources when the container is deleted.
|
The standard consists of a simple specification for how executable plugins can be used to configure network namespaces; this repository also contains a go library implementing that specification.
|
||||||
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 specification itself is contained in [SPEC.md](SPEC.md).
|
||||||
|
|
||||||
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?
|
## 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.
|
Application containers on Linux are a rapidly evolving area, and within this space networking is a particularly unsolved problem, 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.
|
We believe that every container runtime 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.
|
To avoid duplication, we think it is prudent to define a common interface between the network plugins and container execution.
|
||||||
|
Hence we are proposing this specification, along with an initial set of plugins that can be used by different container runtime systems.
|
||||||
|
|
||||||
## Who is using CNI?
|
## Who is using CNI?
|
||||||
|
|
||||||
@ -29,29 +26,25 @@ To avoid duplication, we think it is prudent to define a common interface betwee
|
|||||||
- [Cloud Foundry - a platform for cloud applications](https://github.com/cloudfoundry-incubator/guardian-cni-adapter)
|
- [Cloud Foundry - a platform for cloud applications](https://github.com/cloudfoundry-incubator/guardian-cni-adapter)
|
||||||
- [Weave - a multi-host Docker network](https://github.com/weaveworks/weave)
|
- [Weave - a multi-host Docker network](https://github.com/weaveworks/weave)
|
||||||
- [Project Calico - a layer 3 virtual network](https://github.com/projectcalico/calico-cni)
|
- [Project Calico - a layer 3 virtual network](https://github.com/projectcalico/calico-cni)
|
||||||
- [Contiv Networking - policy networking for various use cases](https://github.com/contiv/netplugin)
|
|
||||||
|
|
||||||
## Contributing to CNI
|
## Contributing to CNI
|
||||||
|
|
||||||
We welcome contributions, including [bug reports](https://github.com/containernetworking/cni/issues), and code and documentation improvements.
|
We welcome contributions, including [bug reports](https://github.com/appc/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.
|
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?
|
## How do I use CNI?
|
||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
CNI requires Go 1.5+ to build.
|
CNI requires Go 1.5+ to build.
|
||||||
|
|
||||||
Go 1.5 users will need to set GO15VENDOREXPERIMENT=1 to get vendored
|
Go 1.5 users will need to set GO15VENDOREXPERIMENT=1 to get vendored
|
||||||
dependencies. This flag is set by default in 1.6.
|
dependencies. This flag is set by default in 1.6.
|
||||||
|
|
||||||
### Included Plugins
|
### Included Plugins
|
||||||
|
|
||||||
This repository includes a number of common plugins in the `plugins/` directory.
|
This repository includes a number of common plugins in the `plugins/` directory.
|
||||||
Please see the [Documentation/](Documentation/) directory for documentation about particular plugins.
|
Please see the [Documentation/](Documentation/) directory for documentation about particular plugins.
|
||||||
|
|
||||||
### Running the 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.
|
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`**
|
**note - priv-net-run.sh depends on `jq`**
|
||||||
@ -146,18 +139,8 @@ lo Link encap:Local Loopback
|
|||||||
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)
|
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
|
## Contact
|
||||||
|
|
||||||
For any questions about CNI, please reach out on the mailing list:
|
For any questions about CNI, please reach out on the mailing list or IRC:
|
||||||
- Email: [cni-dev](https://groups.google.com/forum/#!forum/cni-dev)
|
- Email: [cni-dev](https://groups.google.com/forum/#!forum/cni-dev)
|
||||||
- IRC: #[containernetworking](irc://irc.freenode.org:6667/#containernetworking) channel on freenode.org
|
- IRC: #[appc](irc://irc.freenode.org:6667/#appc) IRC channel on freenode.org
|
||||||
|
4
build
4
build
@ -1,7 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -xe
|
||||||
|
|
||||||
ORG_PATH="github.com/containernetworking"
|
ORG_PATH="github.com/appc"
|
||||||
REPO_PATH="${ORG_PATH}/cni"
|
REPO_PATH="${ORG_PATH}/cni"
|
||||||
|
|
||||||
if [ ! -h gopath/src/${REPO_PATH} ]; then
|
if [ ! -h gopath/src/${REPO_PATH} ]; then
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/libcni"
|
"github.com/appc/cni/libcni"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -17,8 +17,8 @@ package libcni
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/invoke"
|
"github.com/appc/cni/pkg/invoke"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type RuntimeConf struct {
|
type RuntimeConf struct {
|
||||||
|
@ -1,17 +1,3 @@
|
|||||||
// 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
|
package invoke
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -19,7 +5,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func DelegateAdd(delegatePlugin string, netconf []byte) (*types.Result, error) {
|
func DelegateAdd(delegatePlugin string, netconf []byte) (*types.Result, error) {
|
||||||
|
@ -21,7 +21,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func pluginErr(err error, output []byte) error {
|
func pluginErr(err error, output []byte) error {
|
||||||
|
@ -1,17 +1,3 @@
|
|||||||
// 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
|
package invoke_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@ -19,7 +5,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/invoke"
|
"github.com/appc/cni/pkg/invoke"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
)
|
)
|
||||||
|
@ -1,17 +1,3 @@
|
|||||||
// 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
|
package invoke_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
"github.com/appc/cni/pkg/ns"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ func RandomVethName() (string, error) {
|
|||||||
// SetupVeth sets up a virtual ethernet link.
|
// SetupVeth sets up a virtual ethernet link.
|
||||||
// Should be in container netns, and will switch back to hostNS to set the host
|
// Should be in container netns, and will switch back to hostNS to set the host
|
||||||
// veth end up.
|
// veth end up.
|
||||||
func SetupVeth(contVethName string, mtu int, hostNS ns.NetNS) (hostVeth, contVeth netlink.Link, err error) {
|
func SetupVeth(contVethName string, mtu int, hostNS *os.File) (hostVeth, contVeth netlink.Link, err error) {
|
||||||
var hostVethName string
|
var hostVethName string
|
||||||
hostVethName, contVeth, err = makeVeth(contVethName, mtu)
|
hostVethName, contVeth, err = makeVeth(contVethName, mtu)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -104,10 +104,10 @@ func SetupVeth(contVethName string, mtu int, hostNS ns.NetNS) (hostVeth, contVet
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = hostNS.Do(func(_ ns.NetNS) error {
|
err = ns.WithNetNS(hostNS, false, func(_ *os.File) error {
|
||||||
hostVeth, err := netlink.LinkByName(hostVethName)
|
hostVeth, err := netlink.LinkByName(hostVethName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Path(), err)
|
return fmt.Errorf("failed to lookup %q in %q: %v", hostVethName, hostNS.Name(), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = netlink.LinkSetUp(hostVeth); err != nil {
|
if err = netlink.LinkSetUp(hostVeth); err != nil {
|
||||||
|
@ -18,9 +18,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/invoke"
|
"github.com/appc/cni/pkg/invoke"
|
||||||
"github.com/containernetworking/cni/pkg/ip"
|
"github.com/appc/cni/pkg/ip"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
### Namespaces, Threads, and Go
|
|
||||||
On Linux each OS thread can have a different network namespace. Go's thread scheduling model switches goroutines between OS threads based on OS thread load and whether the goroutine would block other goroutines. This can result in a goroutine switching network namespaces without notice and lead to errors in your code.
|
|
||||||
|
|
||||||
### Namespace Switching
|
|
||||||
Switching namespaces with the `ns.Set()` method is not recommended without additional strategies to prevent unexpected namespace changes when your goroutines switch OS threads.
|
|
||||||
|
|
||||||
Go provides the `runtime.LockOSThread()` function to ensure a specific goroutine executes on its current OS thread and prevents any other goroutine from running in that thread until the locked one exits. Careful usage of `LockOSThread()` and goroutines can provide good control over which network namespace a given goroutine executes in.
|
|
||||||
|
|
||||||
For example, you cannot rely on the `ns.Set()` namespace being the current namespace after the `Set()` call unless you do two things. First, the goroutine calling `Set()` must have previously called `LockOSThread()`. Second, you must ensure `runtime.UnlockOSThread()` is not called somewhere in-between. You also cannot rely on the initial network namespace remaining the current network namespace if any other code in your program switches namespaces, unless you have already called `LockOSThread()` in that goroutine. Note that `LockOSThread()` prevents the Go scheduler from optimally scheduling goroutines for best performance, so `LockOSThread()` should only be used in small, isolated goroutines that release the lock quickly.
|
|
||||||
|
|
||||||
### Do() The Recommended Thing
|
|
||||||
The `ns.Do()` method provides control over network namespaces for you by implementing these strategies. All code dependent on a particular network namespace should be wrapped in the `ns.Do()` method to ensure the correct namespace is selected for the duration of your code. For example:
|
|
||||||
|
|
||||||
```go
|
|
||||||
targetNs, err := ns.NewNS()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
err = targetNs.Do(func(hostNs ns.NetNS) error {
|
|
||||||
dummy := &netlink.Dummy{
|
|
||||||
LinkAttrs: netlink.LinkAttrs{
|
|
||||||
Name: "dummy0",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return netlink.LinkAdd(dummy)
|
|
||||||
})
|
|
||||||
```
|
|
||||||
|
|
||||||
### Further Reading
|
|
||||||
- https://github.com/golang/go/wiki/LockOSThread
|
|
||||||
- http://morsmachine.dk/go-scheduler
|
|
318
pkg/ns/ns.go
318
pkg/ns/ns.go
@ -15,293 +15,79 @@
|
|||||||
package ns
|
package ns
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type NetNS interface {
|
var setNsMap = map[string]uintptr{
|
||||||
// Executes the passed closure in this object's network namespace,
|
"386": 346,
|
||||||
// attemtping to restore the original namespace before returning.
|
"amd64": 308,
|
||||||
// However, since each OS thread can have a different network namespace,
|
"arm": 374,
|
||||||
// and Go's thread scheduling is highly variable, callers cannot
|
|
||||||
// guarantee any specific namespace is set unless operations that
|
|
||||||
// require that namespace are wrapped with Do(). Also, no code called
|
|
||||||
// from Do() should call runtime.UnlockOSThread(), or the risk
|
|
||||||
// of executing code in an incorrect namespace will be greater. See
|
|
||||||
// https://github.com/golang/go/wiki/LockOSThread for further details.
|
|
||||||
Do(toRun func(NetNS) error) error
|
|
||||||
|
|
||||||
// Sets the current network namespace to this object's network namespace.
|
|
||||||
// Note that since Go's thread scheduling is highly variable, callers
|
|
||||||
// cannot guarantee the requested namespace will be the current namespace
|
|
||||||
// after this function is called; to ensure this wrap operations that
|
|
||||||
// require the namespace with Do() instead.
|
|
||||||
Set() error
|
|
||||||
|
|
||||||
// Returns the filesystem path representing this object's network namespace
|
|
||||||
Path() string
|
|
||||||
|
|
||||||
// Returns a file descriptor representing this object's network namespace
|
|
||||||
Fd() uintptr
|
|
||||||
|
|
||||||
// Cleans up this instance of the network namespace; if this instance
|
|
||||||
// is the last user the namespace will be destroyed
|
|
||||||
Close() error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type netNS struct {
|
// SetNS sets the network namespace on a target file.
|
||||||
file *os.File
|
func SetNS(f *os.File, flags uintptr) error {
|
||||||
mounted bool
|
if runtime.GOOS != "linux" {
|
||||||
closed bool
|
return fmt.Errorf("unsupported OS: %s", runtime.GOOS)
|
||||||
}
|
|
||||||
|
|
||||||
func getCurrentThreadNetNSPath() string {
|
|
||||||
// /proc/self/ns/net returns the namespace of the main thread, not
|
|
||||||
// of whatever thread this goroutine is running on. Make sure we
|
|
||||||
// use the thread's net namespace since the thread is switching around
|
|
||||||
return fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), unix.Gettid())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns an object representing the current OS thread's network namespace
|
|
||||||
func GetCurrentNS() (NetNS, error) {
|
|
||||||
return GetNS(getCurrentThreadNetNSPath())
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
// https://github.com/torvalds/linux/blob/master/include/uapi/linux/magic.h
|
|
||||||
NSFS_MAGIC = 0x6e736673
|
|
||||||
PROCFS_MAGIC = 0x9fa0
|
|
||||||
)
|
|
||||||
|
|
||||||
func IsNS(nspath string) (isNS bool, msg string, err error) {
|
|
||||||
stat := syscall.Statfs_t{}
|
|
||||||
if err = syscall.Statfs(nspath, &stat); err != nil {
|
|
||||||
err = fmt.Errorf("failed to Statfs %s: %v", nspath, err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch stat.Type {
|
trap, ok := setNsMap[runtime.GOARCH]
|
||||||
case PROCFS_MAGIC:
|
if !ok {
|
||||||
// Kernel < 3.19
|
return fmt.Errorf("unsupported arch: %s", runtime.GOARCH)
|
||||||
|
|
||||||
validPathContent := "ns/"
|
|
||||||
validName := strings.Contains(nspath, validPathContent)
|
|
||||||
if !validName {
|
|
||||||
msg = fmt.Sprintf("path doesn't contain %q", validPathContent)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
isNS = true
|
|
||||||
case NSFS_MAGIC:
|
|
||||||
// Kernel >= 3.19
|
|
||||||
|
|
||||||
isNS = true
|
|
||||||
default:
|
|
||||||
msg = fmt.Sprintf("unknown FS magic: %x", stat.Type)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns an object representing the namespace referred to by @path
|
|
||||||
func GetNS(nspath string) (NetNS, error) {
|
|
||||||
isNS, msg, err := IsNS(nspath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if !isNS {
|
|
||||||
return nil, fmt.Errorf("no network namespace detected on %s: %s", nspath, msg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fd, err := os.Open(nspath)
|
_, _, err := syscall.RawSyscall(trap, f.Fd(), flags, 0)
|
||||||
if err != nil {
|
if err != 0 {
|
||||||
return nil, fmt.Errorf("Failed to open %v: %v", nspath, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &netNS{file: fd}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a new persistent network namespace and returns an object
|
|
||||||
// representing that namespace, without switching to it
|
|
||||||
func NewNS() (NetNS, error) {
|
|
||||||
const nsRunDir = "/var/run/netns"
|
|
||||||
|
|
||||||
b := make([]byte, 16)
|
|
||||||
_, err := rand.Reader.Read(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to generate random netns name: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.MkdirAll(nsRunDir, 0755)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// create an empty file at the mount point
|
|
||||||
nsName := fmt.Sprintf("cni-%x-%x-%x-%x-%x", b[0:4], b[4:6], b[6:8], b[8:10], b[10:])
|
|
||||||
nsPath := path.Join(nsRunDir, nsName)
|
|
||||||
mountPointFd, err := os.Create(nsPath)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
mountPointFd.Close()
|
|
||||||
|
|
||||||
// Ensure the mount point is cleaned up on errors; if the namespace
|
|
||||||
// was successfully mounted this will have no effect because the file
|
|
||||||
// is in-use
|
|
||||||
defer os.RemoveAll(nsPath)
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(1)
|
|
||||||
|
|
||||||
// do namespace work in a dedicated goroutine, so that we can safely
|
|
||||||
// Lock/Unlock OSThread without upsetting the lock/unlock state of
|
|
||||||
// the caller of this function
|
|
||||||
var fd *os.File
|
|
||||||
go (func() {
|
|
||||||
defer wg.Done()
|
|
||||||
runtime.LockOSThread()
|
|
||||||
|
|
||||||
var origNS NetNS
|
|
||||||
origNS, err = GetNS(getCurrentThreadNetNSPath())
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer origNS.Close()
|
|
||||||
|
|
||||||
// create a new netns on the current thread
|
|
||||||
err = unix.Unshare(unix.CLONE_NEWNET)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer origNS.Set()
|
|
||||||
|
|
||||||
// bind mount the new netns from the current thread onto the mount point
|
|
||||||
err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND, "")
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fd, err = os.Open(nsPath)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
})()
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
unix.Unmount(nsPath, unix.MNT_DETACH)
|
|
||||||
return nil, fmt.Errorf("failed to create namespace: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &netNS{file: fd, mounted: true}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *netNS) Path() string {
|
|
||||||
return ns.file.Name()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *netNS) Fd() uintptr {
|
|
||||||
return ns.file.Fd()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *netNS) errorIfClosed() error {
|
|
||||||
if ns.closed {
|
|
||||||
return fmt.Errorf("%q has already been closed", ns.file.Name())
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *netNS) Close() error {
|
|
||||||
if err := ns.errorIfClosed(); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ns.file.Close(); err != nil {
|
|
||||||
return fmt.Errorf("Failed to close %q: %v", ns.file.Name(), err)
|
|
||||||
}
|
|
||||||
ns.closed = true
|
|
||||||
|
|
||||||
if ns.mounted {
|
|
||||||
if err := unix.Unmount(ns.file.Name(), unix.MNT_DETACH); err != nil {
|
|
||||||
return fmt.Errorf("Failed to unmount namespace %s: %v", ns.file.Name(), err)
|
|
||||||
}
|
|
||||||
if err := os.RemoveAll(ns.file.Name()); err != nil {
|
|
||||||
return fmt.Errorf("Failed to clean up namespace %s: %v", ns.file.Name(), err)
|
|
||||||
}
|
|
||||||
ns.mounted = false
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *netNS) Do(toRun func(NetNS) error) error {
|
|
||||||
if err := ns.errorIfClosed(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
containedCall := func(hostNS NetNS) error {
|
|
||||||
threadNS, err := GetNS(getCurrentThreadNetNSPath())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to open current netns: %v", err)
|
|
||||||
}
|
|
||||||
defer threadNS.Close()
|
|
||||||
|
|
||||||
// switch to target namespace
|
|
||||||
if err = ns.Set(); err != nil {
|
|
||||||
return fmt.Errorf("error switching to ns %v: %v", ns.file.Name(), err)
|
|
||||||
}
|
|
||||||
defer threadNS.Set() // switch back
|
|
||||||
|
|
||||||
return toRun(hostNS)
|
|
||||||
}
|
|
||||||
|
|
||||||
// save a handle to current network namespace
|
|
||||||
hostNS, err := GetNS(getCurrentThreadNetNSPath())
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed to open current namespace: %v", err)
|
|
||||||
}
|
|
||||||
defer hostNS.Close()
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
wg.Add(1)
|
|
||||||
|
|
||||||
var innerError error
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
runtime.LockOSThread()
|
|
||||||
innerError = containedCall(hostNS)
|
|
||||||
}()
|
|
||||||
wg.Wait()
|
|
||||||
|
|
||||||
return innerError
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ns *netNS) Set() error {
|
|
||||||
if err := ns.errorIfClosed(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, _, err := unix.Syscall(unix.SYS_SETNS, ns.Fd(), uintptr(unix.CLONE_NEWNET), 0); err != 0 {
|
|
||||||
return fmt.Errorf("Error switching to ns %v: %v", ns.file.Name(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// WithNetNSPath executes the passed closure under the given network
|
// WithNetNSPath executes the passed closure under the given network
|
||||||
// namespace, restoring the original namespace afterwards.
|
// namespace, restoring the original namespace afterwards.
|
||||||
func WithNetNSPath(nspath string, toRun func(NetNS) error) error {
|
// Changing namespaces must be done on a goroutine that has been
|
||||||
ns, err := GetNS(nspath)
|
// locked to an OS thread. If lockThread arg is true, this function
|
||||||
|
// locks the goroutine prior to change namespace and unlocks before
|
||||||
|
// returning
|
||||||
|
func WithNetNSPath(nspath string, lockThread bool, f func(*os.File) error) error {
|
||||||
|
ns, err := os.Open(nspath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fmt.Errorf("Failed to open %v: %v", nspath, err)
|
||||||
}
|
}
|
||||||
defer ns.Close()
|
defer ns.Close()
|
||||||
return ns.Do(toRun)
|
return WithNetNS(ns, lockThread, f)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithNetNS executes the passed closure under the given network
|
||||||
|
// namespace, restoring the original namespace afterwards.
|
||||||
|
// Changing namespaces must be done on a goroutine that has been
|
||||||
|
// locked to an OS thread. If lockThread arg is true, this function
|
||||||
|
// locks the goroutine prior to change namespace and unlocks before
|
||||||
|
// returning. If the closure returns an error, WithNetNS attempts to
|
||||||
|
// restore the original namespace before returning.
|
||||||
|
func WithNetNS(ns *os.File, lockThread bool, f func(*os.File) error) error {
|
||||||
|
if lockThread {
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
}
|
||||||
|
// save a handle to current (host) network namespace
|
||||||
|
thisNS, err := os.Open("/proc/self/ns/net")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Failed to open /proc/self/ns/net: %v", err)
|
||||||
|
}
|
||||||
|
defer thisNS.Close()
|
||||||
|
|
||||||
|
if err = SetNS(ns, syscall.CLONE_NEWNET); err != nil {
|
||||||
|
return fmt.Errorf("Error switching to ns %v: %v", ns.Name(), err)
|
||||||
|
}
|
||||||
|
defer SetNS(thisNS, syscall.CLONE_NEWNET) // switch back
|
||||||
|
|
||||||
|
if err = f(thisNS); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,3 @@
|
|||||||
// 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
|
package ns_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,140 +1,135 @@
|
|||||||
// 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
|
package ns_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/appc/cni/pkg/ns"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "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) {
|
func getInode(path string) (uint64, error) {
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
return getInodeFd(int(file.Fd()))
|
return getInodeF(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getInodeFd(fd int) (uint64, error) {
|
func getInodeF(file *os.File) (uint64, error) {
|
||||||
stat := &unix.Stat_t{}
|
stat := &unix.Stat_t{}
|
||||||
err := unix.Fstat(fd, stat)
|
err := unix.Fstat(int(file.Fd()), stat)
|
||||||
return stat.Ino, err
|
return stat.Ino, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const CurrentNetNS = "/proc/self/ns/net"
|
||||||
|
|
||||||
var _ = Describe("Linux namespace operations", func() {
|
var _ = Describe("Linux namespace operations", func() {
|
||||||
Describe("WithNetNS", func() {
|
Describe("WithNetNS", func() {
|
||||||
var (
|
var (
|
||||||
originalNetNS ns.NetNS
|
originalNetNS *os.File
|
||||||
targetNetNS ns.NetNS
|
|
||||||
|
targetNetNSName string
|
||||||
|
targetNetNSPath string
|
||||||
|
targetNetNS *os.File
|
||||||
)
|
)
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
var err error
|
var err error
|
||||||
|
originalNetNS, err = os.Open(CurrentNetNS)
|
||||||
originalNetNS, err = ns.NewNS()
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
targetNetNS, err = ns.NewNS()
|
targetNetNSName = fmt.Sprintf("test-netns-%d", rand.Int())
|
||||||
|
|
||||||
|
err = exec.Command("ip", "netns", "add", targetNetNSName).Run()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
targetNetNSPath = filepath.Join("/var/run/netns/", targetNetNSName)
|
||||||
|
targetNetNS, err = os.Open(targetNetNSPath)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
Expect(targetNetNS.Close()).To(Succeed())
|
Expect(targetNetNS.Close()).To(Succeed())
|
||||||
|
|
||||||
|
err := exec.Command("ip", "netns", "del", targetNetNSName).Run()
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
Expect(originalNetNS.Close()).To(Succeed())
|
Expect(originalNetNS.Close()).To(Succeed())
|
||||||
})
|
})
|
||||||
|
|
||||||
It("executes the callback within the target network namespace", func() {
|
It("executes the callback within the target network namespace", func() {
|
||||||
expectedInode, err := getInodeNS(targetNetNS)
|
expectedInode, err := getInode(targetNetNSPath)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
err = targetNetNS.Do(func(ns.NetNS) error {
|
var actualInode uint64
|
||||||
defer GinkgoRecover()
|
var innerErr error
|
||||||
|
err = ns.WithNetNS(targetNetNS, false, func(*os.File) error {
|
||||||
actualInode, err := getInodeCurNetNS()
|
actualInode, innerErr = getInode(CurrentNetNS)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(actualInode).To(Equal(expectedInode))
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(innerErr).NotTo(HaveOccurred())
|
||||||
|
Expect(actualInode).To(Equal(expectedInode))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("provides the original namespace as the argument to the callback", func() {
|
It("provides the original namespace as the argument to the callback", func() {
|
||||||
// Ensure we start in originalNetNS
|
hostNSInode, err := getInode(CurrentNetNS)
|
||||||
err := originalNetNS.Do(func(ns.NetNS) error {
|
Expect(err).NotTo(HaveOccurred())
|
||||||
defer GinkgoRecover()
|
|
||||||
|
|
||||||
origNSInode, err := getInodeNS(originalNetNS)
|
var inputNSInode uint64
|
||||||
Expect(err).NotTo(HaveOccurred())
|
var innerErr error
|
||||||
|
err = ns.WithNetNS(targetNetNS, false, func(inputNS *os.File) error {
|
||||||
err = targetNetNS.Do(func(hostNS ns.NetNS) error {
|
inputNSInode, err = getInodeF(inputNS)
|
||||||
defer GinkgoRecover()
|
|
||||||
|
|
||||||
hostNSInode, err := getInodeNS(hostNS)
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(hostNSInode).To(Equal(origNSInode))
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(innerErr).NotTo(HaveOccurred())
|
||||||
|
Expect(inputNSInode).To(Equal(hostNSInode))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("restores the calling thread to the original network namespace", func() {
|
||||||
|
preTestInode, err := getInode(CurrentNetNS)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
err = ns.WithNetNS(targetNetNS, false, func(*os.File) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
postTestInode, err := getInode(CurrentNetNS)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(postTestInode).To(Equal(preTestInode))
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("when the callback returns an error", func() {
|
Context("when the callback returns an error", func() {
|
||||||
It("restores the calling thread to the original namespace before returning", func() {
|
It("restores the calling thread to the original namespace before returning", func() {
|
||||||
err := originalNetNS.Do(func(ns.NetNS) error {
|
preTestInode, err := getInode(CurrentNetNS)
|
||||||
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())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
_ = ns.WithNetNS(targetNetNS, false, func(*os.File) error {
|
||||||
|
return errors.New("potato")
|
||||||
|
})
|
||||||
|
|
||||||
|
postTestInode, err := getInode(CurrentNetNS)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(postTestInode).To(Equal(preTestInode))
|
||||||
})
|
})
|
||||||
|
|
||||||
It("returns the error from the callback", func() {
|
It("returns the error from the callback", func() {
|
||||||
err := targetNetNS.Do(func(ns.NetNS) error {
|
err := ns.WithNetNS(targetNetNS, false, func(*os.File) error {
|
||||||
return errors.New("potato")
|
return errors.New("potato")
|
||||||
})
|
})
|
||||||
Expect(err).To(MatchError("potato"))
|
Expect(err).To(MatchError("potato"))
|
||||||
@ -143,104 +138,16 @@ var _ = Describe("Linux namespace operations", func() {
|
|||||||
|
|
||||||
Describe("validating inode mapping to namespaces", func() {
|
Describe("validating inode mapping to namespaces", func() {
|
||||||
It("checks that different namespaces have different inodes", func() {
|
It("checks that different namespaces have different inodes", func() {
|
||||||
origNSInode, err := getInodeNS(originalNetNS)
|
hostNSInode, err := getInode(CurrentNetNS)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
testNsInode, err := getInodeNS(targetNetNS)
|
testNsInode, err := getInode(targetNetNSPath)
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(hostNSInode).NotTo(Equal(0))
|
||||||
Expect(testNsInode).NotTo(Equal(0))
|
Expect(testNsInode).NotTo(Equal(0))
|
||||||
Expect(testNsInode).NotTo(Equal(origNSInode))
|
Expect(testNsInode).NotTo(Equal(hostNSInode))
|
||||||
})
|
})
|
||||||
|
|
||||||
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())
|
|
||||||
errString := fmt.Sprintf("%v", err)
|
|
||||||
Expect(errString).To(HavePrefix("no network namespace detected on %s", nspath))
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
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("IsNS", func() {
|
|
||||||
It("should detect a namespace", func() {
|
|
||||||
createdNetNS, err := ns.NewNS()
|
|
||||||
isNSFS, msg, err := ns.IsNS(createdNetNS.Path())
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(msg).To(Equal(""))
|
|
||||||
Expect(isNSFS).To(Equal(true))
|
|
||||||
})
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
isNSFS, _, err := ns.IsNS(nspath)
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
Expect(isNSFS).To(Equal(false))
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
func allNetNSInCurrentProcess() []string {
|
|
||||||
pid := unix.Getpid()
|
|
||||||
paths, err := filepath.Glob(fmt.Sprintf("/proc/%d/task/*/ns/net", pid))
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
return paths
|
|
||||||
}
|
|
||||||
|
@ -22,7 +22,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CmdArgs captures all the arguments passed in to the plugin
|
// CmdArgs captures all the arguments passed in to the plugin
|
||||||
|
@ -1,17 +1,3 @@
|
|||||||
// 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
|
package skel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,17 +1,3 @@
|
|||||||
// 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
|
package skel
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
// 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()
|
|
||||||
}
|
|
@ -1,23 +1,9 @@
|
|||||||
// 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
|
package types_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
. "github.com/containernetworking/cni/pkg/types"
|
. "github.com/appc/cni/pkg/types"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/ginkgo/extensions/table"
|
. "github.com/onsi/ginkgo/extensions/table"
|
||||||
|
@ -1,17 +1,3 @@
|
|||||||
// 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
|
package types_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
// 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)
|
|
||||||
}
|
|
@ -1,17 +1,3 @@
|
|||||||
// 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
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,17 +1,3 @@
|
|||||||
// 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
|
package utils_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1,17 +1,3 @@
|
|||||||
// 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
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -27,8 +27,8 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/appc/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
"github.com/coreos/go-systemd/activation"
|
"github.com/coreos/go-systemd/activation"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -26,8 +27,8 @@ import (
|
|||||||
"github.com/d2g/dhcp4client"
|
"github.com/d2g/dhcp4client"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
"github.com/appc/cni/pkg/ns"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RFC 2131 suggests using exponential backoff, starting with 4sec
|
// RFC 2131 suggests using exponential backoff, starting with 4sec
|
||||||
@ -73,7 +74,7 @@ func AcquireLease(clientID, netns, ifName string) (*DHCPLease, error) {
|
|||||||
|
|
||||||
l.wg.Add(1)
|
l.wg.Add(1)
|
||||||
go func() {
|
go func() {
|
||||||
errCh <- ns.WithNetNSPath(netns, func(_ ns.NetNS) error {
|
errCh <- ns.WithNetNSPath(netns, true, func(_ *os.File) error {
|
||||||
defer l.wg.Done()
|
defer l.wg.Done()
|
||||||
|
|
||||||
link, err := netlink.LinkByName(ifName)
|
link, err := netlink.LinkByName(ifName)
|
||||||
|
@ -20,8 +20,8 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/appc/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const socketPath = "/run/cni/dhcp.sock"
|
const socketPath = "/run/cni/dhcp.sock"
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
"github.com/d2g/dhcp4"
|
"github.com/d2g/dhcp4"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
"github.com/d2g/dhcp4"
|
"github.com/d2g/dhcp4"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -18,9 +18,9 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ip"
|
"github.com/appc/cni/pkg/ip"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/plugins/ipam/host-local/backend"
|
"github.com/appc/cni/plugins/ipam/host-local/backend"
|
||||||
)
|
)
|
||||||
|
|
||||||
type IPAllocator struct {
|
type IPAllocator struct {
|
||||||
|
@ -19,7 +19,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IPAMConfig represents the IP related network configuration.
|
// IPAMConfig represents the IP related network configuration.
|
||||||
|
@ -15,10 +15,10 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/containernetworking/cni/plugins/ipam/host-local/backend/disk"
|
"github.com/appc/cni/plugins/ipam/host-local/backend/disk"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/appc/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
@ -19,15 +19,16 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
"syscall"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ip"
|
"github.com/appc/cni/pkg/ip"
|
||||||
"github.com/containernetworking/cni/pkg/ipam"
|
"github.com/appc/cni/pkg/ipam"
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
"github.com/appc/cni/pkg/ns"
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/appc/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/utils"
|
"github.com/appc/cni/pkg/utils"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -35,12 +36,10 @@ const defaultBrName = "cni0"
|
|||||||
|
|
||||||
type NetConf struct {
|
type NetConf struct {
|
||||||
types.NetConf
|
types.NetConf
|
||||||
BrName string `json:"bridge"`
|
BrName string `json:"bridge"`
|
||||||
IsGW bool `json:"isGateway"`
|
IsGW bool `json:"isGateway"`
|
||||||
IsDefaultGW bool `json:"isDefaultGateway"`
|
IPMasq bool `json:"ipMasq"`
|
||||||
IPMasq bool `json:"ipMasq"`
|
MTU int `json:"mtu"`
|
||||||
MTU int `json:"mtu"`
|
|
||||||
HairpinMode bool `json:"hairpinMode"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -102,11 +101,6 @@ func ensureBridge(brName string, mtu int) (*netlink.Bridge, error) {
|
|||||||
LinkAttrs: netlink.LinkAttrs{
|
LinkAttrs: netlink.LinkAttrs{
|
||||||
Name: brName,
|
Name: brName,
|
||||||
MTU: mtu,
|
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,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -129,10 +123,10 @@ func ensureBridge(brName string, mtu int) (*netlink.Bridge, error) {
|
|||||||
return br, nil
|
return br, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairpinMode bool) error {
|
func setupVeth(netns string, br *netlink.Bridge, ifName string, mtu int) error {
|
||||||
var hostVethName string
|
var hostVethName string
|
||||||
|
|
||||||
err := netns.Do(func(hostNS ns.NetNS) error {
|
err := ns.WithNetNSPath(netns, false, func(hostNS *os.File) error {
|
||||||
// create the veth pair in the container and move host end into host netns
|
// create the veth pair in the container and move host end into host netns
|
||||||
hostVeth, _, err := ip.SetupVeth(ifName, mtu, hostNS)
|
hostVeth, _, err := ip.SetupVeth(ifName, mtu, hostNS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -157,11 +151,6 @@ func setupVeth(netns ns.NetNS, br *netlink.Bridge, ifName string, mtu int, hairp
|
|||||||
return fmt.Errorf("failed to connect %q to bridge %v: %v", hostVethName, br.Attrs().Name, err)
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,22 +175,12 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.IsDefaultGW {
|
|
||||||
n.IsGW = true
|
|
||||||
}
|
|
||||||
|
|
||||||
br, err := setupBridge(n)
|
br, err := setupBridge(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
netns, err := ns.GetNS(args.Netns)
|
if err = setupVeth(args.Netns, br, args.IfName, n.MTU); err != nil {
|
||||||
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
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +190,6 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make this optional when IPv6 is supported
|
|
||||||
if result.IP4 == nil {
|
if result.IP4 == nil {
|
||||||
return errors.New("IPAM plugin returned missing IPv4 config")
|
return errors.New("IPAM plugin returned missing IPv4 config")
|
||||||
}
|
}
|
||||||
@ -220,35 +198,10 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
result.IP4.Gateway = calcGatewayIP(&result.IP4.IP)
|
result.IP4.Gateway = calcGatewayIP(&result.IP4.IP)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := netns.Do(func(_ ns.NetNS) error {
|
err = ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) 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
|
|
||||||
}
|
|
||||||
|
|
||||||
return ipam.ConfigureIface(args.IfName, result)
|
return ipam.ConfigureIface(args.IfName, result)
|
||||||
}); err != nil {
|
})
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,7 +243,7 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ipn *net.IPNet
|
var ipn *net.IPNet
|
||||||
err = ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
err = ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) error {
|
||||||
var err error
|
var err error
|
||||||
ipn, err = ip.DelLinkByNameAddr(args.IfName, netlink.FAMILY_V4)
|
ipn, err = ip.DelLinkByNameAddr(args.IfName, netlink.FAMILY_V4)
|
||||||
return err
|
return err
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
// 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")
|
|
||||||
}
|
|
@ -1,239 +0,0 @@
|
|||||||
// 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/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{
|
|
||||||
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{
|
|
||||||
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(`{
|
|
||||||
"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 {
|
|
||||||
if 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))
|
|
||||||
|
|
||||||
// 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())
|
|
||||||
})
|
|
||||||
})
|
|
@ -18,13 +18,14 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ip"
|
"github.com/appc/cni/pkg/ip"
|
||||||
"github.com/containernetworking/cni/pkg/ipam"
|
"github.com/appc/cni/pkg/ipam"
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
"github.com/appc/cni/pkg/ns"
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/appc/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -64,7 +65,7 @@ func modeFromString(s string) (netlink.IPVlanMode, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createIpvlan(conf *NetConf, ifName string, netns ns.NetNS) error {
|
func createIpvlan(conf *NetConf, ifName string, netns *os.File) error {
|
||||||
mode, err := modeFromString(conf.Mode)
|
mode, err := modeFromString(conf.Mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -96,7 +97,7 @@ func createIpvlan(conf *NetConf, ifName string, netns ns.NetNS) error {
|
|||||||
return fmt.Errorf("failed to create ipvlan: %v", err)
|
return fmt.Errorf("failed to create ipvlan: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return netns.Do(func(_ ns.NetNS) error {
|
return ns.WithNetNS(netns, false, func(_ *os.File) error {
|
||||||
err := renameLink(tmpName, ifName)
|
err := renameLink(tmpName, ifName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to rename ipvlan to %q: %v", ifName, err)
|
return fmt.Errorf("failed to rename ipvlan to %q: %v", ifName, err)
|
||||||
@ -111,9 +112,9 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
netns, err := ns.GetNS(args.Netns)
|
netns, err := os.Open(args.Netns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to open netns %q: %v", args.Netns, err)
|
return fmt.Errorf("failed to open netns %q: %v", netns, err)
|
||||||
}
|
}
|
||||||
defer netns.Close()
|
defer netns.Close()
|
||||||
|
|
||||||
@ -130,7 +131,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
return errors.New("IPAM plugin returned missing IPv4 config")
|
return errors.New("IPAM plugin returned missing IPv4 config")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = netns.Do(func(_ ns.NetNS) error {
|
err = ns.WithNetNS(netns, false, func(_ *os.File) error {
|
||||||
return ipam.ConfigureIface(args.IfName, result)
|
return ipam.ConfigureIface(args.IfName, result)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -152,7 +153,7 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
return ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) error {
|
||||||
return ip.DelLinkByName(args.IfName)
|
return ip.DelLinkByName(args.IfName)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
// 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")
|
|
||||||
}
|
|
@ -1,168 +0,0 @@
|
|||||||
// 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{
|
|
||||||
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(`{
|
|
||||||
"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())
|
|
||||||
})
|
|
||||||
})
|
|
@ -1,29 +1,17 @@
|
|||||||
// 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
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
"os"
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/ns"
|
||||||
|
"github.com/appc/cni/pkg/skel"
|
||||||
|
"github.com/appc/cni/pkg/types"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
func cmdAdd(args *skel.CmdArgs) error {
|
func cmdAdd(args *skel.CmdArgs) error {
|
||||||
args.IfName = "lo" // ignore config, this only works for loopback
|
args.IfName = "lo" // ignore config, this only works for loopback
|
||||||
err := ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
err := ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) error {
|
||||||
link, err := netlink.LinkByName(args.IfName)
|
link, err := netlink.LinkByName(args.IfName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err // not tested
|
return err // not tested
|
||||||
@ -46,7 +34,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
|
|
||||||
func cmdDel(args *skel.CmdArgs) error {
|
func cmdDel(args *skel.CmdArgs) error {
|
||||||
args.IfName = "lo" // ignore config, this only works for loopback
|
args.IfName = "lo" // ignore config, this only works for loopback
|
||||||
err := ns.WithNetNSPath(args.Netns, func(ns.NetNS) error {
|
err := ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) error {
|
||||||
link, err := netlink.LinkByName(args.IfName)
|
link, err := netlink.LinkByName(args.IfName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err // not tested
|
return err // not tested
|
||||||
|
@ -1,26 +1,18 @@
|
|||||||
// 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
|
package main_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
"github.com/onsi/gomega/gexec"
|
"github.com/onsi/gomega/gexec"
|
||||||
|
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
var pathToLoPlugin string
|
var pathToLoPlugin string
|
||||||
@ -32,10 +24,54 @@ func TestLoopback(t *testing.T) {
|
|||||||
|
|
||||||
var _ = BeforeSuite(func() {
|
var _ = BeforeSuite(func() {
|
||||||
var err error
|
var err error
|
||||||
pathToLoPlugin, err = gexec.Build("github.com/containernetworking/cni/plugins/main/loopback")
|
pathToLoPlugin, err = gexec.Build("github.com/appc/cni/plugins/main/loopback")
|
||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
})
|
})
|
||||||
|
|
||||||
var _ = AfterSuite(func() {
|
var _ = AfterSuite(func() {
|
||||||
gexec.CleanupBuildArtifacts()
|
gexec.CleanupBuildArtifacts()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
func makeNetworkNS(containerID string) string {
|
||||||
|
namespace := "/var/run/netns/" + containerID
|
||||||
|
pid := unix.Getpid()
|
||||||
|
tid := unix.Gettid()
|
||||||
|
|
||||||
|
err := os.MkdirAll("/var/run/netns", 0600)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
runtime.LockOSThread()
|
||||||
|
defer runtime.UnlockOSThread()
|
||||||
|
go (func() {
|
||||||
|
defer GinkgoRecover()
|
||||||
|
|
||||||
|
err = unix.Unshare(unix.CLONE_NEWNET)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
fd, err := os.Create(namespace)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
defer fd.Close()
|
||||||
|
|
||||||
|
err = unix.Mount("/proc/self/ns/net", namespace, "none", unix.MS_BIND, "")
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
})()
|
||||||
|
|
||||||
|
Eventually(namespace).Should(BeAnExistingFile())
|
||||||
|
|
||||||
|
fd, err := unix.Open(fmt.Sprintf("/proc/%d/task/%d/ns/net", pid, tid), unix.O_RDONLY, 0)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
|
||||||
|
defer unix.Close(fd)
|
||||||
|
|
||||||
|
_, _, e1 := unix.Syscall(unix.SYS_SETNS, uintptr(fd), uintptr(unix.CLONE_NEWNET), 0)
|
||||||
|
Expect(e1).To(BeZero())
|
||||||
|
|
||||||
|
return namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeNetworkNS(networkNS string) error {
|
||||||
|
err := unix.Unmount(networkNS, unix.MNT_DETACH)
|
||||||
|
|
||||||
|
err = os.RemoveAll(networkNS)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
@ -1,26 +1,13 @@
|
|||||||
// 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
|
package main_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
"github.com/appc/cni/pkg/ns"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/onsi/gomega/gbytes"
|
"github.com/onsi/gomega/gbytes"
|
||||||
@ -29,7 +16,7 @@ import (
|
|||||||
|
|
||||||
var _ = Describe("Loopback", func() {
|
var _ = Describe("Loopback", func() {
|
||||||
var (
|
var (
|
||||||
networkNS ns.NetNS
|
networkNS string
|
||||||
containerID string
|
containerID string
|
||||||
command *exec.Cmd
|
command *exec.Cmd
|
||||||
environ []string
|
environ []string
|
||||||
@ -37,14 +24,12 @@ var _ = Describe("Loopback", func() {
|
|||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
command = exec.Command(pathToLoPlugin)
|
command = exec.Command(pathToLoPlugin)
|
||||||
|
containerID = "some-container-id"
|
||||||
var err error
|
networkNS = makeNetworkNS(containerID)
|
||||||
networkNS, err = ns.NewNS()
|
|
||||||
Expect(err).NotTo(HaveOccurred())
|
|
||||||
|
|
||||||
environ = []string{
|
environ = []string{
|
||||||
fmt.Sprintf("CNI_CONTAINERID=%s", containerID),
|
fmt.Sprintf("CNI_CONTAINERID=%s", containerID),
|
||||||
fmt.Sprintf("CNI_NETNS=%s", networkNS.Path()),
|
fmt.Sprintf("CNI_NETNS=%s", networkNS),
|
||||||
fmt.Sprintf("CNI_IFNAME=%s", "this is ignored"),
|
fmt.Sprintf("CNI_IFNAME=%s", "this is ignored"),
|
||||||
fmt.Sprintf("CNI_ARGS=%s", "none"),
|
fmt.Sprintf("CNI_ARGS=%s", "none"),
|
||||||
fmt.Sprintf("CNI_PATH=%s", "/some/test/path"),
|
fmt.Sprintf("CNI_PATH=%s", "/some/test/path"),
|
||||||
@ -53,7 +38,7 @@ var _ = Describe("Loopback", func() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
AfterEach(func() {
|
AfterEach(func() {
|
||||||
Expect(networkNS.Close()).To(Succeed())
|
Expect(removeNetworkNS(networkNS)).To(Succeed())
|
||||||
})
|
})
|
||||||
|
|
||||||
Context("when given a network namespace", func() {
|
Context("when given a network namespace", func() {
|
||||||
@ -67,7 +52,7 @@ var _ = Describe("Loopback", func() {
|
|||||||
Eventually(session).Should(gexec.Exit(0))
|
Eventually(session).Should(gexec.Exit(0))
|
||||||
|
|
||||||
var lo *net.Interface
|
var lo *net.Interface
|
||||||
err = networkNS.Do(func(ns.NetNS) error {
|
err = ns.WithNetNSPath(networkNS, true, func(hostNS *os.File) error {
|
||||||
var err error
|
var err error
|
||||||
lo, err = net.InterfaceByName("lo")
|
lo, err = net.InterfaceByName("lo")
|
||||||
return err
|
return err
|
||||||
@ -87,7 +72,7 @@ var _ = Describe("Loopback", func() {
|
|||||||
Eventually(session).Should(gexec.Exit(0))
|
Eventually(session).Should(gexec.Exit(0))
|
||||||
|
|
||||||
var lo *net.Interface
|
var lo *net.Interface
|
||||||
err = networkNS.Do(func(ns.NetNS) error {
|
err = ns.WithNetNSPath(networkNS, true, func(hostNS *os.File) error {
|
||||||
var err error
|
var err error
|
||||||
lo, err = net.InterfaceByName("lo")
|
lo, err = net.InterfaceByName("lo")
|
||||||
return err
|
return err
|
||||||
|
@ -18,21 +18,17 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ip"
|
"github.com/appc/cni/pkg/ip"
|
||||||
"github.com/containernetworking/cni/pkg/ipam"
|
"github.com/appc/cni/pkg/ipam"
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
"github.com/appc/cni/pkg/ns"
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/appc/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/utils/sysctl"
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
IPv4InterfaceArpProxySysctlTemplate = "net.ipv4.conf.%s.proxy_arp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type NetConf struct {
|
type NetConf struct {
|
||||||
types.NetConf
|
types.NetConf
|
||||||
Master string `json:"master"`
|
Master string `json:"master"`
|
||||||
@ -73,7 +69,7 @@ func modeFromString(s string) (netlink.MacvlanMode, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) error {
|
func createMacvlan(conf *NetConf, ifName string, netns *os.File) error {
|
||||||
mode, err := modeFromString(conf.Mode)
|
mode, err := modeFromString(conf.Mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -84,7 +80,7 @@ func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) error {
|
|||||||
return fmt.Errorf("failed to lookup master %q: %v", conf.Master, err)
|
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
|
// due to kernel bug we have to create with tmpname or it might
|
||||||
// collide with the name on the host and error out
|
// collide with the name on the host and error out
|
||||||
tmpName, err := ip.RandomVethName()
|
tmpName, err := ip.RandomVethName()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -105,18 +101,9 @@ func createMacvlan(conf *NetConf, ifName string, netns ns.NetNS) error {
|
|||||||
return fmt.Errorf("failed to create macvlan: %v", err)
|
return fmt.Errorf("failed to create macvlan: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return netns.Do(func(_ ns.NetNS) error {
|
return ns.WithNetNS(netns, false, func(_ *os.File) 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)
|
err := renameLink(tmpName, ifName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
_ = netlink.LinkDel(mv)
|
|
||||||
return fmt.Errorf("failed to rename macvlan to %q: %v", ifName, err)
|
return fmt.Errorf("failed to rename macvlan to %q: %v", ifName, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -129,7 +116,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
netns, err := ns.GetNS(args.Netns)
|
netns, err := os.Open(args.Netns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to open netns %q: %v", netns, err)
|
return fmt.Errorf("failed to open netns %q: %v", netns, err)
|
||||||
}
|
}
|
||||||
@ -148,7 +135,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
return errors.New("IPAM plugin returned missing IPv4 config")
|
return errors.New("IPAM plugin returned missing IPv4 config")
|
||||||
}
|
}
|
||||||
|
|
||||||
err = netns.Do(func(_ ns.NetNS) error {
|
err = ns.WithNetNS(netns, false, func(_ *os.File) error {
|
||||||
return ipam.ConfigureIface(args.IfName, result)
|
return ipam.ConfigureIface(args.IfName, result)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -170,7 +157,7 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
return ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) error {
|
||||||
return ip.DelLinkByName(args.IfName)
|
return ip.DelLinkByName(args.IfName)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
// 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")
|
|
||||||
}
|
|
@ -1,168 +0,0 @@
|
|||||||
// 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("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{
|
|
||||||
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(`{
|
|
||||||
"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))
|
|
||||||
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())
|
|
||||||
})
|
|
||||||
})
|
|
@ -24,12 +24,12 @@ import (
|
|||||||
|
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ip"
|
"github.com/appc/cni/pkg/ip"
|
||||||
"github.com/containernetworking/cni/pkg/ipam"
|
"github.com/appc/cni/pkg/ipam"
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
"github.com/appc/cni/pkg/ns"
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/appc/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
"github.com/containernetworking/cni/pkg/utils"
|
"github.com/appc/cni/pkg/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -58,7 +58,7 @@ func setupContainerVeth(netns, ifName string, mtu int, pr *types.Result) (string
|
|||||||
// In other words we force all traffic to ARP via the gateway except for GW itself.
|
// In other words we force all traffic to ARP via the gateway except for GW itself.
|
||||||
|
|
||||||
var hostVethName string
|
var hostVethName string
|
||||||
err := ns.WithNetNSPath(netns, func(hostNS ns.NetNS) error {
|
err := ns.WithNetNSPath(netns, false, func(hostNS *os.File) error {
|
||||||
hostVeth, _, err := ip.SetupVeth(ifName, mtu, hostNS)
|
hostVeth, _, err := ip.SetupVeth(ifName, mtu, hostNS)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -200,7 +200,7 @@ func cmdDel(args *skel.CmdArgs) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var ipn *net.IPNet
|
var ipn *net.IPNet
|
||||||
err := ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
err := ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) error {
|
||||||
var err error
|
var err error
|
||||||
ipn, err = ip.DelLinkByNameAddr(args.IfName, netlink.FAMILY_V4)
|
ipn, err = ip.DelLinkByNameAddr(args.IfName, netlink.FAMILY_V4)
|
||||||
return err
|
return err
|
||||||
|
@ -29,9 +29,9 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/invoke"
|
"github.com/appc/cni/pkg/invoke"
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/appc/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -21,12 +21,13 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containernetworking/cni/pkg/ns"
|
"github.com/appc/cni/pkg/ns"
|
||||||
"github.com/containernetworking/cni/pkg/skel"
|
"github.com/appc/cni/pkg/skel"
|
||||||
"github.com/containernetworking/cni/pkg/types"
|
"github.com/appc/cni/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TuningConf represents the network tuning configuration.
|
// TuningConf represents the network tuning configuration.
|
||||||
@ -44,7 +45,7 @@ func cmdAdd(args *skel.CmdArgs) error {
|
|||||||
// The directory /proc/sys/net is per network namespace. Enter in the
|
// The directory /proc/sys/net is per network namespace. Enter in the
|
||||||
// network namespace before writing on it.
|
// network namespace before writing on it.
|
||||||
|
|
||||||
err := ns.WithNetNSPath(args.Netns, func(_ ns.NetNS) error {
|
err := ns.WithNetNSPath(args.Netns, false, func(hostNS *os.File) error {
|
||||||
for key, value := range tuningConf.SysCtl {
|
for key, value := range tuningConf.SysCtl {
|
||||||
fileName := filepath.Join("/proc/sys", strings.Replace(key, ".", "/", -1))
|
fileName := filepath.Join("/proc/sys", strings.Replace(key, ".", "/", -1))
|
||||||
fileName = filepath.Clean(fileName)
|
fileName = filepath.Clean(fileName)
|
||||||
|
18
test
18
test
@ -11,8 +11,8 @@ set -e
|
|||||||
|
|
||||||
source ./build
|
source ./build
|
||||||
|
|
||||||
TESTABLE="plugins/ipam/dhcp plugins/main/loopback pkg/invoke pkg/ns pkg/skel pkg/types pkg/utils plugins/main/ipvlan plugins/main/macvlan plugins/main/bridge"
|
TESTABLE="plugins/ipam/dhcp plugins/main/loopback pkg/invoke pkg/ns pkg/skel pkg/types pkg/utils"
|
||||||
FORMATTABLE="$TESTABLE libcni pkg/ip pkg/ipam pkg/testutils plugins/ipam/host-local plugins/main/bridge plugins/meta/flannel plugins/meta/tuning"
|
FORMATTABLE="$TESTABLE libcni pkg/ip pkg/ns pkg/types pkg/ipam plugins/ipam/host-local plugins/main/bridge plugins/meta/flannel plugins/meta/tuning"
|
||||||
|
|
||||||
# user has not provided PKG override
|
# user has not provided PKG override
|
||||||
if [ -z "$PKG" ]; then
|
if [ -z "$PKG" ]; then
|
||||||
@ -35,7 +35,7 @@ TEST=${split[@]/#/${REPO_PATH}/}
|
|||||||
|
|
||||||
echo -n "Running tests "
|
echo -n "Running tests "
|
||||||
function testrun {
|
function testrun {
|
||||||
sudo -E bash -c "umask 0; PATH=$GOROOT/bin:$GOBIN:$PATH go test -covermode set $@"
|
sudo -E bash -c "umask 0; PATH=\$GOROOT/bin:\$PATH go test -covermode set $@"
|
||||||
}
|
}
|
||||||
if [ ! -z "${COVERALLS}" ]; then
|
if [ ! -z "${COVERALLS}" ]; then
|
||||||
echo "with coverage profile generation..."
|
echo "with coverage profile generation..."
|
||||||
@ -65,16 +65,4 @@ if [ -n "${vetRes}" ]; then
|
|||||||
exit 255
|
exit 255
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Checking license header..."
|
|
||||||
licRes=$(
|
|
||||||
for file in $(find . -type f -iname '*.go' ! -path './vendor/*'); do
|
|
||||||
head -n1 "${file}" | grep -Eq "(Copyright|generated)" || echo -e " ${file}"
|
|
||||||
done
|
|
||||||
)
|
|
||||||
if [ -n "${licRes}" ]; then
|
|
||||||
echo -e "license header checking failed:\n${licRes}"
|
|
||||||
exit 255
|
|
||||||
fi
|
|
||||||
|
|
||||||
|
|
||||||
echo "Success"
|
echo "Success"
|
||||||
|
Reference in New Issue
Block a user