77 Commits

Author SHA1 Message Date
Madhu Venugopal 3daf672705 Merge pull request #354 from aboch/docker1.7.0_integ
Interface Statistics() API
2015-06-30 17:13:21 -07:00
Alessandro Boch 96afee8b4c Interface Statistics() API
- Reworked implementation for docker_1.7.0_integ branch

Signed-off-by: Alessandro Boch <aboch@docker.com>
2015-06-30 16:27:06 -07:00
Jana Radhakrishnan fc286182cd Merge pull request #353 from mavenugo/170integ_Godeps
Updating to latest netns to fix amd64 / RPI issues
2015-06-30 15:38:00 -07:00
Madhu Venugopal d82b7a9573 Updating to latest netns to fix amd64 / RPI issues
Signed-off-by: Madhu Venugopal <madhu@docker.com>
2015-06-30 15:27:12 -07:00
aboch 75716d8756 Merge pull request #351 from mrjana/docker1.7.0_integ
Honor driver side resolv.conf file
2015-06-30 15:26:47 -07:00
Jana Radhakrishnan 0c4a707c98 Honor driver side resolv.conf file
For the moment in 1.7.1 since we provide a resolv.conf set api
to the driver honor that so that for host driver we can use the
the host's /etc/resolv.conf file as is rather than putting the
contents through a filtering logic.

It should be noted that the driver side capability to set the
resolv.conf file is most likely going to go away in the future
but this should be fine for 1.7.1

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-06-30 13:50:32 -07:00
aboch 4d49958029 Merge pull request #350 from mrjana/docker1.7.0_integ
Update netlink and manually bring up host side veth interface
2015-06-30 13:30:35 -07:00
Jana Radhakrishnan eb4ecdf537 Update vishvananda/netlink package
PR to update to vishvananda/netlink package

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-06-30 11:10:08 -07:00
Jana Radhakrishnan 30d1b7861f Manually bring up the host side veth interface
In preparation for the new update of vishvananda/netlink package
we need to bringup the host veth interface manually.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-06-30 11:08:16 -07:00
Madhu Venugopal 7465ee11d3 Merge pull request #349 from mrjana/docker1.7.0_integ
Fix networking issues in RHEL/Centos 6.6
2015-06-30 09:02:01 -07:00
Jana Radhakrishnan 6cacb70196 Fix networking issues in RHEL/Centos 6.6
Some parts of the bridge driver code needs to use a different kernel
api or use the already existing apis in slightly different ways to
make the bridge driver work in RHEL/Centos 6.6. This PR provides
those fixes.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-06-29 23:10:30 -07:00
Madhu Venugopal cb9c2bfaa2 Merge pull request #315 from mrjana/docker1.7.0_integ
Use ioctl to create bridge
2015-06-18 16:25:50 -07:00
Jana Radhakrishnan 36bcacd1c6 Use ioctls to create bridge
The netlink way of creating bridge has problems in older
kernels like the one used on RHEL 6 (which is a supported
one). So trying to use ioctl method to create bridge
so that it works on any version.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-06-18 16:04:06 -07:00
Jana Radhakrishnan 7b9631cd46 Updated Godeps
Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-06-18 16:04:06 -07:00
Madhu Venugopal b116b5c0d2 Merge pull request #303 from icecrime/cherry-picks-1.7.0
Cherry picks 1.7.0
2015-06-16 11:28:50 -07:00
Madhu Venugopal 78fef19fc6 Fixed the tests.
Signed-off-by: Madhu Venugopal <madhu@docker.com>
2015-06-16 11:23:13 -07:00
Arnaud Porterie 6c07f504c3 Fix duplicated iptables rules
The `iptables.Exists` function is wrong in two ways:
1. The iptables -C call doesn't add `-j DOCKER` and fails to match
2. The long path takes ordering into account in comparison and fails to match

This patch fixes issue 1 by including `-j DOCKER` in the check.

Signed-off-by: Arnaud Porterie <arnaud.porterie@docker.com>
2015-06-16 11:23:08 -07:00
Madhu Venugopal c32ef60c4b Cleaning up iptables nat table on driver bootup
This is required to have consistent behaviour as in 1.6.2.

Signed-off-by: Madhu Venugopal <madhu@docker.com>

Conflicts:
	drivers/bridge/bridge.go
2015-06-16 10:41:17 -07:00
Jana Radhakrishnan e578e95aa1 Merge pull request #281 from mavenugo/hairpin
enable hairpin mode on the bridge port & fix iptables rule
2015-06-11 17:25:09 -07:00
Madhu Venugopal 9548cbe674 enable hairpin mode on the bridge port & fix iptables rule
* When userland-proxy is disabled, enable hairpin mode on the host-side of the veth
* When userland-proxy is enabled, fix the iptable rules appropriately

Signed-off-by: Madhu Venugopal <madhu@docker.com>
2015-06-11 17:06:07 -07:00
Jana Radhakrishnan 90638ec9cf Merge pull request #275 from mrjana/docker1.7.0_integ
Check GC loop is active/necessary before triggering GC
2015-06-10 15:05:51 -07:00
Phil Estes 22f8e6bbab Check GC loop is active/necessary before triggering GC
Calling GC() without ever creating a network namespace (sandbox on
Linux) will hang as the GC loop is not running (and therefore the
channel is not being listened to).

Tested via Docker that this corrects a daemon shutdown error if the
daemon is started and stopped without any containers or networks being
created while the daemon is up.

Docker-DCO-1.1-Signed-off-by: Phil Estes <estesp@linux.vnet.ibm.com>
2015-06-10 14:54:08 -07:00
Madhu Venugopal 8b3374153e Merge pull request #274 from mrjana/docker1.7.0_integ
Generate container mac address based on IP
2015-06-10 14:43:19 -07:00
Jana Radhakrishnan 9c6e929b63 Generate container mac address based on IP
Currently we craete container mac address completely
randomly. But we probably need to generate based on
IP so that the mac address stays the same for a given
IP.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-06-10 14:21:24 -07:00
Madhu Venugopal ace6b57623 Merge pull request #263 from mrjana/docker1.7.0_integ
Add support to trigger immediate garbage collection
2015-06-05 14:57:53 -07:00
Jana Radhakrishnan 68d42b860c Add support to trigger immediate garbage collection
Right now the namespace paths are cleaned up every
garbage collection period. But if the daemon is restarted
before all the namespace paths of removed containers are
garbage collected they will remain there forever. The fix
is to provide a GC() api so that garbage collection can be
triggered immediately.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-06-05 14:46:46 -07:00
Madhu Venugopal c624d72ce9 Merge pull request #261 from mavenugo/docker1.7.0_integ
Update netns to include support for PowerPC LE (ppc64le) architecture
2015-06-05 14:22:36 -07:00
Pradipta Kr. Banerjee 6532b64a07 Update netns to include support for PowerPC LE (ppc64le) architecture
Current version of netns used in libnetwork do not have requisite syscall
entry for PowerPC (ppc64le) arch. Consequently docker which uses libnetwork fails
to create any network enabled containers on Power systems.

This patch updates netns to latest commit 5478c060110032f972e86a1f844fdb9a2f008f2c
to add ppc64le syscall entry.

Signed-off-by: Pradipta Kr. Banerjee <bpradip@in.ibm.com>
2015-06-05 13:09:25 -07:00
Jana Radhakrishnan f72ad20491 Merge pull request #254 from aboch/docker1.7.0_integ
Change in bridge EndpointOperInfo()
2015-06-03 21:09:44 -07:00
Alessandro Boch 2133cdc219 Change in bridge EndpointOperInfo()
Signed-off-by: Alessandro Boch <aboch@docker.com>
2015-06-03 20:52:23 -07:00
Jana Radhakrishnan 005bc475ee Merge pull request #247 from mrjana/docker1.7.0_integ
Do not warn in packages
2015-06-02 16:37:19 -07:00
Michael Crosby d1d67dca84 Do not warn in packages
Do not have log output in packages that applications consume because the
output can mess with logs, stdout/stderr of applications and such and
there is nothing that the consumer can do about it other than change the
package that they are using.

Signed-off-by: Michael Crosby <crosbymichael@gmail.com>
2015-06-02 16:28:36 -07:00
Jana Radhakrishnan 4ded6fe364 Merge pull request #231 from mavenugo/1.7.0
Fixes https://github.com/docker/docker/issues/13426
2015-05-29 12:49:47 -07:00
Madhu Venugopal effb423db7 Fixes https://github.com/docker/docker/issues/13426
Signed-off-by: Madhu Venugopal <madhu@docker.com>
2015-05-29 11:22:52 -07:00
Madhu Venugopal a5ac79f562 Merge pull request #230 from mrjana/docker1.7.0_integ
Fix miscellaneous data races
2015-05-28 17:30:50 -07:00
Jana Radhakrishnan 694754890c Fix miscellaneaus data races
Fixed the remaining data races in the libnetwork code.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-05-28 23:29:21 +00:00
Madhu Venugopal 7c2a2c8a87 Merge pull request #229 from mrjana/docker1.7.0_integ
Modprobe bridge driver specific kernel modules
2015-05-28 14:30:29 -07:00
Jana Radhakrishnan 1ab46c7c4b Modprobe bridge driver r specific kernel modules
Try too modprobe bridge driverer specic modulein case
they are not loaded into the kernel.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-05-28 20:01:00 +00:00
Madhu Venugopal 2da2dc055d Merge pull request #226 from mrjana/docker1.7.0_integ
Remove the init time cleanup of namespace files
2015-05-27 16:59:45 -07:00
Jana Radhakrishnan 83799f7458 Removee the init time cleanup of namespace files
Removing this as this may cause problems when
multiple instances are e running.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-05-27 23:49:32 +00:00
Madhu Venugopal 60da50e183 Merge pull request #225 from mrjana/docker1.7.0_integ
Rework garbage collection code to use tick
2015-05-27 15:16:57 -07:00
Jana Radhakrishnan b028371e0a Reworkkgarbage collection code to use tick
Instead of sleeping reworked the code to use recurring ticks.
Also cleaned up unnecessary defers.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-05-27 21:52:21 +00:00
Madhu Venugopal fc2dfe5201 Merge pull request #224 from mrjana/docker1.7.0_integ
Loopback interface not  brought up
2015-05-27 13:44:37 -07:00
Jana Radhakrishnan 051f4ccdad Loopback interface not t brought up
Loopback interface was s not brought up when wemoved
to clone method of creating namespace. e. Adding it.
Also taking care of PR R comments.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-05-27 20:20:24 +00:00
Madhu Venugopal a4d595b6e7 Merge pull request #223 from mrjana/docker1.7.0_integ
Workaround kernel bugs s related to namespaces
2015-05-27 12:23:27 -07:00
Jana Radhakrishnan 05462c27b2 Workaround kernel bugs s related to namespaces
This PR attempts to work around bugs present in kernel
version 3.18-4.0.1 relating to namespace creation
and destruction. This fix attempts to avoid certain
systemmcalls to not get in the kkernel bug path as well
as lazily garbage collecting the name paths when they are removed.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-05-27 19:06:19 +00:00
Jana Radhakrishnan 6743808072 Merge pull request #206 from mavenugo/experimental
Moved all the service commands under experimental build tag
2015-05-23 18:02:57 -07:00
Jana Radhakrishnan 8b61195508 Merge pull request #205 from mavenugo/api_version
Added a catch-all root hierarchy for the API path
2015-05-23 18:02:10 -07:00
Madhu Venugopal 74c93b8c68 UI formatting applied on top of Experimental Service PR
Thanks to @nerdalert for the contribution via #203.

Signed-off-by: Madhu Venugopal <madhu@docker.com>
2015-05-23 16:00:42 -07:00
Madhu Venugopal ed7cba986e Moved all the service commands under experimental build tag
In order to support the docker experimental feature build, moving the
service commands under experimental tag. Please refer to :
https://github.com/docker/docker/pull/13338/ for more information

Signed-off-by: Madhu Venugopal <madhu@docker.com>
2015-05-23 13:05:48 -07:00
Madhu Venugopal 377066c62c Added a catch-all root hierarchy for the API path
Though libnetwork api is supposed to handle the sub router, it is given
the entire URL to deal with. But the current api.go assumes the network/
to be in the root path.
We need this patch to make it work seamlessly with docker & dnet UI & API

Signed-off-by: Madhu Venugopal <madhu@docker.com>
2015-05-23 12:20:30 -07:00
Madhu Venugopal ba22b50a6a Merge pull request #195 from LK4D4/dummyproxy
Add dummy proxy on port map
2015-05-22 13:53:15 -07:00
Alexander Morozov 198f0ef7cf Add dummy proxy on port map
It is needed in cases when mapped port is already bound, or another
application bind mapped port. All this will be undetected because we use
iptables and not net.Listen.

Signed-off-by: Alexander Morozov <lk4d4@docker.com>
2015-05-22 12:38:28 -07:00
Jana Radhakrishnan 429678548b Merge pull request #196 from mavenugo/api-changes
Modified Client to make use of the corrected REST API & service endpoint support
2015-05-22 11:02:07 -07:00
Madhu Venugopal 3892c2db42 Service endpoint UI support
Signed-off-by: Madhu Venugopal <madhu@docker.com>
2015-05-22 09:53:24 -07:00
Madhu Venugopal 1f0f737948 Adding support for network/id/endpoints in api
Signed-off-by: Madhu Venugopal <madhu@docker.com>
2015-05-22 09:53:19 -07:00
Madhu Venugopal 17bfe1f3f5 Modified Client to make use of the corrected REST API
Also supporting name, id & partial-id lookups for all the network
commands

Signed-off-by: Madhu Venugopal <madhu@docker.com>
2015-05-21 17:38:40 -07:00
Jana Radhakrishnan 3253d89aed Merge pull request #194 from aboch/rest
REST API: Support query by partial id
2015-05-21 14:56:46 -07:00
Alessandro Boch 3dec1a68e0 REST API: Support query by partial id
- for networks and endpoints

Signed-off-by: Alessandro Boch <aboch@docker.com>
2015-05-21 14:37:21 -07:00
Madhu Venugopal bc56eb5053 Merge pull request #193 from mrjana/cnm
Modify driver Join api to only allow dst prefix
2015-05-21 13:58:22 -07:00
Jana Radhakrishnan 647975a330 Modify driver Join api to only allow dst prefix
Currently the driver api allows the driver to specify the
full interface name for the interface inside the container.
This is not appropriate since the driver does not have the full
view of the sandbox to correcly allocate an unambiguous interface
name. Instead with this PR the driver will be allowed to specify
a prefix for the name and libnetwork and sandbox layers will
disambiguate it with an appropriate suffix.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-05-21 20:17:44 +00:00
Madhu Venugopal eb8273e466 Merge pull request #162 from squaremo/net-plugin
Implement remote driver
2015-05-21 12:10:56 -07:00
Michael Bridgen b18eff4940 Remote driver implementation
In essense, this just involves marshalling structs back and forth to a
remote process, via the plugin client. There are a couple of types
that don't JSONify well, notably `net.IPNet`, so there is some
translation to be done.

To conform to the driverapi interface, we must give the list of
endpoint interfaces to the remote process, and let it puzzle out what
it's supposed to do; including the possibility of returning an error.

The constraints on EndpointInfo are enforced by the remote driver
implementation; namely:

 * It can't be nil

 * If it's got non-empty Interfaces(), the remote process can't put
   more in

In the latter case, or if we fail to add an interface for some
(future) reason, we try to roll the endpoint creation back. Likewise
for join -- if we fail to set the fields of the JoinInfo, we roll the
join back by leaving.

Signed-off-by: Michael Bridgen <mikeb@squaremobius.net>
2015-05-21 19:32:41 +01:00
Jana Radhakrishnan 6a48128ebc Merge pull request #191 from mavenugo/master
Revert "Added more test coverage for portmapper package."
2015-05-21 10:57:21 -07:00
Madhu Venugopal 7c707bc574 Revert "Added more test coverage for portmapper package."
This reverts commit 1d34250950.

Signed-off-by: Madhu Venugopal <madhu@docker.com>
2015-05-21 10:39:14 -07:00
Jana Radhakrishnan 9909a379b9 Merge pull request #190 from aboch/rest
Fix test failure in api
2015-05-21 10:22:24 -07:00
Alessandro Boch 1bf2e96b08 Fix test failure in api
- Happened during merge

Signed-off-by: Alessandro Boch <aboch@docker.com>
2015-05-21 09:59:35 -07:00
Jana Radhakrishnan 4a6f3c2377 Merge pull request #163 from aboch/eorr
Provide interface to categorize errors
2015-05-21 09:09:15 -07:00
Madhu Venugopal fc02890e06 Merge pull request #179 from aboch/rest
Changes in rest api
2015-05-21 03:51:51 -07:00
Madhu Venugopal 9fa618958a Merge pull request #153 from nerdalert/brent-link-fix
fixing a link in design.md
2015-05-21 01:39:28 -07:00
Alessandro Boch 50964c9948 Provide interface to categorize errors
- Package types to define the interfaces libnetwork errors
  may implement, so that caller can categorize them.

Signed-off-by: Alessandro Boch <aboch@docker.com>
2015-05-20 22:29:29 -07:00
Alessandro Boch 39888c3836 Changes in rest api
- Fix POST to collection
- Only resource ID in URI, search by name as query parameter
- Fix URLs, consistency and restrict regex

Signed-off-by: Alessandro Boch <aboch@docker.com>
2015-05-20 16:44:06 -07:00
Madhu Venugopal e2bdf92d6b Merge pull request #187 from mrjana/cross
Move network types to types package
2015-05-20 14:03:52 -07:00
Jana Radhakrishnan 7783fcd13f Move network types to types package
This is need to decouple types from netutils which has linux
dependencies. This way the client code which needs network types
can just pull in types package which makes client code platform
agnostic.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-05-20 20:28:46 +00:00
Madhu Venugopal b39597744b Merge pull request #182 from mrjana/cnm_integ
Fix /etc/resolv.conf permission issue
2015-05-19 22:57:58 -07:00
Jana Radhakrishnan a5ed1b5bd4 Fix /etc/resolv.conf permission issue
The container's /etc/resolv.conf permission was getting setup
as 0600 while it should be 0644 for every user inside the
container to be able to read it. The tempfile that we create
initially to populate the resolvconf content is getting created
with 0600 mode. Changed it to 0644 once it is created since there
is noway to pass mode option to ioutil.Tempfile

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
2015-05-20 05:46:30 +00:00
Brent Salisbury ee9ebf0e89 fixing a link in design.md
Signed-off-by: Brent Salisbury <brent.salisbury@docker.com>
2015-05-14 16:02:06 -04:00
90 changed files with 6174 additions and 1245 deletions
+8 -3
View File
@@ -1,6 +1,6 @@
{
"ImportPath": "github.com/docker/libnetwork",
"GoVersion": "go1.4.1",
"GoVersion": "go1.4.2",
"Packages": [
"./..."
],
@@ -55,6 +55,11 @@
"Comment": "v1.4.1-3479-ga9172f5",
"Rev": "a9172f572e13086859c652e2d581950e910d63d4"
},
{
"ImportPath": "github.com/docker/libcontainer/netlink",
"Comment": "v1.4.0-495-g3e66118",
"Rev": "3e661186ba24f259d3860f067df052c7f6904bee"
},
{
"ImportPath": "github.com/docker/libcontainer/user",
"Comment": "v1.4.0-495-g3e66118",
@@ -75,11 +80,11 @@
},
{
"ImportPath": "github.com/vishvananda/netlink",
"Rev": "8eb64238879fed52fd51c5b30ad20b928fb4c36c"
"Rev": "20397a138846e4d6590e01783ed023ed7e1c38a6"
},
{
"ImportPath": "github.com/vishvananda/netns",
"Rev": "008d17ae001344769b031375bdb38a86219154c6"
"Rev": "493029407eeb434d0c2d44e02ea072ff2488d322"
}
]
}
@@ -0,0 +1,2 @@
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
Guillaume J. Charmes <guillaume@docker.com> (@creack)
@@ -0,0 +1,31 @@
// Packet netlink provide access to low level Netlink sockets and messages.
//
// Actual implementations are in:
// netlink_linux.go
// netlink_darwin.go
package netlink
import (
"errors"
"net"
)
var (
ErrWrongSockType = errors.New("Wrong socket type")
ErrShortResponse = errors.New("Got short response from netlink")
ErrInterfaceExists = errors.New("Network interface already exists")
)
// A Route is a subnet associated with the interface to reach it.
type Route struct {
*net.IPNet
Iface *net.Interface
Default bool
}
// An IfAddr defines IP network settings for a given network interface
type IfAddr struct {
Iface *net.Interface
IP net.IP
IPNet *net.IPNet
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
package netlink
func ifrDataByte(b byte) uint8 {
return uint8(b)
}
@@ -0,0 +1,7 @@
// +build !arm
package netlink
func ifrDataByte(b byte) int8 {
return int8(b)
}
@@ -0,0 +1,408 @@
package netlink
import (
"net"
"strings"
"syscall"
"testing"
)
type testLink struct {
name string
linkType string
}
func addLink(t *testing.T, name string, linkType string) {
if err := NetworkLinkAdd(name, linkType); err != nil {
t.Fatalf("Unable to create %s link: %s", name, err)
}
}
func readLink(t *testing.T, name string) *net.Interface {
iface, err := net.InterfaceByName(name)
if err != nil {
t.Fatalf("Could not find %s interface: %s", name, err)
}
return iface
}
func deleteLink(t *testing.T, name string) {
if err := NetworkLinkDel(name); err != nil {
t.Fatalf("Unable to delete %s link: %s", name, err)
}
}
func upLink(t *testing.T, name string) {
iface := readLink(t, name)
if err := NetworkLinkUp(iface); err != nil {
t.Fatalf("Could not bring UP %#v interface: %s", iface, err)
}
}
func downLink(t *testing.T, name string) {
iface := readLink(t, name)
if err := NetworkLinkDown(iface); err != nil {
t.Fatalf("Could not bring DOWN %#v interface: %s", iface, err)
}
}
func ipAssigned(iface *net.Interface, ip net.IP) bool {
addrs, _ := iface.Addrs()
for _, addr := range addrs {
args := strings.SplitN(addr.String(), "/", 2)
if args[0] == ip.String() {
return true
}
}
return false
}
func TestNetworkLinkAddDel(t *testing.T) {
if testing.Short() {
return
}
testLinks := []testLink{
{"tstEth", "dummy"},
{"tstBr", "bridge"},
}
for _, tl := range testLinks {
addLink(t, tl.name, tl.linkType)
defer deleteLink(t, tl.name)
readLink(t, tl.name)
}
}
func TestNetworkLinkUpDown(t *testing.T) {
if testing.Short() {
return
}
tl := testLink{name: "tstEth", linkType: "dummy"}
addLink(t, tl.name, tl.linkType)
defer deleteLink(t, tl.name)
upLink(t, tl.name)
ifcAfterUp := readLink(t, tl.name)
if (ifcAfterUp.Flags & syscall.IFF_UP) != syscall.IFF_UP {
t.Fatalf("Could not bring UP %#v initerface", tl)
}
downLink(t, tl.name)
ifcAfterDown := readLink(t, tl.name)
if (ifcAfterDown.Flags & syscall.IFF_UP) == syscall.IFF_UP {
t.Fatalf("Could not bring DOWN %#v initerface", tl)
}
}
func TestNetworkSetMacAddress(t *testing.T) {
if testing.Short() {
return
}
tl := testLink{name: "tstEth", linkType: "dummy"}
macaddr := "22:ce:e0:99:63:6f"
addLink(t, tl.name, tl.linkType)
defer deleteLink(t, tl.name)
ifcBeforeSet := readLink(t, tl.name)
if err := NetworkSetMacAddress(ifcBeforeSet, macaddr); err != nil {
t.Fatalf("Could not set %s MAC address on %#v interface: %s", macaddr, tl, err)
}
ifcAfterSet := readLink(t, tl.name)
if ifcAfterSet.HardwareAddr.String() != macaddr {
t.Fatalf("Could not set %s MAC address on %#v interface", macaddr, tl)
}
}
func TestNetworkSetMTU(t *testing.T) {
if testing.Short() {
return
}
tl := testLink{name: "tstEth", linkType: "dummy"}
mtu := 1400
addLink(t, tl.name, tl.linkType)
defer deleteLink(t, tl.name)
ifcBeforeSet := readLink(t, tl.name)
if err := NetworkSetMTU(ifcBeforeSet, mtu); err != nil {
t.Fatalf("Could not set %d MTU on %#v interface: %s", mtu, tl, err)
}
ifcAfterSet := readLink(t, tl.name)
if ifcAfterSet.MTU != mtu {
t.Fatalf("Could not set %d MTU on %#v interface", mtu, tl)
}
}
func TestNetworkSetMasterNoMaster(t *testing.T) {
if testing.Short() {
return
}
master := testLink{"tstBr", "bridge"}
slave := testLink{"tstEth", "dummy"}
testLinks := []testLink{master, slave}
for _, tl := range testLinks {
addLink(t, tl.name, tl.linkType)
defer deleteLink(t, tl.name)
upLink(t, tl.name)
}
masterIfc := readLink(t, master.name)
slaveIfc := readLink(t, slave.name)
if err := NetworkSetMaster(slaveIfc, masterIfc); err != nil {
t.Fatalf("Could not set %#v to be the master of %#v: %s", master, slave, err)
}
// Trying to figure out a way to test which will not break on RHEL6.
// We could check for existence of /sys/class/net/tstEth/upper_tstBr
// which should point to the ../tstBr which is the UPPER device i.e. network bridge
if err := NetworkSetNoMaster(slaveIfc); err != nil {
t.Fatalf("Could not UNset %#v master of %#v: %s", master, slave, err)
}
}
func TestNetworkChangeName(t *testing.T) {
if testing.Short() {
return
}
tl := testLink{"tstEth", "dummy"}
newName := "newTst"
addLink(t, tl.name, tl.linkType)
linkIfc := readLink(t, tl.name)
if err := NetworkChangeName(linkIfc, newName); err != nil {
deleteLink(t, tl.name)
t.Fatalf("Could not change %#v interface name to %s: %s", tl, newName, err)
}
readLink(t, newName)
deleteLink(t, newName)
}
func TestNetworkLinkAddVlan(t *testing.T) {
if testing.Short() {
return
}
tl := struct {
name string
id uint16
}{
name: "tstVlan",
id: 32,
}
masterLink := testLink{"tstEth", "dummy"}
addLink(t, masterLink.name, masterLink.linkType)
defer deleteLink(t, masterLink.name)
if err := NetworkLinkAddVlan(masterLink.name, tl.name, tl.id); err != nil {
t.Fatalf("Unable to create %#v VLAN interface: %s", tl, err)
}
readLink(t, tl.name)
}
func TestNetworkLinkAddMacVlan(t *testing.T) {
if testing.Short() {
return
}
tl := struct {
name string
mode string
}{
name: "tstVlan",
mode: "private",
}
masterLink := testLink{"tstEth", "dummy"}
addLink(t, masterLink.name, masterLink.linkType)
defer deleteLink(t, masterLink.name)
if err := NetworkLinkAddMacVlan(masterLink.name, tl.name, tl.mode); err != nil {
t.Fatalf("Unable to create %#v MAC VLAN interface: %s", tl, err)
}
readLink(t, tl.name)
}
func TestNetworkLinkAddMacVtap(t *testing.T) {
if testing.Short() {
return
}
tl := struct {
name string
mode string
}{
name: "tstVtap",
mode: "private",
}
masterLink := testLink{"tstEth", "dummy"}
addLink(t, masterLink.name, masterLink.linkType)
defer deleteLink(t, masterLink.name)
if err := NetworkLinkAddMacVtap(masterLink.name, tl.name, tl.mode); err != nil {
t.Fatalf("Unable to create %#v MAC VTAP interface: %s", tl, err)
}
readLink(t, tl.name)
}
func TestAddDelNetworkIp(t *testing.T) {
if testing.Short() {
return
}
ifaceName := "lo"
ip := net.ParseIP("127.0.1.1")
mask := net.IPv4Mask(255, 255, 255, 255)
ipNet := &net.IPNet{IP: ip, Mask: mask}
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
t.Skip("No 'lo' interface; skipping tests")
}
if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil {
t.Fatalf("Could not add IP address %s to interface %#v: %s", ip.String(), iface, err)
}
if !ipAssigned(iface, ip) {
t.Fatalf("Could not locate address '%s' in lo address list.", ip.String())
}
if err := NetworkLinkDelIp(iface, ip, ipNet); err != nil {
t.Fatalf("Could not delete IP address %s from interface %#v: %s", ip.String(), iface, err)
}
if ipAssigned(iface, ip) {
t.Fatalf("Located address '%s' in lo address list after removal.", ip.String())
}
}
func TestAddRouteSourceSelection(t *testing.T) {
tstIp := "127.1.1.1"
tl := testLink{name: "tstEth", linkType: "dummy"}
addLink(t, tl.name, tl.linkType)
defer deleteLink(t, tl.name)
ip := net.ParseIP(tstIp)
mask := net.IPv4Mask(255, 255, 255, 255)
ipNet := &net.IPNet{IP: ip, Mask: mask}
iface, err := net.InterfaceByName(tl.name)
if err != nil {
t.Fatalf("Lost created link %#v", tl)
}
if err := NetworkLinkAddIp(iface, ip, ipNet); err != nil {
t.Fatalf("Could not add IP address %s to interface %#v: %s", ip.String(), iface, err)
}
upLink(t, tl.name)
defer downLink(t, tl.name)
if err := AddRoute("127.0.0.0/8", tstIp, "", tl.name); err != nil {
t.Fatalf("Failed to add route with source address")
}
}
func TestCreateVethPair(t *testing.T) {
if testing.Short() {
return
}
var (
name1 = "veth1"
name2 = "veth2"
)
if err := NetworkCreateVethPair(name1, name2, 0); err != nil {
t.Fatalf("Could not create veth pair %s %s: %s", name1, name2, err)
}
defer NetworkLinkDel(name1)
readLink(t, name1)
readLink(t, name2)
}
//
// netlink package tests which do not use RTNETLINK
//
func TestCreateBridgeWithMac(t *testing.T) {
if testing.Short() {
return
}
name := "testbridge"
if err := CreateBridge(name, true); err != nil {
t.Fatal(err)
}
if _, err := net.InterfaceByName(name); err != nil {
t.Fatal(err)
}
// cleanup and tests
if err := DeleteBridge(name); err != nil {
t.Fatal(err)
}
if _, err := net.InterfaceByName(name); err == nil {
t.Fatalf("expected error getting interface because %s bridge was deleted", name)
}
}
func TestSetMacAddress(t *testing.T) {
if testing.Short() {
return
}
name := "testmac"
mac := randMacAddr()
if err := NetworkLinkAdd(name, "bridge"); err != nil {
t.Fatal(err)
}
defer NetworkLinkDel(name)
if err := SetMacAddress(name, mac); err != nil {
t.Fatal(err)
}
iface, err := net.InterfaceByName(name)
if err != nil {
t.Fatal(err)
}
if iface.HardwareAddr.String() != mac {
t.Fatalf("mac address %q does not match %q", iface.HardwareAddr, mac)
}
}
@@ -0,0 +1,88 @@
// +build !linux
package netlink
import (
"errors"
"net"
)
var (
ErrNotImplemented = errors.New("not implemented")
)
func NetworkGetRoutes() ([]Route, error) {
return nil, ErrNotImplemented
}
func NetworkLinkAdd(name string, linkType string) error {
return ErrNotImplemented
}
func NetworkLinkDel(name string) error {
return ErrNotImplemented
}
func NetworkLinkUp(iface *net.Interface) error {
return ErrNotImplemented
}
func NetworkLinkAddIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
return ErrNotImplemented
}
func NetworkLinkDelIp(iface *net.Interface, ip net.IP, ipNet *net.IPNet) error {
return ErrNotImplemented
}
func AddRoute(destination, source, gateway, device string) error {
return ErrNotImplemented
}
func AddDefaultGw(ip, device string) error {
return ErrNotImplemented
}
func NetworkSetMTU(iface *net.Interface, mtu int) error {
return ErrNotImplemented
}
func NetworkSetTxQueueLen(iface *net.Interface, txQueueLen int) error {
return ErrNotImplemented
}
func NetworkCreateVethPair(name1, name2 string, txQueueLen int) error {
return ErrNotImplemented
}
func NetworkChangeName(iface *net.Interface, newName string) error {
return ErrNotImplemented
}
func NetworkSetNsFd(iface *net.Interface, fd int) error {
return ErrNotImplemented
}
func NetworkSetNsPid(iface *net.Interface, nspid int) error {
return ErrNotImplemented
}
func NetworkSetMaster(iface, master *net.Interface) error {
return ErrNotImplemented
}
func NetworkLinkDown(iface *net.Interface) error {
return ErrNotImplemented
}
func CreateBridge(name string, setMacAddr bool) error {
return ErrNotImplemented
}
func DeleteBridge(name string) error {
return ErrNotImplemented
}
func AddToBridge(iface, master *net.Interface) error {
return ErrNotImplemented
}
+7 -1
View File
@@ -43,13 +43,19 @@ import (
)
func main() {
mybridge := &netlink.Bridge{netlink.LinkAttrs{Name: "foo"}}
la := netlink.NewLinkAttrs()
la.Name = "foo"
mybridge := &netlink.Bridge{la}}
_ := netlink.LinkAdd(mybridge)
eth1, _ := netlink.LinkByName("eth1")
netlink.LinkSetMaster(eth1, mybridge)
}
```
Note `NewLinkAttrs` constructor, it sets default values in structure. For now
it sets only `TxQLen` to `-1`, so kernel will set default by itself. If you're
using simple initialization(`LinkAttrs{Name: "foo"}`) `TxQLen` will be set to
`0` unless you specify it like `LinkAttrs{Name: "foo", TxQLen: 1000}`.
Add a new ip address to loopback:
+2 -2
View File
@@ -14,8 +14,8 @@ type Addr struct {
}
// String returns $ip/$netmask $label
func (addr Addr) String() string {
return fmt.Sprintf("%s %s", addr.IPNet, addr.Label)
func (a Addr) String() string {
return fmt.Sprintf("%s %s", a.IPNet, a.Label)
}
// ParseAddr parses the string representation of an address in the
+16 -2
View File
@@ -81,7 +81,7 @@ func AddrList(link Link, family int) ([]Addr, error) {
index = base.Index
}
res := make([]Addr, 0)
var res []Addr
for _, m := range msgs {
msg := nl.DeserializeIfAddrmsg(m)
@@ -95,11 +95,17 @@ func AddrList(link Link, family int) ([]Addr, error) {
return nil, err
}
var local, dst *net.IPNet
var addr Addr
for _, attr := range attrs {
switch attr.Attr.Type {
case syscall.IFA_ADDRESS:
addr.IPNet = &net.IPNet{
dst = &net.IPNet{
IP: attr.Value,
Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
}
case syscall.IFA_LOCAL:
local = &net.IPNet{
IP: attr.Value,
Mask: net.CIDRMask(int(msg.Prefixlen), 8*len(attr.Value)),
}
@@ -107,6 +113,14 @@ func AddrList(link Link, family int) ([]Addr, error) {
addr.Label = string(attr.Value[:len(attr.Value)-1])
}
}
// IFA_LOCAL should be there but if not, fall back to IFA_ADDRESS
if local != nil {
addr.IPNet = local
} else {
addr.IPNet = dst
}
res = append(res, addr)
}
+28 -3
View File
@@ -10,16 +10,29 @@ type Link interface {
Type() string
}
type (
NsPid int
NsFd int
)
// LinkAttrs represents data shared by most link types
type LinkAttrs struct {
Index int
MTU int
TxQLen uint32 // Transmit Queue Length
TxQLen int // Transmit Queue Length
Name string
HardwareAddr net.HardwareAddr
Flags net.Flags
ParentIndex int // index of the parent link device
MasterIndex int // must be the index of a bridge
ParentIndex int // index of the parent link device
MasterIndex int // must be the index of a bridge
Namespace interface{} // nil | NsPid | NsFd
}
// NewLinkAttrs returns LinkAttrs structure filled with default values
func NewLinkAttrs() LinkAttrs {
return LinkAttrs{
TxQLen: -1,
}
}
// Device links cannot be created via netlink. These links
@@ -76,9 +89,21 @@ func (vlan *Vlan) Type() string {
return "vlan"
}
type MacvlanMode uint16
const (
MACVLAN_MODE_DEFAULT MacvlanMode = iota
MACVLAN_MODE_PRIVATE
MACVLAN_MODE_VEPA
MACVLAN_MODE_BRIDGE
MACVLAN_MODE_PASSTHRU
MACVLAN_MODE_SOURCE
)
// Macvlan links have ParentIndex set in their Attrs()
type Macvlan struct {
LinkAttrs
Mode MacvlanMode
}
func (macvlan *Macvlan) Attrs() *LinkAttrs {
+73 -15
View File
@@ -13,6 +13,15 @@ import (
var native = nl.NativeEndian()
var lookupByDump = false
var macvlanModes = [...]uint32{
0,
nl.MACVLAN_MODE_PRIVATE,
nl.MACVLAN_MODE_VEPA,
nl.MACVLAN_MODE_BRIDGE,
nl.MACVLAN_MODE_PASSTHRU,
nl.MACVLAN_MODE_SOURCE,
}
func ensureIndex(link *LinkAttrs) {
if link != nil && link.Index == 0 {
newlink, _ := LinkByName(link.Name)
@@ -39,7 +48,7 @@ func LinkSetUp(link Link) error {
return err
}
// LinkSetUp disables link device.
// LinkSetDown disables link device.
// Equivalent to: `ip link set $link down`
func LinkSetDown(link Link) error {
base := link.Attrs()
@@ -67,7 +76,7 @@ func LinkSetMTU(link Link, mtu int) error {
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
msg.Change = syscall.IFLA_MTU
req.AddData(msg)
b := make([]byte, 4)
@@ -91,7 +100,7 @@ func LinkSetName(link Link, name string) error {
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
msg.Change = syscall.IFLA_IFNAME
req.AddData(msg)
data := nl.NewRtAttr(syscall.IFLA_IFNAME, []byte(name))
@@ -112,7 +121,7 @@ func LinkSetHardwareAddr(link Link, hwaddr net.HardwareAddr) error {
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
msg.Change = syscall.IFLA_ADDRESS
req.AddData(msg)
data := nl.NewRtAttr(syscall.IFLA_ADDRESS, []byte(hwaddr))
@@ -145,7 +154,7 @@ func LinkSetMasterByIndex(link Link, masterIndex int) error {
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
msg.Change = syscall.IFLA_MASTER
req.AddData(msg)
b := make([]byte, 4)
@@ -170,7 +179,7 @@ func LinkSetNsPid(link Link, nspid int) error {
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
msg.Change = syscall.IFLA_NET_NS_PID
req.AddData(msg)
b := make([]byte, 4)
@@ -183,7 +192,7 @@ func LinkSetNsPid(link Link, nspid int) error {
return err
}
// LinkSetNsPid puts the device into a new network namespace. The
// LinkSetNsFd puts the device into a new network namespace. The
// fd must be an open file descriptor to a network namespace.
// Similar to: `ip link set $link netns $ns`
func LinkSetNsFd(link Link, fd int) error {
@@ -195,7 +204,7 @@ func LinkSetNsFd(link Link, fd int) error {
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
msg.Change = nl.IFLA_NET_NS_FD
req.AddData(msg)
b := make([]byte, 4)
@@ -312,11 +321,28 @@ func LinkAdd(link Link) error {
req.AddData(mtu)
}
if base.TxQLen >= 0 {
qlen := nl.NewRtAttr(syscall.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen)))
req.AddData(qlen)
}
if base.Namespace != nil {
var attr *nl.RtAttr
switch base.Namespace.(type) {
case NsPid:
val := nl.Uint32Attr(uint32(base.Namespace.(NsPid)))
attr = nl.NewRtAttr(syscall.IFLA_NET_NS_PID, val)
case NsFd:
val := nl.Uint32Attr(uint32(base.Namespace.(NsFd)))
attr = nl.NewRtAttr(nl.IFLA_NET_NS_FD, val)
}
req.AddData(attr)
}
linkInfo := nl.NewRtAttr(syscall.IFLA_LINKINFO, nil)
nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_KIND, nl.NonZeroTerminated(link.Type()))
nl.NewRtAttrChild(linkInfo, syscall.IFLA_TXQLEN, nl.Uint32Attr(base.TxQLen))
if vlan, ok := link.(*Vlan); ok {
b := make([]byte, 2)
native.PutUint16(b, uint16(vlan.VlanId))
@@ -327,15 +353,23 @@ func LinkAdd(link Link) error {
peer := nl.NewRtAttrChild(data, nl.VETH_INFO_PEER, nil)
nl.NewIfInfomsgChild(peer, syscall.AF_UNSPEC)
nl.NewRtAttrChild(peer, syscall.IFLA_IFNAME, nl.ZeroTerminated(veth.PeerName))
nl.NewRtAttrChild(peer, syscall.IFLA_TXQLEN, nl.Uint32Attr(base.TxQLen))
if base.TxQLen >= 0 {
nl.NewRtAttrChild(peer, syscall.IFLA_TXQLEN, nl.Uint32Attr(uint32(base.TxQLen)))
}
if base.MTU > 0 {
nl.NewRtAttrChild(peer, syscall.IFLA_MTU, nl.Uint32Attr(uint32(base.MTU)))
}
} else if vxlan, ok := link.(*Vxlan); ok {
addVxlanAttrs(vxlan, linkInfo)
} else if ipv, ok := link.(*IPVlan); ok {
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
nl.NewRtAttrChild(data, nl.IFLA_IPVLAN_MODE, nl.Uint16Attr(uint16(ipv.Mode)))
} else if macv, ok := link.(*Macvlan); ok {
if macv.Mode != MACVLAN_MODE_DEFAULT {
data := nl.NewRtAttrChild(linkInfo, nl.IFLA_INFO_DATA, nil)
nl.NewRtAttrChild(data, nl.IFLA_MACVLAN_MODE, nl.Uint32Attr(macvlanModes[macv.Mode]))
}
}
req.AddData(linkInfo)
@@ -483,6 +517,8 @@ func linkDeserialize(m []byte) (Link, error) {
link = &Vxlan{}
case "ipvlan":
link = &IPVlan{}
case "macvlan":
link = &Macvlan{}
default:
link = &Generic{LinkType: linkType}
}
@@ -498,6 +534,8 @@ func linkDeserialize(m []byte) (Link, error) {
parseVxlanData(link, data)
case "ipvlan":
parseIPVlanData(link, data)
case "macvlan":
parseMacvlanData(link, data)
}
}
}
@@ -520,7 +558,7 @@ func linkDeserialize(m []byte) (Link, error) {
case syscall.IFLA_MASTER:
base.MasterIndex = int(native.Uint32(attr.Value[0:4]))
case syscall.IFLA_TXQLEN:
base.TxQLen = native.Uint32(attr.Value[0:4])
base.TxQLen = int(native.Uint32(attr.Value[0:4]))
}
}
// Links that don't have IFLA_INFO_KIND are hardware devices
@@ -547,8 +585,7 @@ func LinkList() ([]Link, error) {
return nil, err
}
res := make([]Link, 0)
var res []Link
for _, m := range msgs {
link, err := linkDeserialize(m)
if err != nil {
@@ -593,7 +630,7 @@ func setProtinfoAttr(link Link, mode bool, attr int) error {
msg.Type = syscall.RTM_SETLINK
msg.Flags = syscall.NLM_F_REQUEST
msg.Index = int32(base.Index)
msg.Change = nl.DEFAULT_CHANGE
msg.Change = syscall.IFLA_PROTINFO | syscall.NLA_F_NESTED
req.AddData(msg)
br := nl.NewRtAttr(syscall.IFLA_PROTINFO|syscall.NLA_F_NESTED, nil)
@@ -674,6 +711,27 @@ func parseIPVlanData(link Link, data []syscall.NetlinkRouteAttr) {
}
}
func parseMacvlanData(link Link, data []syscall.NetlinkRouteAttr) {
macv := link.(*Macvlan)
for _, datum := range data {
if datum.Attr.Type == nl.IFLA_MACVLAN_MODE {
switch native.Uint32(datum.Value[0:4]) {
case nl.MACVLAN_MODE_PRIVATE:
macv.Mode = MACVLAN_MODE_PRIVATE
case nl.MACVLAN_MODE_VEPA:
macv.Mode = MACVLAN_MODE_VEPA
case nl.MACVLAN_MODE_BRIDGE:
macv.Mode = MACVLAN_MODE_BRIDGE
case nl.MACVLAN_MODE_PASSTHRU:
macv.Mode = MACVLAN_MODE_PASSTHRU
case nl.MACVLAN_MODE_SOURCE:
macv.Mode = MACVLAN_MODE_SOURCE
}
return
}
}
}
// copied from pkg/net_linux.go
func linkFlags(rawFlags uint32) net.Flags {
var f net.Flags
+114 -5
View File
@@ -8,7 +8,10 @@ import (
"github.com/vishvananda/netns"
)
const testTxQLen uint32 = 100
const (
testTxQLen int = 100
defaultTxQLen int = 1000
)
func testLinkAddDel(t *testing.T, link Link) {
links, err := LinkList()
@@ -50,9 +53,9 @@ func testLinkAddDel(t *testing.T, link Link) {
}
}
if veth, ok := link.(*Veth); ok {
if veth.TxQLen != testTxQLen {
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, testTxQLen)
if veth, ok := result.(*Veth); ok {
if rBase.TxQLen != base.TxQLen {
t.Fatalf("qlen is %d, should be %d", rBase.TxQLen, base.TxQLen)
}
if rBase.MTU != base.MTU {
t.Fatalf("MTU is %d, should be %d", rBase.MTU, base.MTU)
@@ -91,6 +94,16 @@ func testLinkAddDel(t *testing.T, link Link) {
}
}
if macv, ok := link.(*Macvlan); ok {
other, ok := result.(*Macvlan)
if !ok {
t.Fatal("Result of create is not a macvlan")
}
if macv.Mode != other.Mode {
t.Fatalf("Got unexpected mode: %d, expected: %d", other.Mode, macv.Mode)
}
}
if err = LinkDel(link); err != nil {
t.Fatal(err)
}
@@ -199,7 +212,10 @@ func TestLinkAddDelMacvlan(t *testing.T) {
t.Fatal(err)
}
testLinkAddDel(t, &Macvlan{LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index}})
testLinkAddDel(t, &Macvlan{
LinkAttrs: LinkAttrs{Name: "bar", ParentIndex: parent.Attrs().Index},
Mode: MACVLAN_MODE_PRIVATE,
})
if err := LinkDel(parent); err != nil {
t.Fatal(err)
@@ -213,6 +229,99 @@ func TestLinkAddDelVeth(t *testing.T) {
testLinkAddDel(t, &Veth{LinkAttrs{Name: "foo", TxQLen: testTxQLen, MTU: 1400}, "bar"})
}
func TestLinkAddVethWithDefaultTxQLen(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
la := NewLinkAttrs()
la.Name = "foo"
veth := &Veth{LinkAttrs: la, PeerName: "bar"}
if err := LinkAdd(veth); err != nil {
t.Fatal(err)
}
link, err := LinkByName("foo")
if err != nil {
t.Fatal(err)
}
if veth, ok := link.(*Veth); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if veth.TxQLen != defaultTxQLen {
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, defaultTxQLen)
}
}
peer, err := LinkByName("bar")
if err != nil {
t.Fatal(err)
}
if veth, ok := peer.(*Veth); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if veth.TxQLen != defaultTxQLen {
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, defaultTxQLen)
}
}
}
func TestLinkAddVethWithZeroTxQLen(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
la := NewLinkAttrs()
la.Name = "foo"
la.TxQLen = 0
veth := &Veth{LinkAttrs: la, PeerName: "bar"}
if err := LinkAdd(veth); err != nil {
t.Fatal(err)
}
link, err := LinkByName("foo")
if err != nil {
t.Fatal(err)
}
if veth, ok := link.(*Veth); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if veth.TxQLen != 0 {
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, 0)
}
}
peer, err := LinkByName("bar")
if err != nil {
t.Fatal(err)
}
if veth, ok := peer.(*Veth); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if veth.TxQLen != 0 {
t.Fatalf("TxQLen is %d, should be %d", veth.TxQLen, 0)
}
}
}
func TestLinkAddDummyWithTxQLen(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
la := NewLinkAttrs()
la.Name = "foo"
la.TxQLen = 1500
dummy := &Dummy{LinkAttrs: la}
if err := LinkAdd(dummy); err != nil {
t.Fatal(err)
}
link, err := LinkByName("foo")
if err != nil {
t.Fatal(err)
}
if dummy, ok := link.(*Dummy); !ok {
t.Fatalf("unexpected link type: %T", link)
} else {
if dummy.TxQLen != 1500 {
t.Fatalf("TxQLen is %d, should be %d", dummy.TxQLen, 1500)
}
}
}
func TestLinkAddDelBridgeMaster(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
+1 -1
View File
@@ -141,7 +141,7 @@ func NeighList(linkIndex, family int) ([]Neigh, error) {
return nil, err
}
res := make([]Neigh, 0)
var res []Neigh
for _, m := range msgs {
ndm := deserializeNdmsg(m)
if linkIndex != 0 && int(ndm.Index) != linkIndex {
+15
View File
@@ -79,3 +79,18 @@ const (
// not defined in syscall
IFLA_NET_NS_FD = 28
)
const (
IFLA_MACVLAN_UNSPEC = iota
IFLA_MACVLAN_MODE
IFLA_MACVLAN_FLAGS
IFLA_MACVLAN_MAX = IFLA_MACVLAN_FLAGS
)
const (
MACVLAN_MODE_PRIVATE = 1
MACVLAN_MODE_VEPA = 2
MACVLAN_MODE_BRIDGE = 4
MACVLAN_MODE_PASSTHRU = 8
MACVLAN_MODE_SOURCE = 16
)
+11 -11
View File
@@ -172,16 +172,16 @@ type NetlinkRequest struct {
}
// Serialize the Netlink Request into a byte array
func (msg *NetlinkRequest) Serialize() []byte {
func (req *NetlinkRequest) Serialize() []byte {
length := syscall.SizeofNlMsghdr
dataBytes := make([][]byte, len(msg.Data))
for i, data := range msg.Data {
dataBytes := make([][]byte, len(req.Data))
for i, data := range req.Data {
dataBytes[i] = data.Serialize()
length = length + len(dataBytes[i])
}
msg.Len = uint32(length)
req.Len = uint32(length)
b := make([]byte, length)
hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(msg)))[:]
hdr := (*(*[syscall.SizeofNlMsghdr]byte)(unsafe.Pointer(req)))[:]
next := syscall.SizeofNlMsghdr
copy(b[0:next], hdr)
for _, data := range dataBytes {
@@ -193,9 +193,9 @@ func (msg *NetlinkRequest) Serialize() []byte {
return b
}
func (msg *NetlinkRequest) AddData(data NetlinkRequestData) {
func (req *NetlinkRequest) AddData(data NetlinkRequestData) {
if data != nil {
msg.Data = append(msg.Data, data)
req.Data = append(req.Data, data)
}
}
@@ -218,11 +218,11 @@ func (req *NetlinkRequest) Execute(sockType int, resType uint16) ([][]byte, erro
return nil, err
}
res := make([][]byte, 0)
var res [][]byte
done:
for {
msgs, err := s.Recieve()
msgs, err := s.Receive()
if err != nil {
return nil, err
}
@@ -294,7 +294,7 @@ func getNetlinkSocket(protocol int) (*NetlinkSocket, error) {
// Create a netlink socket with a given protocol (e.g. NETLINK_ROUTE)
// and subscribe it to multicast groups passed in variable argument list.
// Returns the netlink socket on whic hReceive() method can be called
// Returns the netlink socket on which Receive() method can be called
// to retrieve the messages from the kernel.
func Subscribe(protocol int, groups ...uint) (*NetlinkSocket, error) {
fd, err := syscall.Socket(syscall.AF_NETLINK, syscall.SOCK_RAW, protocol)
@@ -329,7 +329,7 @@ func (s *NetlinkSocket) Send(request *NetlinkRequest) error {
return nil
}
func (s *NetlinkSocket) Recieve() ([]syscall.NetlinkMessage, error) {
func (s *NetlinkSocket) Receive() ([]syscall.NetlinkMessage, error) {
rb := make([]byte, syscall.Getpagesize())
nr, _, err := syscall.Recvfrom(s.fd, rb, 0)
if err != nil {
+3 -4
View File
@@ -104,9 +104,8 @@ func (x *XfrmAddress) ToIPNet(prefixlen uint8) *net.IPNet {
ip := x.ToIP()
if GetIPFamily(ip) == FAMILY_V4 {
return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 32)}
} else {
return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 128)}
}
return &net.IPNet{IP: ip, Mask: net.CIDRMask(int(prefixlen), 128)}
}
func (x *XfrmAddress) FromIP(ip net.IP) {
@@ -125,8 +124,8 @@ func DeserializeXfrmAddress(b []byte) *XfrmAddress {
return (*XfrmAddress)(unsafe.Pointer(&b[0:SizeofXfrmAddress][0]))
}
func (msg *XfrmAddress) Serialize() []byte {
return (*(*[SizeofXfrmAddress]byte)(unsafe.Pointer(msg)))[:]
func (x *XfrmAddress) Serialize() []byte {
return (*(*[SizeofXfrmAddress]byte)(unsafe.Pointer(x)))[:]
}
// struct xfrm_selector {
+1 -1
View File
@@ -16,7 +16,7 @@ type Protinfo struct {
// String returns a list of enabled flags
func (prot *Protinfo) String() string {
boolStrings := make([]string, 0)
var boolStrings []string
if prot.Hairpin {
boolStrings = append(boolStrings, "Hairpin")
}
+2 -2
View File
@@ -119,7 +119,7 @@ func RouteList(link Link, family int) ([]Route, error) {
}
native := nl.NativeEndian()
res := make([]Route, 0)
var res []Route
for _, m := range msgs {
msg := nl.DeserializeRtMsg(m)
@@ -193,7 +193,7 @@ func RouteGet(destination net.IP) ([]Route, error) {
}
native := nl.NativeEndian()
res := make([]Route, 0)
var res []Route
for _, m := range msgs {
msg := nl.DeserializeRtMsg(m)
attrs, err := nl.ParseRouteAttr(m[msg.Len():])
+1 -1
View File
@@ -84,7 +84,7 @@ func XfrmPolicyList(family int) ([]XfrmPolicy, error) {
return nil, err
}
res := make([]XfrmPolicy, 0)
var res []XfrmPolicy
for _, m := range msgs {
msg := nl.DeserializeXfrmUserpolicyInfo(m)
+1 -1
View File
@@ -118,7 +118,7 @@ func XfrmStateList(family int) ([]XfrmState, error) {
return nil, err
}
res := make([]XfrmState, 0)
var res []XfrmState
for _, m := range msgs {
msg := nl.DeserializeXfrmUsersaInfo(m)
+1
View File
@@ -12,6 +12,7 @@ import (
"fmt"
"syscall"
)
// NsHandle is a handle to a network namespace. It can be cast directly
// to an int and used as a file descriptor.
type NsHandle int
+14 -15
View File
@@ -1,3 +1,5 @@
// +build linux
package netns
import (
@@ -50,33 +52,30 @@ func Get() (NsHandle, error) {
return GetFromThread(os.Getpid(), syscall.Gettid())
}
// GetFromName gets a handle to a named network namespace such as one
// created by `ip netns add`.
func GetFromName(name string) (NsHandle, error) {
fd, err := syscall.Open(fmt.Sprintf("/var/run/netns/%s", name), syscall.O_RDONLY, 0)
// GetFromPath gets a handle to a network namespace
// identified by the path
func GetFromPath(path string) (NsHandle, error) {
fd, err := syscall.Open(path, syscall.O_RDONLY, 0)
if err != nil {
return -1, err
}
return NsHandle(fd), nil
}
// GetFromName gets a handle to a named network namespace such as one
// created by `ip netns add`.
func GetFromName(name string) (NsHandle, error) {
return GetFromPath(fmt.Sprintf("/var/run/netns/%s", name))
}
// GetFromPid gets a handle to the network namespace of a given pid.
func GetFromPid(pid int) (NsHandle, error) {
fd, err := syscall.Open(fmt.Sprintf("/proc/%d/ns/net", pid), syscall.O_RDONLY, 0)
if err != nil {
return -1, err
}
return NsHandle(fd), nil
return GetFromPath(fmt.Sprintf("/proc/%d/ns/net", pid))
}
// GetFromThread gets a handle to the network namespace of a given pid and tid.
func GetFromThread(pid, tid int) (NsHandle, error) {
name := fmt.Sprintf("/proc/%d/task/%d/ns/net", pid, tid)
fd, err := syscall.Open(name, syscall.O_RDONLY, 0)
if err != nil {
return -1, err
}
return NsHandle(fd), nil
return GetFromPath(fmt.Sprintf("/proc/%d/task/%d/ns/net", pid, tid))
}
// GetFromDocker gets a handle to the network namespace of a docker container.
@@ -1,3 +1,5 @@
// +build linux,386
package netns
const (
@@ -1,3 +1,5 @@
// +build linux,amd64
package netns
const (
+3 -1
View File
@@ -1,5 +1,7 @@
// +build linux,arm
package netns
const (
SYS_SETNS = 374
SYS_SETNS = 375
)
@@ -0,0 +1,7 @@
// +build linux,arm64
package netns
const (
SYS_SETNS = 268
)
@@ -0,0 +1,7 @@
// +build linux,ppc64le
package netns
const (
SYS_SETNS = 350
)
+6 -6
View File
@@ -10,26 +10,26 @@ var (
ErrNotImplemented = errors.New("not implemented")
)
func Set(ns Namespace) (err error) {
func Set(ns NsHandle) (err error) {
return ErrNotImplemented
}
func New() (ns Namespace, err error) {
func New() (ns NsHandle, err error) {
return -1, ErrNotImplemented
}
func Get() (Namespace, error) {
func Get() (NsHandle, error) {
return -1, ErrNotImplemented
}
func GetFromName(name string) (Namespace, error) {
func GetFromName(name string) (NsHandle, error) {
return -1, ErrNotImplemented
}
func GetFromPid(pid int) (Namespace, error) {
func GetFromPid(pid int) (NsHandle, error) {
return -1, ErrNotImplemented
}
func GetFromDocker(id string) (Namespace, error) {
func GetFromDocker(id string) (NsHandle, error) {
return -1, ErrNotImplemented
}
+1 -1
View File
@@ -22,7 +22,7 @@ build: ${build_image}.created
${docker} make build-local
build-local:
$(shell which godep) go build ./...
$(shell which godep) go build -tags experimental ./...
check: ${build_image}.created
${docker} make check-local
+122 -62
View File
@@ -5,8 +5,10 @@ import (
"fmt"
"io/ioutil"
"net/http"
"strings"
"github.com/docker/libnetwork"
"github.com/docker/libnetwork/types"
"github.com/gorilla/mux"
)
@@ -14,13 +16,28 @@ var (
successResponse = responseStatus{Status: "Success", StatusCode: http.StatusOK}
createdResponse = responseStatus{Status: "Created", StatusCode: http.StatusCreated}
mismatchResponse = responseStatus{Status: "Body/URI parameter mismatch", StatusCode: http.StatusBadRequest}
badQueryresponse = responseStatus{Status: "Unsupported query", StatusCode: http.StatusBadRequest}
)
const (
urlNwName = "name"
urlNwID = "id"
// Resource name regex
regex = "[a-zA-Z_0-9-]+"
// Router URL variable definition
nwName = "{" + urlNwName + ":" + regex + "}"
nwID = "{" + urlNwID + ":" + regex + "}"
nwPID = "{" + urlNwPID + ":" + regex + "}"
epName = "{" + urlEpName + ":" + regex + "}"
epID = "{" + urlEpID + ":" + regex + "}"
epPID = "{" + urlEpPID + ":" + regex + "}"
cnID = "{" + urlCnID + ":" + regex + "}"
// Internal URL variable name, they can be anything
urlNwName = "network-name"
urlNwID = "network-id"
urlNwPID = "network-partial-id"
urlEpName = "endpoint-name"
urlEpID = "endpoint-id"
urlEpPID = "endpoint-partial-id"
urlCnID = "container-id"
)
@@ -59,42 +76,41 @@ func (h *httpHandler) handleRequest(w http.ResponseWriter, req *http.Request) {
}
func (h *httpHandler) initRouter() {
m := map[string]map[string]processor{
m := map[string][]struct {
url string
qrs []string
fct processor
}{
"GET": {
"/networks": procGetNetworks,
"/networks/name/{" + urlNwName + ":.*}": procGetNetwork,
"/networks/id/{" + urlNwID + ":.*}": procGetNetwork,
"/networks/name/{" + urlNwName + ":.*}/endpoints/": procGetEndpoints,
"/networks/id/{" + urlNwID + ":.*}/endpoints/": procGetEndpoints,
"/networks/name/{" + urlNwName + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procGetEndpoint,
"/networks/id/{" + urlNwID + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procGetEndpoint,
"/networks/name/{" + urlNwName + ":.*}/endpoints/id/{" + urlEpID + ":.*}": procGetEndpoint,
"/networks/id/{" + urlNwID + ":.*}/endpoints/id/{" + urlEpID + ":.*}": procGetEndpoint,
// Order matters
{"/networks", []string{"name", nwName}, procGetNetworks},
{"/networks", []string{"partial-id", nwPID}, procGetNetworks},
{"/networks", nil, procGetNetworks},
{"/networks/" + nwID, nil, procGetNetwork},
{"/networks/" + nwID + "/endpoints", []string{"name", epName}, procGetEndpoints},
{"/networks/" + nwID + "/endpoints", []string{"partial-id", epPID}, procGetEndpoints},
{"/networks/" + nwID + "/endpoints", nil, procGetEndpoints},
{"/networks/" + nwID + "/endpoints/" + epID, nil, procGetEndpoint},
},
"POST": {
"/networks/name/{" + urlNwName + ":.*}": procCreateNetwork,
"/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}": procCreateEndpoint,
"/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procJoinEndpoint,
{"/networks", nil, procCreateNetwork},
{"/networks/" + nwID + "/endpoints", nil, procCreateEndpoint},
{"/networks/" + nwID + "/endpoints/" + epID + "/containers", nil, procJoinEndpoint},
},
"DELETE": {
"/networks/name/{" + urlNwName + ":.*}": procDeleteNetwork,
"/networks/id/{" + urlNwID + ":.*}": procDeleteNetwork,
"/networks/name/{" + urlNwName + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procDeleteEndpoint,
"/networks/name/{" + urlNwName + ":.*}/endpoints/id/{" + urlEpID + ":.*}": procDeleteEndpoint,
"/networks/id/{" + urlNwID + ":.*}/endpoints/name/{" + urlEpName + ":.*}": procDeleteEndpoint,
"/networks/id/{" + urlNwID + ":.*}/endpoints/id/{" + urlEpID + ":.*}": procDeleteEndpoint,
"/networks/name/{" + urlNwName + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint,
"/networks/name/{" + urlNwName + ":.*}/endpoint/id/{" + urlEpID + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint,
"/networks/id/{" + urlNwID + ":.*}/endpoint/name/{" + urlEpName + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint,
"/networks/id/{" + urlNwID + ":.*}/endpoint/id/{" + urlEpID + ":.*}/container/{" + urlCnID + ":.*}": procLeaveEndpoint,
{"/networks/" + nwID, nil, procDeleteNetwork},
{"/networks/" + nwID + "/endpoints/" + epID, nil, procDeleteEndpoint},
{"/networks/id/" + nwID + "/endpoints/" + epID + "/containers/" + cnID, nil, procLeaveEndpoint},
},
}
h.r = mux.NewRouter()
for method, routes := range m {
for route, fct := range routes {
f := makeHandler(h.c, fct)
h.r.Path(route).Methods(method).HandlerFunc(f)
for _, route := range routes {
r := h.r.Path("/{.*}" + route.url).Methods(method).HandlerFunc(makeHandler(h.c, route.fct))
if route.qrs != nil {
r.Queries(route.qrs...)
}
}
}
}
@@ -208,12 +224,7 @@ func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, b
return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
}
name := vars[urlNwName]
if name != create.Name {
return "", &mismatchResponse
}
nw, err := c.NewNetwork(create.NetworkType, name, nil)
nw, err := c.NewNetwork(create.NetworkType, create.Name, nil)
if err != nil {
return "", convertNetworkError(err)
}
@@ -232,10 +243,33 @@ func procGetNetwork(c libnetwork.NetworkController, vars map[string]string, body
func procGetNetworks(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
var list []*networkResource
for _, nw := range c.Networks() {
nwr := buildNetworkResource(nw)
list = append(list, nwr)
// Look for query filters and validate
name, queryByName := vars[urlNwName]
shortID, queryByPid := vars[urlNwPID]
if queryByName && queryByPid {
return nil, &badQueryresponse
}
if queryByName {
if nw, errRsp := findNetwork(c, name, byName); errRsp.isOK() {
list = append(list, buildNetworkResource(nw))
}
} else if queryByPid {
// Return all the prefix-matching networks
l := func(nw libnetwork.Network) bool {
if strings.HasPrefix(nw.ID(), shortID) {
list = append(list, buildNetworkResource(nw))
}
return false
}
c.WalkNetworks(l)
} else {
for _, nw := range c.Networks() {
list = append(list, buildNetworkResource(nw))
}
}
return list, &successResponse
}
@@ -250,21 +284,12 @@ func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string,
return "", &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
}
epn := vars[urlEpName]
if ec.Name != epn {
return "", &mismatchResponse
}
nwT, nwBy := detectNetworkTarget(vars)
n, errRsp := findNetwork(c, nwT, nwBy)
if !errRsp.isOK() {
return "", errRsp
}
if ec.NetworkID != n.ID() {
return "", &mismatchResponse
}
var setFctList []libnetwork.EndpointOption
if ec.ExposedPorts != nil {
setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(ec.ExposedPorts))
@@ -273,7 +298,7 @@ func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string,
setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(ec.PortMapping))
}
ep, err := n.CreateEndpoint(epn, setFctList...)
ep, err := n.CreateEndpoint(ec.Name, setFctList...)
if err != nil {
return "", convertNetworkError(err)
}
@@ -294,17 +319,40 @@ func procGetEndpoint(c libnetwork.NetworkController, vars map[string]string, bod
}
func procGetEndpoints(c libnetwork.NetworkController, vars map[string]string, body []byte) (interface{}, *responseStatus) {
target, by := detectNetworkTarget(vars)
// Look for query filters and validate
name, queryByName := vars[urlEpName]
shortID, queryByPid := vars[urlEpPID]
if queryByName && queryByPid {
return nil, &badQueryresponse
}
nw, errRsp := findNetwork(c, target, by)
nwT, nwBy := detectNetworkTarget(vars)
nw, errRsp := findNetwork(c, nwT, nwBy)
if !errRsp.isOK() {
return nil, errRsp
}
var list []*endpointResource
for _, ep := range nw.Endpoints() {
epr := buildEndpointResource(ep)
list = append(list, epr)
// If query parameter is specified, return a filtered collection
if queryByName {
if ep, errRsp := findEndpoint(c, nwT, name, nwBy, byName); errRsp.isOK() {
list = append(list, buildEndpointResource(ep))
}
} else if queryByPid {
// Return all the prefix-matching networks
l := func(ep libnetwork.Endpoint) bool {
if strings.HasPrefix(ep.ID(), shortID) {
list = append(list, buildEndpointResource(ep))
}
return false
}
nw.WalkEndpoints(l)
} else {
for _, ep := range nw.Endpoints() {
epr := buildEndpointResource(ep)
list = append(list, epr)
}
}
return list, &successResponse
@@ -336,11 +384,6 @@ func procJoinEndpoint(c libnetwork.NetworkController, vars map[string]string, bo
return nil, &responseStatus{Status: "Invalid body: " + err.Error(), StatusCode: http.StatusBadRequest}
}
cid := vars[urlCnID]
if ej.ContainerID != cid {
return "", &mismatchResponse
}
nwT, nwBy := detectNetworkTarget(vars)
epT, epBy := detectEndpointTarget(vars)
@@ -434,7 +477,7 @@ func findNetwork(c libnetwork.NetworkController, s string, by int) (libnetwork.N
panic(fmt.Sprintf("unexpected selector for network search: %d", by))
}
if err != nil {
if err == libnetwork.ErrNoSuchNetwork {
if _, ok := err.(libnetwork.ErrNoSuchNetwork); ok {
return nil, &responseStatus{Status: "Resource not found: Network", StatusCode: http.StatusNotFound}
}
return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
@@ -460,7 +503,7 @@ func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int)
panic(fmt.Sprintf("unexpected selector for endpoint search: %d", epBy))
}
if err != nil {
if err == libnetwork.ErrNoSuchEndpoint {
if _, ok := err.(libnetwork.ErrNoSuchEndpoint); ok {
return nil, &responseStatus{Status: "Resource not found: Endpoint", StatusCode: http.StatusNotFound}
}
return nil, &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
@@ -469,9 +512,26 @@ func findEndpoint(c libnetwork.NetworkController, ns, es string, nwBy, epBy int)
}
func convertNetworkError(err error) *responseStatus {
// No real libnetwork error => http error code conversion for now.
// Will came in later when new interface for libnetwork error is vailable
return &responseStatus{Status: err.Error(), StatusCode: http.StatusBadRequest}
var code int
switch err.(type) {
case types.BadRequestError:
code = http.StatusBadRequest
case types.ForbiddenError:
code = http.StatusForbidden
case types.NotFoundError:
code = http.StatusNotFound
case types.TimeoutError:
code = http.StatusRequestTimeout
case types.NotImplementedError:
code = http.StatusNotImplemented
case types.NoServiceError:
code = http.StatusServiceUnavailable
case types.InternalError:
code = http.StatusInternalServerError
default:
code = http.StatusInternalServerError
}
return &responseStatus{Status: err.Error(), StatusCode: code}
}
func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
+414 -154
View File
@@ -16,6 +16,7 @@ import (
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/options"
"github.com/docker/libnetwork/types"
)
const (
@@ -111,7 +112,7 @@ func TestJoinOptionParser(t *testing.T) {
}
func TestJson(t *testing.T) {
nc := networkCreate{Name: "mynet", NetworkType: bridgeNetType}
nc := networkCreate{NetworkType: bridgeNetType}
b, err := json.Marshal(nc)
if err != nil {
t.Fatal(err)
@@ -123,26 +124,10 @@ func TestJson(t *testing.T) {
t.Fatal(err)
}
if nc.Name != ncp.Name || nc.NetworkType != ncp.NetworkType {
if nc.NetworkType != ncp.NetworkType {
t.Fatalf("Incorrect networkCreate after json encoding/deconding: %v", ncp)
}
ec := endpointCreate{Name: "mioEp", NetworkID: "0xabcde"}
b, err = json.Marshal(ec)
if err != nil {
t.Fatal(err)
}
var ecp endpointCreate
err = json.Unmarshal(b, &ecp)
if err != nil {
t.Fatal(err)
}
if ec.Name != ecp.Name || ec.NetworkID != ecp.NetworkID {
t.Fatalf("Incorrect endpointCreate after json encoding/deconding: %v", ecp)
}
jl := endpointJoin{ContainerID: "abcdef456789"}
b, err = json.Marshal(jl)
if err != nil {
@@ -156,7 +141,7 @@ func TestJson(t *testing.T) {
}
if jl.ContainerID != jld.ContainerID {
t.Fatalf("Incorrect endpointJoin after json encoding/deconding: %v", ecp)
t.Fatalf("Incorrect endpointJoin after json encoding/deconding: %v", jld)
}
}
@@ -177,68 +162,55 @@ func TestCreateDeleteNetwork(t *testing.T) {
t.Fatal(err)
}
goodVars := map[string]string{urlNwName: "myNet"}
_, errRsp := procCreateNetwork(c, goodVars, badBody)
if errRsp == &createdResponse {
t.Fatalf("Expected to fail but succeeded")
}
incompleteBody, err := json.Marshal(networkCreate{Name: "myNet"})
if err != nil {
t.Fatal(err)
}
_, errRsp = procCreateNetwork(c, goodVars, incompleteBody)
vars := make(map[string]string)
_, errRsp := procCreateNetwork(c, nil, badBody)
if errRsp == &createdResponse {
t.Fatalf("Expected to fail but succeeded")
}
if errRsp.StatusCode != http.StatusBadRequest {
t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode)
t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp)
}
incompleteBody, err := json.Marshal(networkCreate{})
if err != nil {
t.Fatal(err)
}
_, errRsp = procCreateNetwork(c, vars, incompleteBody)
if errRsp == &createdResponse {
t.Fatalf("Expected to fail but succeeded")
}
if errRsp.StatusCode != http.StatusBadRequest {
t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp)
}
ops := make(map[string]interface{})
ops[netlabel.GenericData] = options.Generic{}
nc := networkCreate{Name: "myNet", NetworkType: bridgeNetType, Options: ops}
nc := networkCreate{Name: "network_1", NetworkType: bridgeNetType, Options: ops}
goodBody, err := json.Marshal(nc)
if err != nil {
t.Fatal(err)
}
badVars := map[string]string{urlNwName: ""}
_, errRsp = procCreateNetwork(c, badVars, goodBody)
if errRsp == &createdResponse {
t.Fatalf("Expected to fail but succeeded")
}
if errRsp.StatusCode != http.StatusBadRequest {
t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode)
}
badVars[urlNwName] = "badNetworkName"
_, errRsp = procCreateNetwork(c, badVars, goodBody)
if errRsp == &createdResponse {
t.Fatalf("Expected to fail but succeeded")
}
if errRsp.StatusCode != http.StatusBadRequest {
t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp.StatusCode)
}
_, errRsp = procCreateNetwork(c, goodVars, goodBody)
_, errRsp = procCreateNetwork(c, vars, goodBody)
if errRsp != &createdResponse {
t.Fatalf("Unexepected failure: %v", errRsp)
}
_, errRsp = procDeleteNetwork(c, badVars, nil)
vars[urlNwName] = ""
_, errRsp = procDeleteNetwork(c, vars, nil)
if errRsp == &successResponse {
t.Fatalf("Expected to fail but succeeded")
}
badVars[urlNwName] = ""
_, errRsp = procDeleteNetwork(c, badVars, nil)
vars[urlNwName] = "abc"
_, errRsp = procDeleteNetwork(c, vars, nil)
if errRsp == &successResponse {
t.Fatalf("Expected to fail but succeeded")
}
_, errRsp = procDeleteNetwork(c, goodVars, nil)
vars[urlNwName] = "network_1"
_, errRsp = procDeleteNetwork(c, vars, nil)
if errRsp != &successResponse {
t.Fatalf("Unexepected failure: %v", errRsp)
}
@@ -262,7 +234,7 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
t.Fatal(err)
}
vars := map[string]string{urlNwName: "sh"}
vars := make(map[string]string)
inid, errRsp := procCreateNetwork(c, vars, body)
if errRsp != &createdResponse {
t.Fatalf("Unexepected failure: %v", errRsp)
@@ -273,29 +245,29 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
}
ec1 := endpointCreate{
Name: "ep1",
NetworkID: string(nid),
ExposedPorts: []netutils.TransportPort{
netutils.TransportPort{Proto: netutils.TCP, Port: uint16(5000)},
netutils.TransportPort{Proto: netutils.UDP, Port: uint16(400)},
netutils.TransportPort{Proto: netutils.TCP, Port: uint16(600)},
Name: "ep1",
ExposedPorts: []types.TransportPort{
types.TransportPort{Proto: types.TCP, Port: uint16(5000)},
types.TransportPort{Proto: types.UDP, Port: uint16(400)},
types.TransportPort{Proto: types.TCP, Port: uint16(600)},
},
PortMapping: []netutils.PortBinding{
netutils.PortBinding{Proto: netutils.TCP, Port: uint16(230), HostPort: uint16(23000)},
netutils.PortBinding{Proto: netutils.UDP, Port: uint16(200), HostPort: uint16(22000)},
netutils.PortBinding{Proto: netutils.TCP, Port: uint16(120), HostPort: uint16(12000)},
PortMapping: []types.PortBinding{
types.PortBinding{Proto: types.TCP, Port: uint16(230), HostPort: uint16(23000)},
types.PortBinding{Proto: types.UDP, Port: uint16(200), HostPort: uint16(22000)},
types.PortBinding{Proto: types.TCP, Port: uint16(120), HostPort: uint16(12000)},
},
}
b1, err := json.Marshal(ec1)
if err != nil {
t.Fatal(err)
}
ec2 := endpointCreate{Name: "ep2", NetworkID: nid}
ec2 := endpointCreate{Name: "ep2"}
b2, err := json.Marshal(ec2)
if err != nil {
t.Fatal(err)
}
vars[urlNwName] = "sh"
vars[urlEpName] = "ep1"
ieid1, errRsp := procCreateEndpoint(c, vars, b1)
if errRsp != &createdResponse {
@@ -471,6 +443,7 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
if errRsp != &successResponse {
t.Fatalf("Unexepected failure: %v", errRsp)
}
delete(vars, urlEpName)
iepList, errRsp = procGetEndpoints(c, vars, nil)
if errRsp != &successResponse {
t.Fatalf("Unexepected failure: %v", errRsp)
@@ -509,6 +482,43 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
}
}
func TestDetectGetNetworksInvalidQueryComposition(t *testing.T) {
c, err := libnetwork.New()
if err != nil {
t.Fatal(err)
}
vars := map[string]string{urlNwName: "x", urlNwPID: "y"}
_, errRsp := procGetNetworks(c, vars, nil)
if errRsp.StatusCode != http.StatusBadRequest {
t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp)
}
}
func TestDetectGetEndpointsInvalidQueryComposition(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
c, err := libnetwork.New()
if err != nil {
t.Fatal(err)
}
err = c.ConfigureNetworkDriver(bridgeNetType, nil)
if err != nil {
t.Fatal(err)
}
_, err = c.NewNetwork(bridgeNetType, "network", nil)
if err != nil {
t.Fatal(err)
}
vars := map[string]string{urlNwName: "network", urlEpName: "x", urlEpPID: "y"}
_, errRsp := procGetEndpoints(c, vars, nil)
if errRsp.StatusCode != http.StatusBadRequest {
t.Fatalf("Expected %d. Got: %v", http.StatusBadRequest, errRsp)
}
}
func TestFindNetworkUtil(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
@@ -603,85 +613,46 @@ func TestCreateDeleteEndpoints(t *testing.T) {
t.Fatal(err)
}
vars := map[string]string{urlNwName: "firstNet"}
vars := make(map[string]string)
i, errRsp := procCreateNetwork(c, vars, body)
if errRsp != &createdResponse {
t.Fatalf("Unexepected failure: %v", errRsp)
}
nid := i2s(i)
vbad, err := json.Marshal("bad endppint create data")
vbad, err := json.Marshal("bad endppoint create data")
if err != nil {
t.Fatal(err)
}
vars[urlEpName] = "ep1"
vars[urlNwName] = "firstNet"
_, errRsp = procCreateEndpoint(c, vars, vbad)
if errRsp == &createdResponse {
t.Fatalf("Expected to fail but succeeded")
}
bad, err := json.Marshal(endpointCreate{Name: "ep1", NetworkID: "123456"})
if err != nil {
t.Fatal(err)
}
_, errRsp = procCreateEndpoint(c, vars, bad)
if errRsp == &createdResponse {
t.Fatalf("Expected to fail but succeeded")
}
soso, err := json.Marshal(endpointCreate{Name: "ep11", NetworkID: nid})
if err != nil {
t.Fatal(err)
}
_, errRsp = procCreateEndpoint(c, vars, soso)
if errRsp != &mismatchResponse {
t.Fatalf("Expected to fail with \"mismatchResponse\", but got: %v", errRsp)
}
bla, err := json.Marshal(endpointCreate{Name: "", NetworkID: nid})
if err != nil {
t.Fatal(err)
}
vars[urlNwName] = "firstNet"
vars[urlEpName] = ""
_, errRsp = procCreateEndpoint(c, vars, bla)
if errRsp == &createdResponse {
t.Fatalf("Expected to fail but succeeded: %v", errRsp)
}
b, err := json.Marshal(endpointCreate{Name: "firstEp", NetworkID: nid})
b, err := json.Marshal(endpointCreate{Name: ""})
if err != nil {
t.Fatal(err)
}
vars[urlNwName] = "secondNet"
vars[urlEpName] = "firstEp"
_, errRsp = procCreateEndpoint(c, vars, b)
if errRsp == &createdResponse {
t.Fatalf("Expected to fail but succeeded")
}
vars[urlNwName] = "firstNet"
vars[urlEpName] = "ep1"
_, errRsp = procCreateEndpoint(c, vars, b)
if errRsp != &mismatchResponse {
t.Fatalf("Expected to fail with \"mismatchResponse\", but got: %v", errRsp)
}
vars = make(map[string]string)
_, errRsp = procCreateEndpoint(c, vars, b)
if errRsp == &successResponse {
t.Fatalf("Expected failure but succeeded: %v", errRsp)
}
vars[urlNwName] = "firstNet"
_, errRsp = procCreateEndpoint(c, vars, b)
if errRsp == &successResponse {
t.Fatalf("Expected failure but succeeded: %v", errRsp)
b, err = json.Marshal(endpointCreate{Name: "firstEp"})
if err != nil {
t.Fatal(err)
}
vars[urlEpName] = "firstEp"
i, errRsp = procCreateEndpoint(c, vars, b)
if errRsp != &createdResponse {
t.Fatalf("Unexepected failure: %v", errRsp)
@@ -713,8 +684,8 @@ func TestCreateDeleteEndpoints(t *testing.T) {
t.Fatalf("Unexepected failure: %v", errRsp)
}
if ep0 != ep1 || ep0 != ep2 || ep0 != ep3 {
t.Fatalf("Diffenrent queries returned different endpoints")
if ep0.ID() != ep1.ID() || ep0.ID() != ep2.ID() || ep0.ID() != ep3.ID() {
t.Fatalf("Diffenrent queries returned different endpoints: \nep0: %v\nep1: %v\nep2: %v\nep3: %v", ep0, ep1, ep2, ep3)
}
vars = make(map[string]string)
@@ -766,18 +737,17 @@ func TestJoinLeave(t *testing.T) {
if err != nil {
t.Fatal(err)
}
vars := map[string]string{urlNwName: "network"}
i, errRsp := procCreateNetwork(c, vars, nb)
vars := make(map[string]string)
_, errRsp := procCreateNetwork(c, vars, nb)
if errRsp != &createdResponse {
t.Fatalf("Unexepected failure: %v", errRsp)
}
nid := i2s(i)
vars[urlEpName] = "epoint"
eb, err := json.Marshal(endpointCreate{Name: "epoint", NetworkID: nid})
eb, err := json.Marshal(endpointCreate{Name: "endpoint"})
if err != nil {
t.Fatal(err)
}
vars[urlNwName] = "network"
_, errRsp = procCreateEndpoint(c, vars, eb)
if errRsp != &createdResponse {
t.Fatalf("Unexepected failure: %v", errRsp)
@@ -792,6 +762,7 @@ func TestJoinLeave(t *testing.T) {
t.Fatalf("Expected failure, got: %v", errRsp)
}
vars[urlEpName] = "endpoint"
bad, err := json.Marshal(endpointJoin{})
if err != nil {
t.Fatal(err)
@@ -811,44 +782,30 @@ func TestJoinLeave(t *testing.T) {
vars = make(map[string]string)
vars[urlNwName] = ""
vars[urlEpName] = ""
vars[urlCnID] = cid
_, errRsp = procJoinEndpoint(c, vars, jlb)
if errRsp == &successResponse {
t.Fatalf("Expected failure, got: %v", errRsp)
}
vars[urlNwName] = "network1"
vars[urlEpName] = ""
_, errRsp = procJoinEndpoint(c, vars, jlb)
if errRsp == &successResponse {
t.Fatalf("Expected failure, got: %v", errRsp)
}
vars[urlNwName] = "network"
vars[urlEpName] = "endpoint"
vars[urlEpName] = ""
_, errRsp = procJoinEndpoint(c, vars, jlb)
if errRsp == &successResponse {
t.Fatalf("Expected failure, got: %v", errRsp)
}
vars[urlEpName] = "epoint"
delete(vars, urlCnID)
_, errRsp = procJoinEndpoint(c, vars, jlb)
if errRsp == &successResponse {
t.Fatalf("Expected failure, got: %v", errRsp)
}
vars[urlCnID] = "who?"
_, errRsp = procJoinEndpoint(c, vars, jlb)
if errRsp == &successResponse {
t.Fatalf("Expected failure, got: %v", errRsp)
}
vars[urlCnID] = cid
vars[urlEpName] = "endpoint"
cdi, errRsp := procJoinEndpoint(c, vars, jlb)
if errRsp != &successResponse {
t.Fatalf("Unexpected failure, got: %v", errRsp)
t.Fatalf("Expected failure, got: %v", errRsp)
}
cd := i2c(cdi)
if cd.SandboxKey == "" {
t.Fatalf("Empty sandbox key")
@@ -897,6 +854,7 @@ func TestJoinLeave(t *testing.T) {
}
delete(vars, urlCnID)
vars[urlEpName] = "endpoint"
_, errRsp = procLeaveEndpoint(c, vars, jlb)
if errRsp == &successResponse {
t.Fatalf("Expected failure, got: %v", errRsp)
@@ -1178,7 +1136,7 @@ func TestHttpHandlerUninit(t *testing.T) {
}
rsp := newWriter()
req, err := http.NewRequest("GET", "/networks", nil)
req, err := http.NewRequest("GET", "/v1.19/networks", nil)
if err != nil {
t.Fatal(err)
}
@@ -1193,15 +1151,24 @@ func TestHttpHandlerUninit(t *testing.T) {
handleRequest(rsp, req)
if rsp.statusCode != http.StatusOK {
t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
t.Fatalf("Expected (%d). Got: (%d): %s", http.StatusOK, rsp.statusCode, rsp.body)
}
n, err := c.NewNetwork(bridgeNetType, "onenet", nil)
var list []*networkResource
err = json.Unmarshal(rsp.body, &list)
if err != nil {
t.Fatal(err)
}
if len(list) != 0 {
t.Fatalf("Expected empty list. Got %v", list)
}
n, err := c.NewNetwork(bridgeNetType, "didietro", nil)
if err != nil {
t.Fatal(err)
}
nwr := buildNetworkResource(n)
expected, err := json.Marshal([]networkResource{*nwr})
expected, err := json.Marshal([]*networkResource{nwr})
if err != nil {
t.Fatal(err)
}
@@ -1229,7 +1196,7 @@ func TestHttpHandlerBadBody(t *testing.T) {
}
handleRequest := NewHTTPHandler(c)
req, err := http.NewRequest("POST", "/networks/name/zero-network", &localReader{beBad: true})
req, err := http.NewRequest("POST", "/v1.19/networks", &localReader{beBad: true})
if err != nil {
t.Fatal(err)
}
@@ -1240,7 +1207,7 @@ func TestHttpHandlerBadBody(t *testing.T) {
body := []byte{}
lr := newLocalReader(body)
req, err = http.NewRequest("POST", "/networks/name/zero-network", lr)
req, err = http.NewRequest("POST", "/v1.19/networks", lr)
if err != nil {
t.Fatal(err)
}
@@ -1250,7 +1217,7 @@ func TestHttpHandlerBadBody(t *testing.T) {
}
}
func TestHttpHandlerGood(t *testing.T) {
func TestEndToEnd(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
rsp := newWriter()
@@ -1261,14 +1228,14 @@ func TestHttpHandlerGood(t *testing.T) {
}
handleRequest := NewHTTPHandler(c)
nc := networkCreate{Name: "zero-network", NetworkType: bridgeNetType}
// Create network
nc := networkCreate{Name: "network-fiftyfive", NetworkType: bridgeNetType}
body, err := json.Marshal(nc)
if err != nil {
t.Fatal(err)
}
lr := newLocalReader(body)
req, err := http.NewRequest("POST", "/networks/name/zero-network", lr)
req, err := http.NewRequest("POST", "/v1.19/networks", lr)
if err != nil {
t.Fatal(err)
}
@@ -1280,13 +1247,102 @@ func TestHttpHandlerGood(t *testing.T) {
t.Fatalf("Empty response body")
}
var id string
err = json.Unmarshal(rsp.body, &id)
var nid string
err = json.Unmarshal(rsp.body, &nid)
if err != nil {
t.Fatal(err)
}
req, err = http.NewRequest("GET", "/networks/id/"+id, nil)
// Query networks collection
req, err = http.NewRequest("GET", "/v1.19/networks", nil)
if err != nil {
t.Fatal(err)
}
handleRequest(rsp, req)
if rsp.statusCode != http.StatusOK {
t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body)
}
b0 := make([]byte, len(rsp.body))
copy(b0, rsp.body)
req, err = http.NewRequest("GET", "/v1.19/networks?name=network-fiftyfive", nil)
if err != nil {
t.Fatal(err)
}
handleRequest(rsp, req)
if rsp.statusCode != http.StatusOK {
t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body)
}
if !bytes.Equal(b0, rsp.body) {
t.Fatalf("Expected same body from GET /networks and GET /networks?name=<nw> when only network <nw> exist.")
}
// Query network by name
req, err = http.NewRequest("GET", "/v1.19/networks?name=culo", nil)
if err != nil {
t.Fatal(err)
}
handleRequest(rsp, req)
if rsp.statusCode != http.StatusOK {
t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body)
}
var list []*networkResource
err = json.Unmarshal(rsp.body, &list)
if err != nil {
t.Fatal(err)
}
if len(list) != 0 {
t.Fatalf("Expected empty list. Got %v", list)
}
req, err = http.NewRequest("GET", "/v1.19/networks?name=network-fiftyfive", nil)
if err != nil {
t.Fatal(err)
}
handleRequest(rsp, req)
if rsp.statusCode != http.StatusOK {
t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
}
err = json.Unmarshal(rsp.body, &list)
if err != nil {
t.Fatal(err)
}
if len(list) == 0 {
t.Fatalf("Expected non empty list")
}
if list[0].Name != "network-fiftyfive" || nid != list[0].ID {
t.Fatalf("Incongruent resource found: %v", list[0])
}
// Query network by partial id
chars := []byte(nid)
partial := string(chars[0 : len(chars)/2])
req, err = http.NewRequest("GET", "/v1.19/networks?partial-id="+partial, nil)
if err != nil {
t.Fatal(err)
}
handleRequest(rsp, req)
if rsp.statusCode != http.StatusOK {
t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
}
err = json.Unmarshal(rsp.body, &list)
if err != nil {
t.Fatal(err)
}
if len(list) == 0 {
t.Fatalf("Expected non empty list")
}
if list[0].Name != "network-fiftyfive" || nid != list[0].ID {
t.Fatalf("Incongruent resource found: %v", list[0])
}
// Get network by id
req, err = http.NewRequest("GET", "/v1.19/networks/"+nid, nil)
if err != nil {
t.Fatal(err)
}
@@ -1300,7 +1356,211 @@ func TestHttpHandlerGood(t *testing.T) {
if err != nil {
t.Fatal(err)
}
if nwr.Name != "zero-network" || id != nwr.ID {
t.Fatalf("Incongruent resource found")
if nwr.Name != "network-fiftyfive" || nid != nwr.ID {
t.Fatalf("Incongruent resource found: %v", nwr)
}
// Create endpoint
eb, err := json.Marshal(endpointCreate{Name: "ep-TwentyTwo"})
if err != nil {
t.Fatal(err)
}
lr = newLocalReader(eb)
req, err = http.NewRequest("POST", "/v1.19/networks/"+nid+"/endpoints", lr)
if err != nil {
t.Fatal(err)
}
handleRequest(rsp, req)
if rsp.statusCode != http.StatusCreated {
t.Fatalf("Unexpectded status code. Expected (%d). Got (%d): %s.", http.StatusCreated, rsp.statusCode, string(rsp.body))
}
if len(rsp.body) == 0 {
t.Fatalf("Empty response body")
}
var eid string
err = json.Unmarshal(rsp.body, &eid)
if err != nil {
t.Fatal(err)
}
// Query endpoint(s)
req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints", nil)
if err != nil {
t.Fatal(err)
}
handleRequest(rsp, req)
if rsp.statusCode != http.StatusOK {
t.Fatalf("Expected StatusOK. Got (%d): %s", rsp.statusCode, rsp.body)
}
req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints?name=bla", nil)
if err != nil {
t.Fatal(err)
}
handleRequest(rsp, req)
if rsp.statusCode != http.StatusOK {
t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
}
var epList []*endpointResource
err = json.Unmarshal(rsp.body, &epList)
if err != nil {
t.Fatal(err)
}
if len(epList) != 0 {
t.Fatalf("Expected empty list. Got %v", epList)
}
// Query endpoint by name
req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints?name=ep-TwentyTwo", nil)
if err != nil {
t.Fatal(err)
}
handleRequest(rsp, req)
if rsp.statusCode != http.StatusOK {
t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
}
err = json.Unmarshal(rsp.body, &epList)
if err != nil {
t.Fatal(err)
}
if len(epList) == 0 {
t.Fatalf("Empty response body")
}
if epList[0].Name != "ep-TwentyTwo" || eid != epList[0].ID {
t.Fatalf("Incongruent resource found: %v", epList[0])
}
// Query endpoint by partial id
chars = []byte(eid)
partial = string(chars[0 : len(chars)/2])
req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints?partial-id="+partial, nil)
if err != nil {
t.Fatal(err)
}
handleRequest(rsp, req)
if rsp.statusCode != http.StatusOK {
t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
}
err = json.Unmarshal(rsp.body, &epList)
if err != nil {
t.Fatal(err)
}
if len(epList) == 0 {
t.Fatalf("Empty response body")
}
if epList[0].Name != "ep-TwentyTwo" || eid != epList[0].ID {
t.Fatalf("Incongruent resource found: %v", epList[0])
}
// Get endpoint by id
req, err = http.NewRequest("GET", "/v1.19/networks/"+nid+"/endpoints/"+eid, nil)
if err != nil {
t.Fatal(err)
}
handleRequest(rsp, req)
if rsp.statusCode != http.StatusOK {
t.Fatalf("Unexpectded failure: (%d): %s", rsp.statusCode, rsp.body)
}
var epr endpointResource
err = json.Unmarshal(rsp.body, &epr)
if err != nil {
t.Fatal(err)
}
if epr.Name != "ep-TwentyTwo" || epr.ID != eid {
t.Fatalf("Incongruent resource found: %v", epr)
}
}
type bre struct{}
func (b *bre) Error() string {
return "I am a bad request error"
}
func (b *bre) BadRequest() {}
type nfe struct{}
func (n *nfe) Error() string {
return "I am a not found error"
}
func (n *nfe) NotFound() {}
type forb struct{}
func (f *forb) Error() string {
return "I am a bad request error"
}
func (f *forb) Forbidden() {}
type notimpl struct{}
func (nip *notimpl) Error() string {
return "I am a not implemented error"
}
func (nip *notimpl) NotImplemented() {}
type inter struct{}
func (it *inter) Error() string {
return "I am a internal error"
}
func (it *inter) Internal() {}
type tout struct{}
func (to *tout) Error() string {
return "I am a timeout error"
}
func (to *tout) Timeout() {}
type noserv struct{}
func (nos *noserv) Error() string {
return "I am a no service error"
}
func (nos *noserv) NoService() {}
type notclassified struct{}
func (noc *notclassified) Error() string {
return "I am a non classified error"
}
func TestErrorConversion(t *testing.T) {
if convertNetworkError(new(bre)).StatusCode != http.StatusBadRequest {
t.Fatalf("Failed to recognize BadRequest error")
}
if convertNetworkError(new(nfe)).StatusCode != http.StatusNotFound {
t.Fatalf("Failed to recognize NotFound error")
}
if convertNetworkError(new(forb)).StatusCode != http.StatusForbidden {
t.Fatalf("Failed to recognize Forbidden error")
}
if convertNetworkError(new(notimpl)).StatusCode != http.StatusNotImplemented {
t.Fatalf("Failed to recognize NotImplemented error")
}
if convertNetworkError(new(inter)).StatusCode != http.StatusInternalServerError {
t.Fatalf("Failed to recognize Internal error")
}
if convertNetworkError(new(tout)).StatusCode != http.StatusRequestTimeout {
t.Fatalf("Failed to recognize Timeout error")
}
if convertNetworkError(new(noserv)).StatusCode != http.StatusServiceUnavailable {
t.Fatalf("Failed to recognize No Service error")
}
if convertNetworkError(new(notclassified)).StatusCode != http.StatusInternalServerError {
t.Fatalf("Failed to recognize not classified error as Internal error")
}
}
+3 -4
View File
@@ -1,6 +1,6 @@
package api
import "github.com/docker/libnetwork/netutils"
import "github.com/docker/libnetwork/types"
/***********
Resources
@@ -35,9 +35,8 @@ type networkCreate struct {
// endpointCreate represents the body of the "create endpoint" http request message
type endpointCreate struct {
Name string
NetworkID string
ExposedPorts []netutils.TransportPort
PortMapping []netutils.PortBinding
ExposedPorts []types.TransportPort
PortMapping []types.PortBinding
}
// endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages
+6
View File
@@ -49,6 +49,12 @@ func (cli *NetworkCli) getMethod(args ...string) (func(string, ...string) error,
// Cmd is borrowed from Docker UI and acts as the entry point for network UI commands.
// network UI commands are designed to be invoked from multiple parent chains
func (cli *NetworkCli) Cmd(chain string, args ...string) error {
if len(args) > 2 {
method, exists := cli.getMethod(args[:3]...)
if exists {
return method(chain+" "+args[0]+" "+args[1], args[3:]...)
}
}
if len(args) > 1 {
method, exists := cli.getMethod(args[:2]...)
if exists {
+124
View File
@@ -0,0 +1,124 @@
// +build experimental
package client
import (
"bytes"
"testing"
_ "github.com/docker/libnetwork/netutils"
)
func TestClientNetworkServiceInvalidCommand(t *testing.T) {
var out, errOut bytes.Buffer
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "service", "invalid")
if err == nil {
t.Fatalf("Passing invalid commands must fail")
}
}
func TestClientNetworkServiceCreate(t *testing.T) {
var out, errOut bytes.Buffer
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "service", "create", mockServiceName, mockNwName)
if err != nil {
t.Fatal(err.Error())
}
}
func TestClientNetworkServiceRm(t *testing.T) {
var out, errOut bytes.Buffer
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "service", "rm", mockServiceName, mockNwName)
if err != nil {
t.Fatal(err.Error())
}
}
func TestClientNetworkServiceLs(t *testing.T) {
var out, errOut bytes.Buffer
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "service", "ls", mockNwName)
if err != nil {
t.Fatal(err.Error())
}
}
func TestClientNetworkServiceInfo(t *testing.T) {
var out, errOut bytes.Buffer
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "service", "info", mockServiceName, mockNwName)
if err != nil {
t.Fatal(err.Error())
}
}
func TestClientNetworkServiceInfoById(t *testing.T) {
var out, errOut bytes.Buffer
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "service", "info", mockServiceID, mockNwID)
if err != nil {
t.Fatal(err.Error())
}
}
func TestClientNetworkServiceJoin(t *testing.T) {
var out, errOut bytes.Buffer
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "service", "join", mockContainerID, mockServiceName, mockNwName)
if err != nil {
t.Fatal(err.Error())
}
}
func TestClientNetworkServiceLeave(t *testing.T) {
var out, errOut bytes.Buffer
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "service", "leave", mockContainerID, mockServiceName, mockNwName)
if err != nil {
t.Fatal(err.Error())
}
}
// Docker Flag processing in flag.go uses os.Exit() frequently, even for --help
// TODO : Handle the --help test-case in the IT when CLI is available
/*
func TestClientNetworkServiceCreateHelp(t *testing.T) {
var out, errOut bytes.Buffer
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
return nil, 0, nil
}
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "create", "--help")
if err != nil {
t.Fatalf(err.Error())
}
}
*/
// Docker flag processing in flag.go uses os.Exit(1) for incorrect parameter case.
// TODO : Handle the missing argument case in the IT when CLI is available
/*
func TestClientNetworkServiceCreateMissingArgument(t *testing.T) {
var out, errOut bytes.Buffer
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
return nil, 0, nil
}
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "create")
if err != nil {
t.Fatal(err.Error())
}
}
*/
+102 -44
View File
@@ -2,7 +2,11 @@ package client
import (
"bytes"
"encoding/json"
"fmt"
"io"
"os"
"strings"
"testing"
_ "github.com/docker/libnetwork/netutils"
@@ -15,12 +19,82 @@ type nopCloser struct {
func (nopCloser) Close() error { return nil }
func TestMain(m *testing.M) {
setupMockHTTPCallback()
os.Exit(m.Run())
}
var callbackFunc func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error)
var mockNwJSON, mockNwListJSON, mockServiceJSON, mockServiceListJSON []byte
var mockNwName = "test"
var mockNwID = "2a3456789"
var mockServiceName = "testSrv"
var mockServiceID = "2a3456789"
var mockContainerID = "2a3456789"
func setupMockHTTPCallback() {
var list []networkResource
nw := networkResource{Name: mockNwName, ID: mockNwID}
mockNwJSON, _ = json.Marshal(nw)
list = append(list, nw)
mockNwListJSON, _ = json.Marshal(list)
var srvList []endpointResource
ep := endpointResource{Name: mockServiceName, ID: mockServiceID, Network: mockNwName}
mockServiceJSON, _ = json.Marshal(ep)
srvList = append(srvList, ep)
mockServiceListJSON, _ = json.Marshal(srvList)
callbackFunc = func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
var rsp string
switch method {
case "GET":
if strings.Contains(path, fmt.Sprintf("networks?name=%s", mockNwName)) {
rsp = string(mockNwListJSON)
} else if strings.Contains(path, "networks?name=") {
rsp = "[]"
} else if strings.Contains(path, fmt.Sprintf("networks?partial-id=%s", mockNwID)) {
rsp = string(mockNwListJSON)
} else if strings.Contains(path, "networks?partial-id=") {
rsp = "[]"
} else if strings.HasSuffix(path, "networks") {
rsp = string(mockNwListJSON)
} else if strings.HasSuffix(path, "networks/"+mockNwID) {
rsp = string(mockNwJSON)
} else if strings.Contains(path, fmt.Sprintf("endpoints?name=%s", mockServiceName)) {
rsp = string(mockServiceListJSON)
} else if strings.Contains(path, "endpoints?name=") {
rsp = "[]"
} else if strings.Contains(path, fmt.Sprintf("endpoints?partial-id=%s", mockServiceID)) {
rsp = string(mockServiceListJSON)
} else if strings.Contains(path, "endpoints?partial-id=") {
rsp = "[]"
} else if strings.HasSuffix(path, "endpoints") {
rsp = string(mockServiceListJSON)
} else if strings.HasSuffix(path, "endpoints/"+mockServiceID) {
rsp = string(mockServiceJSON)
}
case "POST":
var data []byte
if strings.HasSuffix(path, "networks") {
data, _ = json.Marshal(mockNwID)
} else if strings.HasSuffix(path, "endpoints") {
data, _ = json.Marshal(mockServiceID)
} else if strings.HasSuffix(path, "containers") {
data, _ = json.Marshal(mockContainerID)
}
rsp = string(data)
case "PUT":
case "DELETE":
rsp = ""
}
return nopCloser{bytes.NewBufferString(rsp)}, 200, nil
}
}
func TestClientDummyCommand(t *testing.T) {
var out, errOut bytes.Buffer
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
return nopCloser{bytes.NewBufferString("")}, 200, nil
}
cli := NewNetworkCli(&out, &errOut, cFunc)
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "dummy")
if err == nil {
@@ -30,10 +104,7 @@ func TestClientDummyCommand(t *testing.T) {
func TestClientNetworkInvalidCommand(t *testing.T) {
var out, errOut bytes.Buffer
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
return nopCloser{bytes.NewBufferString("")}, 200, nil
}
cli := NewNetworkCli(&out, &errOut, cFunc)
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "invalid")
if err == nil {
@@ -43,12 +114,9 @@ func TestClientNetworkInvalidCommand(t *testing.T) {
func TestClientNetworkCreate(t *testing.T) {
var out, errOut bytes.Buffer
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
return nopCloser{bytes.NewBufferString("")}, 200, nil
}
cli := NewNetworkCli(&out, &errOut, cFunc)
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "create", "test")
err := cli.Cmd("docker", "network", "create", mockNwName)
if err != nil {
t.Fatal(err.Error())
}
@@ -56,17 +124,14 @@ func TestClientNetworkCreate(t *testing.T) {
func TestClientNetworkCreateWithDriver(t *testing.T) {
var out, errOut bytes.Buffer
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
return nopCloser{bytes.NewBufferString("")}, 200, nil
}
cli := NewNetworkCli(&out, &errOut, cFunc)
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "create", "-f=dummy", "test")
err := cli.Cmd("docker", "network", "create", "-f=dummy", mockNwName)
if err == nil {
t.Fatalf("Passing incorrect flags to the create command must fail")
}
err = cli.Cmd("docker", "network", "create", "-d=dummy", "test")
err = cli.Cmd("docker", "network", "create", "-d=dummy", mockNwName)
if err != nil {
t.Fatalf(err.Error())
}
@@ -74,12 +139,9 @@ func TestClientNetworkCreateWithDriver(t *testing.T) {
func TestClientNetworkRm(t *testing.T) {
var out, errOut bytes.Buffer
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
return nopCloser{bytes.NewBufferString("")}, 200, nil
}
cli := NewNetworkCli(&out, &errOut, cFunc)
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "rm", "test")
err := cli.Cmd("docker", "network", "rm", mockNwName)
if err != nil {
t.Fatal(err.Error())
}
@@ -87,47 +149,43 @@ func TestClientNetworkRm(t *testing.T) {
func TestClientNetworkLs(t *testing.T) {
var out, errOut bytes.Buffer
networks := "db,web,test"
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
return nopCloser{bytes.NewBufferString(networks)}, 200, nil
}
cli := NewNetworkCli(&out, &errOut, cFunc)
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "ls")
if err != nil {
t.Fatal(err.Error())
}
if out.String() != networks {
t.Fatal("Network List command fail to return the intended list")
}
}
func TestClientNetworkInfo(t *testing.T) {
var out, errOut bytes.Buffer
info := "dummy info"
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
return nopCloser{bytes.NewBufferString(info)}, 200, nil
}
cli := NewNetworkCli(&out, &errOut, cFunc)
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "info", "test")
err := cli.Cmd("docker", "network", "info", mockNwName)
if err != nil {
t.Fatal(err.Error())
}
if out.String() != info {
t.Fatal("Network List command fail to return the intended list")
}
func TestClientNetworkInfoById(t *testing.T) {
var out, errOut bytes.Buffer
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "info", mockNwID)
if err != nil {
t.Fatal(err.Error())
}
}
// Docker Flag processing in flag.go uses os.Exit() frequently, even for --help
// TODO : Handle the --help test-case in the IT when CLI is available
/*
func TestClientNetworkCreateHelp(t *testing.T) {
func TestClientNetworkServiceCreateHelp(t *testing.T) {
var out, errOut bytes.Buffer
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
return nil, 0, nil
}
cli := NewNetworkCli(&out, &errOut, cFunc)
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "create", "--help")
if err != nil {
@@ -139,12 +197,12 @@ func TestClientNetworkCreateHelp(t *testing.T) {
// Docker flag processing in flag.go uses os.Exit(1) for incorrect parameter case.
// TODO : Handle the missing argument case in the IT when CLI is available
/*
func TestClientNetworkCreateMissingArgument(t *testing.T) {
func TestClientNetworkServiceCreateMissingArgument(t *testing.T) {
var out, errOut bytes.Buffer
cFunc := func(method, path string, data interface{}, headers map[string][]string) (io.ReadCloser, int, error) {
return nil, 0, nil
}
cli := NewNetworkCli(&out, &errOut, cFunc)
cli := NewNetworkCli(&out, &errOut, callbackFunc)
err := cli.Cmd("docker", "network", "create")
if err != nil {
+125 -16
View File
@@ -2,10 +2,13 @@ package client
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"text/tabwriter"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/stringid"
)
const (
@@ -33,7 +36,7 @@ func (cli *NetworkCli) CmdNetwork(chain string, args ...string) error {
err := cmd.ParseFlags(args, true)
if err == nil {
cmd.Usage()
return fmt.Errorf("Invalid command : %v", args)
return fmt.Errorf("invalid command : %v", args)
}
return err
}
@@ -53,31 +56,33 @@ func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error {
nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver}
obj, _, err := readBody(cli.call("POST", "/networks/name/"+cmd.Arg(0), nc, nil))
obj, _, err := readBody(cli.call("POST", "/networks", nc, nil))
if err != nil {
fmt.Fprintf(cli.err, "%s", err.Error())
return err
}
if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil {
var replyID string
err = json.Unmarshal(obj, &replyID)
if err != nil {
return err
}
fmt.Fprintf(cli.out, "%s\n", replyID)
return nil
}
// CmdNetworkRm handles Network Delete UI
func (cli *NetworkCli) CmdNetworkRm(chain string, args ...string) error {
cmd := cli.Subcmd(chain, "rm", "NETWORK-NAME", "Deletes a network", false)
cmd := cli.Subcmd(chain, "rm", "NETWORK", "Deletes a network", false)
cmd.Require(flag.Min, 1)
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
obj, _, err := readBody(cli.call("DELETE", "/networks/name/"+cmd.Arg(0), nil, nil))
id, err := lookupNetworkID(cli, cmd.Arg(0))
if err != nil {
fmt.Fprintf(cli.err, "%s", err.Error())
return err
}
if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil {
_, _, err = readBody(cli.call("DELETE", "/networks/"+id, nil, nil))
if err != nil {
return err
}
return nil
@@ -86,45 +91,149 @@ func (cli *NetworkCli) CmdNetworkRm(chain string, args ...string) error {
// CmdNetworkLs handles Network List UI
func (cli *NetworkCli) CmdNetworkLs(chain string, args ...string) error {
cmd := cli.Subcmd(chain, "ls", "", "Lists all the networks created by the user", false)
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output")
nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show the latest network created")
last := cmd.Int([]string{"n"}, -1, "Show n last created networks")
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
obj, _, err := readBody(cli.call("GET", "/networks", nil, nil))
if err != nil {
fmt.Fprintf(cli.err, "%s", err.Error())
return err
}
if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil {
if *last == -1 && *nLatest {
*last = 1
}
var networkResources []networkResource
err = json.Unmarshal(obj, &networkResources)
if err != nil {
return err
}
wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
// unless quiet (-q) is specified, print field titles
if !*quiet {
fmt.Fprintln(wr, "NETWORK ID\tNAME\tTYPE")
}
for _, networkResource := range networkResources {
ID := networkResource.ID
netName := networkResource.Name
if !*noTrunc {
ID = stringid.TruncateID(ID)
}
if *quiet {
fmt.Fprintln(wr, ID)
continue
}
netType := networkResource.Type
fmt.Fprintf(wr, "%s\t%s\t%s\t",
ID,
netName,
netType)
fmt.Fprint(wr, "\n")
}
wr.Flush()
return nil
}
// CmdNetworkInfo handles Network Info UI
func (cli *NetworkCli) CmdNetworkInfo(chain string, args ...string) error {
cmd := cli.Subcmd(chain, "info", "NETWORK-NAME", "Displays detailed information on a network", false)
cmd := cli.Subcmd(chain, "info", "NETWORK", "Displays detailed information on a network", false)
cmd.Require(flag.Min, 1)
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
obj, _, err := readBody(cli.call("GET", "/networks/name/"+cmd.Arg(0), nil, nil))
id, err := lookupNetworkID(cli, cmd.Arg(0))
if err != nil {
fmt.Fprintf(cli.err, "%s", err.Error())
return err
}
if _, err := io.Copy(cli.out, bytes.NewReader(obj)); err != nil {
obj, _, err := readBody(cli.call("GET", "/networks/"+id, nil, nil))
if err != nil {
return err
}
networkResource := &networkResource{}
if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil {
return err
}
fmt.Fprintf(cli.out, "Network Id: %s\n", networkResource.ID)
fmt.Fprintf(cli.out, "Name: %s\n", networkResource.Name)
fmt.Fprintf(cli.out, "Type: %s\n", networkResource.Type)
if networkResource.Endpoints != nil {
for _, endpointResource := range networkResource.Endpoints {
fmt.Fprintf(cli.out, " Service Id: %s\n", endpointResource.ID)
fmt.Fprintf(cli.out, "\tName: %s\n", endpointResource.Name)
}
}
return nil
}
// Helper function to predict if a string is a name or id or partial-id
// This provides a best-effort mechanism to identify a id with the help of GET Filter APIs
// Being a UI, its most likely that name will be used by the user, which is used to lookup
// the corresponding ID. If ID is not found, this function will assume that the passed string
// is an ID by itself.
func lookupNetworkID(cli *NetworkCli, nameID string) (string, error) {
obj, statusCode, err := readBody(cli.call("GET", "/networks?name="+nameID, nil, nil))
if err != nil {
return "", err
}
if statusCode != http.StatusOK {
return "", fmt.Errorf("name query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
}
var list []*networkResource
err = json.Unmarshal(obj, &list)
if err != nil {
return "", err
}
if len(list) > 0 {
// name query filter will always return a single-element collection
return list[0].ID, nil
}
// Check for Partial-id
obj, statusCode, err = readBody(cli.call("GET", "/networks?partial-id="+nameID, nil, nil))
if err != nil {
return "", err
}
if statusCode != http.StatusOK {
return "", fmt.Errorf("partial-id match query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
}
err = json.Unmarshal(obj, &list)
if err != nil {
return "", err
}
if len(list) == 0 {
return "", fmt.Errorf("resource not found %s", nameID)
}
if len(list) > 1 {
return "", fmt.Errorf("multiple Networks matching the partial identifier (%s). Please use full identifier", nameID)
}
return list[0].ID, nil
}
func networkUsage(chain string) string {
help := "Commands:\n"
for _, cmd := range networkCommands {
help += fmt.Sprintf(" %-10.10s%s\n", cmd.name, cmd.description)
help += fmt.Sprintf(" %-25.25s%s\n", cmd.name, cmd.description)
}
for _, cmd := range serviceCommands {
help += fmt.Sprintf(" %-25.25s%s\n", "service "+cmd.name, cmd.description)
}
help += fmt.Sprintf("\nRun '%s network COMMAND --help' for more information on a command.", chain)
+7
View File
@@ -0,0 +1,7 @@
// +build !experimental
package client
var (
serviceCommands = []command{}
)
+317
View File
@@ -0,0 +1,317 @@
// +build experimental
package client
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"text/tabwriter"
flag "github.com/docker/docker/pkg/mflag"
"github.com/docker/docker/pkg/stringid"
)
var (
serviceCommands = []command{
{"create", "Create a service endpoint"},
{"rm", "Remove a service endpoint"},
{"join", "Join a container to a service endpoint"},
{"leave", "Leave a container from a service endpoint"},
{"ls", "Lists all service endpoints on a network"},
{"info", "Display information of a service endpoint"},
}
)
func lookupServiceID(cli *NetworkCli, networkID string, nameID string) (string, error) {
obj, statusCode, err := readBody(cli.call("GET", fmt.Sprintf("/networks/%s/endpoints?name=%s", networkID, nameID), nil, nil))
if err != nil {
return "", err
}
if statusCode != http.StatusOK {
return "", fmt.Errorf("name query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
}
var list []*networkResource
err = json.Unmarshal(obj, &list)
if err != nil {
return "", err
}
if len(list) > 0 {
// name query filter will always return a single-element collection
return list[0].ID, nil
}
// Check for Partial-id
obj, statusCode, err = readBody(cli.call("GET", fmt.Sprintf("/networks/%s/endpoints?partial-id=%s", networkID, nameID), nil, nil))
if err != nil {
return "", err
}
if statusCode != http.StatusOK {
return "", fmt.Errorf("partial-id match query failed for %s due to : statuscode(%d) %v", nameID, statusCode, string(obj))
}
err = json.Unmarshal(obj, &list)
if err != nil {
return "", err
}
if len(list) == 0 {
return "", fmt.Errorf("resource not found %s", nameID)
}
if len(list) > 1 {
return "", fmt.Errorf("multiple services matching the partial identifier (%s). Please use full identifier", nameID)
}
return list[0].ID, nil
}
func lookupContainerID(cli *NetworkCli, nameID string) (string, error) {
// TODO : containerID to sandbox-key ?
return nameID, nil
}
// CmdNetworkService handles the network service UI
func (cli *NetworkCli) CmdNetworkService(chain string, args ...string) error {
cmd := cli.Subcmd(chain, "service", "COMMAND [OPTIONS] [arg...]", serviceUsage(chain), false)
cmd.Require(flag.Min, 1)
err := cmd.ParseFlags(args, true)
if err == nil {
cmd.Usage()
return fmt.Errorf("Invalid command : %v", args)
}
return err
}
// CmdNetworkServiceCreate handles service create UI
func (cli *NetworkCli) CmdNetworkServiceCreate(chain string, args ...string) error {
cmd := cli.Subcmd(chain, "create", "SERVICE NETWORK", "Creates a new service on a network", false)
cmd.Require(flag.Min, 2)
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
networkID, err := lookupNetworkID(cli, cmd.Arg(1))
if err != nil {
return err
}
ec := endpointCreate{Name: cmd.Arg(0), NetworkID: networkID}
obj, _, err := readBody(cli.call("POST", "/networks/"+networkID+"/endpoints", ec, nil))
if err != nil {
return err
}
var replyID string
err = json.Unmarshal(obj, &replyID)
if err != nil {
return err
}
fmt.Fprintf(cli.out, "%s\n", replyID)
return nil
}
// CmdNetworkServiceRm handles service delete UI
func (cli *NetworkCli) CmdNetworkServiceRm(chain string, args ...string) error {
cmd := cli.Subcmd(chain, "rm", "SERVICE NETWORK", "Deletes a service", false)
cmd.Require(flag.Min, 2)
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
networkID, err := lookupNetworkID(cli, cmd.Arg(1))
if err != nil {
return err
}
serviceID, err := lookupServiceID(cli, networkID, cmd.Arg(0))
if err != nil {
return err
}
_, _, err = readBody(cli.call("DELETE", "/networks/"+networkID+"/endpoints/"+serviceID, nil, nil))
if err != nil {
return err
}
return nil
}
// CmdNetworkServiceLs handles service list UI
func (cli *NetworkCli) CmdNetworkServiceLs(chain string, args ...string) error {
cmd := cli.Subcmd(chain, "ls", "NETWORK", "Lists all the services on a network", false)
quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
noTrunc := cmd.Bool([]string{"#notrunc", "-no-trunc"}, false, "Do not truncate the output")
nLatest := cmd.Bool([]string{"l", "-latest"}, false, "Show the latest network created")
last := cmd.Int([]string{"n"}, -1, "Show n last created networks")
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
cmd.Require(flag.Min, 1)
networkID, err := lookupNetworkID(cli, cmd.Arg(0))
if err != nil {
return err
}
obj, _, err := readBody(cli.call("GET", "/networks/"+networkID+"/endpoints", nil, nil))
if err != nil {
fmt.Fprintf(cli.err, "%s", err.Error())
return err
}
if *last == -1 && *nLatest {
*last = 1
}
var endpointResources []endpointResource
err = json.Unmarshal(obj, &endpointResources)
if err != nil {
return err
}
wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
// unless quiet (-q) is specified, print field titles
if !*quiet {
fmt.Fprintln(wr, "NETWORK SERVICE ID\tNAME\tNETWORK")
}
for _, networkResource := range endpointResources {
ID := networkResource.ID
netName := networkResource.Name
if !*noTrunc {
ID = stringid.TruncateID(ID)
}
if *quiet {
fmt.Fprintln(wr, ID)
continue
}
network := networkResource.Network
fmt.Fprintf(wr, "%s\t%s\t%s",
ID,
netName,
network)
fmt.Fprint(wr, "\n")
}
wr.Flush()
return nil
}
// CmdNetworkServiceInfo handles service info UI
func (cli *NetworkCli) CmdNetworkServiceInfo(chain string, args ...string) error {
cmd := cli.Subcmd(chain, "info", "SERVICE NETWORK", "Displays detailed information on a service", false)
cmd.Require(flag.Min, 2)
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
networkID, err := lookupNetworkID(cli, cmd.Arg(1))
if err != nil {
return err
}
serviceID, err := lookupServiceID(cli, networkID, cmd.Arg(0))
if err != nil {
return err
}
obj, _, err := readBody(cli.call("GET", "/networks/"+networkID+"/endpoints/"+serviceID, nil, nil))
if err != nil {
fmt.Fprintf(cli.err, "%s", err.Error())
return err
}
endpointResource := &endpointResource{}
if err := json.NewDecoder(bytes.NewReader(obj)).Decode(endpointResource); err != nil {
return err
}
fmt.Fprintf(cli.out, "Service Id: %s\n", endpointResource.ID)
fmt.Fprintf(cli.out, "\tName: %s\n", endpointResource.Name)
fmt.Fprintf(cli.out, "\tNetwork: %s\n", endpointResource.Network)
return nil
}
// CmdNetworkServiceJoin handles service join UI
func (cli *NetworkCli) CmdNetworkServiceJoin(chain string, args ...string) error {
cmd := cli.Subcmd(chain, "join", "CONTAINER SERVICE NETWORK", "Sets a container as a service backend", false)
cmd.Require(flag.Min, 3)
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
containerID, err := lookupContainerID(cli, cmd.Arg(0))
if err != nil {
return err
}
networkID, err := lookupNetworkID(cli, cmd.Arg(2))
if err != nil {
return err
}
serviceID, err := lookupServiceID(cli, networkID, cmd.Arg(1))
if err != nil {
return err
}
nc := endpointJoin{ContainerID: containerID}
_, _, err = readBody(cli.call("POST", "/networks/"+networkID+"/endpoints/"+serviceID+"/containers", nc, nil))
if err != nil {
fmt.Fprintf(cli.err, "%s", err.Error())
return err
}
return nil
}
// CmdNetworkServiceLeave handles service leave UI
func (cli *NetworkCli) CmdNetworkServiceLeave(chain string, args ...string) error {
cmd := cli.Subcmd(chain, "leave", "CONTAINER SERVICE NETWORK", "Removes a container from service backend", false)
cmd.Require(flag.Min, 3)
err := cmd.ParseFlags(args, true)
if err != nil {
return err
}
containerID, err := lookupContainerID(cli, cmd.Arg(0))
if err != nil {
return err
}
networkID, err := lookupNetworkID(cli, cmd.Arg(2))
if err != nil {
return err
}
serviceID, err := lookupServiceID(cli, networkID, cmd.Arg(1))
if err != nil {
return err
}
_, _, err = readBody(cli.call("DELETE", "/networks/"+networkID+"/endpoints/"+serviceID+"/containers/"+containerID, nil, nil))
if err != nil {
fmt.Fprintf(cli.err, "%s", err.Error())
return err
}
return nil
}
func serviceUsage(chain string) string {
help := "Commands:\n"
for _, cmd := range serviceCommands {
help += fmt.Sprintf(" %-10.10s%s\n", cmd, cmd.description)
}
help += fmt.Sprintf("\nRun '%s service COMMAND --help' for more information on a command.", chain)
return help
}
+36 -2
View File
@@ -1,6 +1,6 @@
package client
import "github.com/docker/libnetwork/sandbox"
import "github.com/docker/libnetwork/types"
/***********
Resources
@@ -19,7 +19,6 @@ type endpointResource struct {
Name string
ID string
Network string
Info sandbox.Info
}
/***********
@@ -32,3 +31,38 @@ type networkCreate struct {
NetworkType string
Options map[string]interface{}
}
// endpointCreate represents the body of the "create endpoint" http request message
type endpointCreate struct {
Name string
NetworkID string
ExposedPorts []types.TransportPort
PortMapping []types.PortBinding
}
// endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages
type endpointJoin struct {
ContainerID string
HostName string
DomainName string
HostsPath string
ResolvConfPath string
DNS []string
ExtraHosts []endpointExtraHost
ParentUpdates []endpointParentUpdate
UseDefaultSandbox bool
}
// EndpointExtraHost represents the extra host object
type endpointExtraHost struct {
Name string
Address string
}
// EndpointParentUpdate is the object carrying the information about the
// endpoint parent that needs to be updated
type endpointParentUpdate struct {
EndpointID string
Name string
Address string
}
+5 -7
View File
@@ -113,10 +113,8 @@ func (d *dnetConnection) dnetDaemon() error {
}
httpHandler := api.NewHTTPHandler(controller)
r := mux.NewRouter().StrictSlash(false)
post := r.PathPrefix("/networks").Subrouter()
post.Methods("GET").HandlerFunc(httpHandler)
post.Methods("PUT", "POST").HandlerFunc(httpHandler)
post.Methods("DELETE").HandlerFunc(httpHandler)
post := r.PathPrefix("/{.*}/networks").Subrouter()
post.Methods("GET", "PUT", "POST", "DELETE").HandlerFunc(httpHandler)
return http.ListenAndServe(d.addr, r)
}
@@ -143,7 +141,7 @@ func (d *dnetConnection) httpCall(method, path string, data interface{}, headers
return nil, -1, err
}
req, err := http.NewRequest(method, fmt.Sprintf("%s", path), in)
req, err := http.NewRequest(method, fmt.Sprintf("/dnet%s", path), in)
if err != nil {
return nil, -1, err
}
@@ -160,7 +158,7 @@ func (d *dnetConnection) httpCall(method, path string, data interface{}, headers
statusCode = resp.StatusCode
}
if err != nil {
return nil, statusCode, fmt.Errorf("An error occurred trying to connect: %v", err)
return nil, statusCode, fmt.Errorf("error when trying to connect: %v", err)
}
if statusCode < 200 || statusCode >= 400 {
@@ -168,7 +166,7 @@ func (d *dnetConnection) httpCall(method, path string, data interface{}, headers
if err != nil {
return nil, statusCode, err
}
return nil, statusCode, fmt.Errorf("Error response from daemon: %s", bytes.TrimSpace(body))
return nil, statusCode, fmt.Errorf("error : %s", bytes.TrimSpace(body))
}
return resp.Body, statusCode, nil
+1 -1
View File
@@ -16,7 +16,7 @@ type byName []command
var (
flDaemon = flag.Bool([]string{"d", "-daemon"}, false, "Enable daemon mode")
flHost = flag.String([]string{"H", "-Host"}, "", "Daemon socket to connect to")
flHost = flag.String([]string{"H", "-host"}, "", "Daemon socket to connect to")
flLogLevel = flag.String([]string{"l", "-log-level"}, "info", "Set the logging level")
flDebug = flag.Bool([]string{"D", "-debug"}, false, "Enable debug mode")
flHelp = flag.Bool([]string{"h", "-help"}, false, "Print usage")
+2 -2
View File
@@ -5,8 +5,8 @@ import (
"github.com/docker/libnetwork"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/options"
"github.com/docker/libnetwork/types"
)
func main() {
@@ -58,7 +58,7 @@ func main() {
epInfo, err := ep.DriverInfo()
mapData, ok := epInfo[netlabel.PortMap]
if ok {
portMapping, ok := mapData.([]netutils.PortBinding)
portMapping, ok := mapData.([]types.PortBinding)
if ok {
fmt.Printf("Current port mapping for endpoint %s: %v", ep.Name(), portMapping)
}
+16 -6
View File
@@ -76,6 +76,9 @@ type NetworkController interface {
// NetworkByID returns the Network which has the passed id. If not found, the error ErrNoSuchNetwork is returned.
NetworkByID(id string) (Network, error)
// GC triggers immediate garbage collection of resources which are garbage collected.
GC()
}
// NetworkWalker is a client provided function which will be used to walk the Networks.
@@ -134,7 +137,7 @@ func (c *controller) RegisterDriver(networkType string, driver driverapi.Driver)
// are network specific and modeled in a generic way.
func (c *controller) NewNetwork(networkType, name string, options ...NetworkOption) (Network, error) {
if name == "" {
return nil, ErrInvalidName
return nil, ErrInvalidName(name)
}
// Check if a driver for the specified network type is available
c.Lock()
@@ -203,7 +206,7 @@ func (c *controller) WalkNetworks(walker NetworkWalker) {
func (c *controller) NetworkByName(name string) (Network, error) {
if name == "" {
return nil, ErrInvalidName
return nil, ErrInvalidName(name)
}
var n Network
@@ -218,7 +221,7 @@ func (c *controller) NetworkByName(name string) (Network, error) {
c.WalkNetworks(s)
if n == nil {
return nil, ErrNoSuchNetwork
return nil, ErrNoSuchNetwork(name)
}
return n, nil
@@ -226,14 +229,14 @@ func (c *controller) NetworkByName(name string) (Network, error) {
func (c *controller) NetworkByID(id string) (Network, error) {
if id == "" {
return nil, ErrInvalidID
return nil, ErrInvalidID(id)
}
c.Lock()
defer c.Unlock()
if n, ok := c.networks[types.UUID(id)]; ok {
return n, nil
}
return nil, ErrNoSuchNetwork
return nil, ErrNoSuchNetwork(id)
}
func (c *controller) sandboxAdd(key string, create bool) (sandbox.Sandbox, error) {
@@ -286,13 +289,20 @@ func (c *controller) loadDriver(networkType string) (driverapi.Driver, error) {
// As per the design, this Get call will result in remote driver discovery if there is a corresponding plugin available.
_, err := plugins.Get(networkType, driverapi.NetworkPluginEndpointType)
if err != nil {
if err == plugins.ErrNotFound {
return nil, types.NotFoundErrorf(err.Error())
}
return nil, err
}
c.Lock()
defer c.Unlock()
d, ok := c.drivers[networkType]
if !ok {
return nil, ErrInvalidNetworkDriver
return nil, ErrInvalidNetworkDriver(networkType)
}
return d, nil
}
func (c *controller) GC() {
sandbox.GC()
}
+1 -1
View File
@@ -6,7 +6,7 @@ This document describes how libnetwork has been designed in order to acheive thi
Requirements for individual releases can be found on the [Project Page](https://github.com/docker/libnetwork/wiki)
Many of the design decisions are inspired by the learnings from the Docker networking design as of Docker v1.6.
Please refer to this [Docker v1.6 Design](https://github.com/docker/libnetwork/blob/docs/legacy.md) document for more information on networking design as of Docker v1.6.
Please refer to this [Docker v1.6 Design](legacy.md) document for more information on networking design as of Docker v1.6.
## Goal
+2 -23
View File
@@ -1,24 +1,11 @@
package driverapi
import (
"errors"
"fmt"
"net"
"github.com/docker/libnetwork/types"
)
var (
// ErrEndpointExists is returned if more than one endpoint is added to the network
ErrEndpointExists = errors.New("Endpoint already exists (Only one endpoint allowed)")
// ErrNoNetwork is returned if no network with the specified id exists
ErrNoNetwork = errors.New("No network exists")
// ErrNoEndpoint is returned if no endpoint with the specified id exists
ErrNoEndpoint = errors.New("No endpoint exists")
// ErrNotImplemented is returned when a Driver has not implemented an API yet
ErrNotImplemented = errors.New("The API is not implemented yet")
)
// NetworkPluginEndpointType represents the Endpoint Type used by Plugin system
const NetworkPluginEndpointType = "NetworkDriver"
@@ -96,8 +83,8 @@ type InterfaceInfo interface {
// InterfaceNameInfo provides a go interface for the drivers to assign names
// to interfaces.
type InterfaceNameInfo interface {
// SetNames method assigns the srcName and dstName for the interface.
SetNames(srcName, dstName string) error
// SetNames method assigns the srcName and dstPrefix for the interface.
SetNames(srcName, dstPrefix string) error
// ID returns the numerical id that was assigned to the interface by the driver
// CreateEndpoint.
@@ -124,14 +111,6 @@ type JoinInfo interface {
SetResolvConfPath(string) error
}
// ErrActiveRegistration represents an error when a driver is registered to a networkType that is previously registered
type ErrActiveRegistration string
// Error interface for ErrActiveRegistration
func (ar ErrActiveRegistration) Error() string {
return fmt.Sprintf("Driver already registered for type %q", string(ar))
}
// DriverCallback provides a Callback interface for Drivers into LibNetwork
type DriverCallback interface {
// RegisterDriver provides a way for Remote drivers to dynamically register new NetworkType and associate with a driver instance
+56
View File
@@ -0,0 +1,56 @@
package driverapi
import (
"fmt"
)
// ErrNoNetwork is returned if no network with the specified id exists
type ErrNoNetwork string
func (enn ErrNoNetwork) Error() string {
return fmt.Sprintf("No network (%s) exists", string(enn))
}
// NotFound denotes the type of this error
func (enn ErrNoNetwork) NotFound() {}
// ErrEndpointExists is returned if more than one endpoint is added to the network
type ErrEndpointExists string
func (ee ErrEndpointExists) Error() string {
return fmt.Sprintf("Endpoint (%s) already exists (Only one endpoint allowed)", string(ee))
}
// Forbidden denotes the type of this error
func (ee ErrEndpointExists) Forbidden() {}
// ErrNotImplemented is returned when a Driver has not implemented an API yet
type ErrNotImplemented struct{}
func (eni *ErrNotImplemented) Error() string {
return "The API is not implemented yet"
}
// NotImplemented denotes the type of this error
func (eni *ErrNotImplemented) NotImplemented() {}
// ErrNoEndpoint is returned if no endpoint with the specified id exists
type ErrNoEndpoint string
func (ene ErrNoEndpoint) Error() string {
return fmt.Sprintf("No endpoint (%s) exists", string(ene))
}
// NotFound denotes the type of this error
func (ene ErrNoEndpoint) NotFound() {}
// ErrActiveRegistration represents an error when a driver is registered to a networkType that is previously registered
type ErrActiveRegistration string
// Error interface for ErrActiveRegistration
func (ar ErrActiveRegistration) Error() string {
return fmt.Sprintf("Driver already registered for type %q", string(ar))
}
// Forbidden denotes the type of this error
func (ar ErrActiveRegistration) Forbidden() {}
+133 -47
View File
@@ -2,12 +2,17 @@ package bridge
import (
"errors"
"fmt"
"net"
"os/exec"
"strings"
"sync"
"github.com/Sirupsen/logrus"
bri "github.com/docker/libcontainer/netlink"
"github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/ipallocator"
"github.com/docker/libnetwork/iptables"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/options"
@@ -21,7 +26,7 @@ const (
networkType = "bridge"
vethPrefix = "veth"
vethLen = 7
containerVeth = "eth0"
containerVethPrefix = "eth"
maxAllocatePortAttempts = 10
ifaceID = 1
)
@@ -57,8 +62,8 @@ type NetworkConfiguration struct {
// EndpointConfiguration represents the user specified configuration for the sandbox endpoint
type EndpointConfiguration struct {
MacAddress net.HardwareAddr
PortBindings []netutils.PortBinding
ExposedPorts []netutils.TransportPort
PortBindings []types.PortBinding
ExposedPorts []types.TransportPort
}
// ContainerConfiguration represents the user specified configuration for a container
@@ -73,7 +78,7 @@ type bridgeEndpoint struct {
macAddress net.HardwareAddr
config *EndpointConfiguration // User specified parameters
containerConfig *ContainerConfiguration
portMapping []netutils.PortBinding // Operation port bindings
portMapping []types.PortBinding // Operation port bindings
}
type bridgeNetwork struct {
@@ -102,6 +107,15 @@ func newDriver() driverapi.Driver {
// Init registers a new instance of bridge driver
func Init(dc driverapi.DriverCallback) error {
// try to modprobe bridge first
// see gh#12177
if out, err := exec.Command("modprobe", "-va", "bridge", "nf_nat", "br_netfilter").Output(); err != nil {
logrus.Warnf("Running modprobe bridge nf_nat failed with message: %s, error: %v", out, err)
}
if err := iptables.RemoveExistingChain(DockerChain, iptables.Nat); err != nil {
logrus.Warnf("Failed to remove existing iptables entries in %s : %v", DockerChain, err)
}
return dc.RegisterDriver(networkType, newDriver())
}
@@ -109,7 +123,7 @@ func Init(dc driverapi.DriverCallback) error {
// Whatever can be assessed a priori before attempting any programming.
func (c *NetworkConfiguration) Validate() error {
if c.Mtu < 0 {
return ErrInvalidMtu
return ErrInvalidMtu(c.Mtu)
}
// If bridge v4 subnet is specified
@@ -118,19 +132,19 @@ func (c *NetworkConfiguration) Validate() error {
if c.FixedCIDR != nil {
// Check Network address
if !c.AddressIPv4.Contains(c.FixedCIDR.IP) {
return ErrInvalidContainerSubnet
return &ErrInvalidContainerSubnet{}
}
// Check it is effectively a subset
brNetLen, _ := c.AddressIPv4.Mask.Size()
cnNetLen, _ := c.FixedCIDR.Mask.Size()
if brNetLen > cnNetLen {
return ErrInvalidContainerSubnet
return &ErrInvalidContainerSubnet{}
}
}
// If default gw is specified, it must be part of bridge subnet
if c.DefaultGatewayIPv4 != nil {
if !c.AddressIPv4.Contains(c.DefaultGatewayIPv4) {
return ErrInvalidGateway
return &ErrInvalidGateway{}
}
}
}
@@ -138,7 +152,7 @@ func (c *NetworkConfiguration) Validate() error {
// If default v6 gw is specified, FixedCIDRv6 must be specified and gw must belong to FixedCIDRv6 subnet
if c.EnableIPv6 && c.DefaultGatewayIPv6 != nil {
if c.FixedCIDRv6 == nil || !c.FixedCIDRv6.Contains(c.DefaultGatewayIPv6) {
return ErrInvalidGateway
return &ErrInvalidGateway{}
}
}
@@ -167,7 +181,7 @@ func (d *driver) Config(option map[string]interface{}) error {
defer d.Unlock()
if d.config != nil {
return ErrConfigExists
return &ErrConfigExists{}
}
genericData, ok := option[netlabel.GenericData]
@@ -182,7 +196,7 @@ func (d *driver) Config(option map[string]interface{}) error {
case *Configuration:
config = opt
default:
return ErrInvalidDriverConfig
return &ErrInvalidDriverConfig{}
}
d.config = config
@@ -220,7 +234,7 @@ func parseNetworkOptions(option options.Generic) (*NetworkConfiguration, error)
case *NetworkConfiguration:
config = opt
default:
return nil, ErrInvalidNetworkConfig
return nil, &ErrInvalidNetworkConfig{}
}
if err := config.Validate(); err != nil {
@@ -247,7 +261,7 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err
// Sanity checks
if d.network != nil {
d.Unlock()
return ErrNetworkExists
return &ErrNetworkExists{}
}
// Create and set network handler in driver
@@ -287,6 +301,11 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err
// Even if a bridge exists try to setup IPv4.
bridgeSetup.queueStep(setupBridgeIPv4)
enableIPv6Forwarding := false
if d.config != nil && d.config.EnableIPForwarding && config.FixedCIDRv6 != nil {
enableIPv6Forwarding = true
}
// Conditionally queue setup steps depending on configuration values.
for _, step := range []struct {
Condition bool
@@ -310,6 +329,9 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err
// specified subnet.
{config.FixedCIDRv6 != nil, setupFixedCIDRv6},
// Enable IPv6 Forwarding
{enableIPv6Forwarding, setupIPv6Forwarding},
// Setup Loopback Adresses Routing
{!config.EnableUserlandProxy, setupLoopbackAdressesRouting},
@@ -361,7 +383,7 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
// Sanity check
if n == nil {
err = driverapi.ErrNoNetwork
err = driverapi.ErrNoNetwork(nid)
return err
}
@@ -377,6 +399,20 @@ func (d *driver) DeleteNetwork(nid types.UUID) error {
return err
}
func addToBridge(ifaceName, bridgeName string) error {
iface, err := net.InterfaceByName(ifaceName)
if err != nil {
return fmt.Errorf("could not find interface %s: %v", ifaceName, err)
}
master, err := net.InterfaceByName(bridgeName)
if err != nil {
return fmt.Errorf("could not find bridge %s: %v", bridgeName, err)
}
return bri.AddToBridge(iface, master)
}
func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
var (
ipv6Addr *net.IPNet
@@ -397,7 +433,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
config := n.config
d.Unlock()
if n == nil {
return driverapi.ErrNoNetwork
return driverapi.ErrNoNetwork(nid)
}
// Sanity check
@@ -416,7 +452,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
// Endpoint with that id exists either on desired or other sandbox
if ep != nil {
return driverapi.ErrEndpointExists
return driverapi.ErrEndpointExists(eid)
}
// Try to convert the options to endpoint configuration
@@ -441,27 +477,27 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
}()
// Generate a name for what will be the host side pipe interface
name1, err := generateIfaceName()
hostIfName, err := generateIfaceName()
if err != nil {
return err
}
// Generate a name for what will be the sandbox side pipe interface
name2, err := generateIfaceName()
containerIfName, err := generateIfaceName()
if err != nil {
return err
}
// Generate and add the interface pipe host <-> sandbox
veth := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{Name: name1, TxQLen: 0},
PeerName: name2}
LinkAttrs: netlink.LinkAttrs{Name: hostIfName, TxQLen: 0},
PeerName: containerIfName}
if err = netlink.LinkAdd(veth); err != nil {
return err
}
// Get the host side pipe interface handler
host, err := netlink.LinkByName(name1)
host, err := netlink.LinkByName(hostIfName)
if err != nil {
return err
}
@@ -472,7 +508,7 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
}()
// Get the sandbox side pipe interface handler
sbox, err := netlink.LinkByName(name2)
sbox, err := netlink.LinkByName(containerIfName)
if err != nil {
return err
}
@@ -482,14 +518,6 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
}
}()
// Set the sbox's MAC. If specified, use the one configured by user, otherwise use a random one
mac := electMacAddress(epConfig)
err = netlink.LinkSetHardwareAddr(sbox, mac)
if err != nil {
return err
}
endpoint.macAddress = mac
// Add bridge inherited attributes to pipe interfaces
if config.Mtu != 0 {
err = netlink.LinkSetMTU(host, config.Mtu)
@@ -503,9 +531,15 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
}
// Attach host side pipe interface into the bridge
if err = netlink.LinkSetMaster(host,
&netlink.Bridge{LinkAttrs: netlink.LinkAttrs{Name: config.BridgeName}}); err != nil {
return err
if err = addToBridge(hostIfName, config.BridgeName); err != nil {
return fmt.Errorf("adding interface %s to bridge %s failed: %v", hostIfName, config.BridgeName, err)
}
if !config.EnableUserlandProxy {
err = netlink.LinkSetHairpin(host, true)
if err != nil {
return err
}
}
// v4 address for the sandbox side pipe interface
@@ -515,6 +549,24 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
}
ipv4Addr := &net.IPNet{IP: ip4, Mask: n.bridge.bridgeIPv4.Mask}
// Down the interface before configuring mac address.
if err := netlink.LinkSetDown(sbox); err != nil {
return fmt.Errorf("could not set link down for container interface %s: %v", containerIfName, err)
}
// Set the sbox's MAC. If specified, use the one configured by user, otherwise generate one based on IP.
mac := electMacAddress(epConfig, ip4)
err = netlink.LinkSetHardwareAddr(sbox, mac)
if err != nil {
return fmt.Errorf("could not set mac address for container interface %s: %v", containerIfName, err)
}
endpoint.macAddress = mac
// Up the host interface after finishing all netlink configuration
if err := netlink.LinkSetUp(host); err != nil {
return fmt.Errorf("could not set link up for host interface %s: %v", hostIfName, err)
}
// v6 address for the sandbox side pipe interface
ipv6Addr = &net.IPNet{}
if config.EnableIPv6 {
@@ -544,8 +596,8 @@ func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointIn
// Create the sandbox side pipe interface
intf := &sandbox.Interface{}
intf.SrcName = name2
intf.DstName = containerVeth
intf.SrcName = containerIfName
intf.DstName = containerVethPrefix
intf.Address = ipv4Addr
if config.EnableIPv6 {
@@ -578,7 +630,7 @@ func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
config := n.config
d.Unlock()
if n == nil {
return driverapi.ErrNoNetwork
return driverapi.ErrNoNetwork(nid)
}
// Sanity Check
@@ -648,7 +700,7 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{},
n := d.network
d.Unlock()
if n == nil {
return nil, driverapi.ErrNoNetwork
return nil, driverapi.ErrNoNetwork(nid)
}
// Sanity check
@@ -665,14 +717,23 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{},
return nil, err
}
if ep == nil {
return nil, driverapi.ErrNoEndpoint
return nil, driverapi.ErrNoEndpoint(eid)
}
m := make(map[string]interface{})
if ep.config.ExposedPorts != nil {
// Return a copy of the config data
epc := make([]types.TransportPort, 0, len(ep.config.ExposedPorts))
for _, tp := range ep.config.ExposedPorts {
epc = append(epc, tp.GetCopy())
}
m[netlabel.ExposedPorts] = epc
}
if ep.portMapping != nil {
// Return a copy of the operational data
pmc := make([]netutils.PortBinding, 0, len(ep.portMapping))
pmc := make([]types.PortBinding, 0, len(ep.portMapping))
for _, pm := range ep.portMapping {
pmc = append(pmc, pm.GetCopy())
}
@@ -856,23 +917,23 @@ func parseEndpointOptions(epOptions map[string]interface{}) (*EndpointConfigurat
if mac, ok := opt.(net.HardwareAddr); ok {
ec.MacAddress = mac
} else {
return nil, ErrInvalidEndpointConfig
return nil, &ErrInvalidEndpointConfig{}
}
}
if opt, ok := epOptions[netlabel.PortMap]; ok {
if bs, ok := opt.([]netutils.PortBinding); ok {
if bs, ok := opt.([]types.PortBinding); ok {
ec.PortBindings = bs
} else {
return nil, ErrInvalidEndpointConfig
return nil, &ErrInvalidEndpointConfig{}
}
}
if opt, ok := epOptions[netlabel.ExposedPorts]; ok {
if ports, ok := opt.([]netutils.TransportPort); ok {
if ports, ok := opt.([]types.TransportPort); ok {
ec.ExposedPorts = ports
} else {
return nil, ErrInvalidEndpointConfig
return nil, &ErrInvalidEndpointConfig{}
}
}
@@ -901,11 +962,36 @@ func parseContainerOptions(cOptions map[string]interface{}) (*ContainerConfigura
}
}
func electMacAddress(epConfig *EndpointConfiguration) net.HardwareAddr {
// Generate a IEEE802 compliant MAC address from the given IP address.
//
// The generator is guaranteed to be consistent: the same IP will always yield the same
// MAC address. This is to avoid ARP cache issues.
func generateMacAddr(ip net.IP) net.HardwareAddr {
hw := make(net.HardwareAddr, 6)
// The first byte of the MAC address has to comply with these rules:
// 1. Unicast: Set the least-significant bit to 0.
// 2. Address is locally administered: Set the second-least-significant bit (U/L) to 1.
// 3. As "small" as possible: The veth address has to be "smaller" than the bridge address.
hw[0] = 0x02
// The first 24 bits of the MAC represent the Organizationally Unique Identifier (OUI).
// Since this address is locally administered, we can do whatever we want as long as
// it doesn't conflict with other addresses.
hw[1] = 0x42
// Insert the IP address into the last 32 bits of the MAC address.
// This is a simple way to guarantee the address will be consistent and unique.
copy(hw[2:], ip.To4())
return hw
}
func electMacAddress(epConfig *EndpointConfiguration, ip net.IP) net.HardwareAddr {
if epConfig != nil && epConfig.MacAddress != nil {
return epConfig.MacAddress
}
return netutils.GenerateRandomMAC()
return generateMacAddr(ip)
}
// Generates a name to be used for a virtual ethernet
@@ -924,5 +1010,5 @@ func generateIfaceName() (string, error) {
return "", err
}
}
return "", ErrIfaceName
return "", &ErrIfaceName{}
}
+12 -11
View File
@@ -11,6 +11,7 @@ import (
"github.com/docker/libnetwork/iptables"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/types"
"github.com/vishvananda/netlink"
)
@@ -202,7 +203,7 @@ func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) {
if !ok {
t.Fatalf("Endpoint operational data does not contain port mapping data")
}
pm, ok := pmd.([]netutils.PortBinding)
pm, ok := pmd.([]types.PortBinding)
if !ok {
t.Fatalf("Unexpected format for port mapping in endpoint operational data")
}
@@ -261,19 +262,19 @@ func TestCreateLinkWithOptions(t *testing.T) {
}
}
func getExposedPorts() []netutils.TransportPort {
return []netutils.TransportPort{
netutils.TransportPort{Proto: netutils.TCP, Port: uint16(5000)},
netutils.TransportPort{Proto: netutils.UDP, Port: uint16(400)},
netutils.TransportPort{Proto: netutils.TCP, Port: uint16(600)},
func getExposedPorts() []types.TransportPort {
return []types.TransportPort{
types.TransportPort{Proto: types.TCP, Port: uint16(5000)},
types.TransportPort{Proto: types.UDP, Port: uint16(400)},
types.TransportPort{Proto: types.TCP, Port: uint16(600)},
}
}
func getPortMapping() []netutils.PortBinding {
return []netutils.PortBinding{
netutils.PortBinding{Proto: netutils.TCP, Port: uint16(230), HostPort: uint16(23000)},
netutils.PortBinding{Proto: netutils.UDP, Port: uint16(200), HostPort: uint16(22000)},
netutils.PortBinding{Proto: netutils.TCP, Port: uint16(120), HostPort: uint16(12000)},
func getPortMapping() []types.PortBinding {
return []types.PortBinding{
types.PortBinding{Proto: types.TCP, Port: uint16(230), HostPort: uint16(23000)},
types.PortBinding{Proto: types.UDP, Port: uint16(200), HostPort: uint16(22000)},
types.PortBinding{Proto: types.TCP, Port: uint16(120), HostPort: uint16(12000)},
}
}
-201
View File
@@ -1,201 +0,0 @@
package bridge
import (
"errors"
"fmt"
"net"
)
var (
// ErrConfigExists error is returned when driver already has a config applied.
ErrConfigExists = errors.New("configuration already exists, bridge configuration can be applied only once")
// ErrInvalidDriverConfig error is returned when Bridge Driver is passed an invalid config
ErrInvalidDriverConfig = errors.New("Invalid configuration passed to Bridge Driver")
// ErrInvalidNetworkConfig error is returned when a network is created on a driver without valid config.
ErrInvalidNetworkConfig = errors.New("trying to create a network on a driver without valid config")
// ErrInvalidContainerConfig error is returned when a endpoint create is attempted with an invalid configuration.
ErrInvalidContainerConfig = errors.New("Error in joining a container due to invalid configuration")
// ErrInvalidEndpointConfig error is returned when a endpoint create is attempted with an invalid endpoint configuration.
ErrInvalidEndpointConfig = errors.New("trying to create an endpoint with an invalid endpoint configuration")
// ErrNetworkExists error is returned when a network already exists and another network is created.
ErrNetworkExists = errors.New("network already exists, bridge can only have one network")
// ErrIfaceName error is returned when a new name could not be generated.
ErrIfaceName = errors.New("failed to find name for new interface")
// ErrNoIPAddr error is returned when bridge has no IPv4 address configured.
ErrNoIPAddr = errors.New("bridge has no IPv4 address configured")
// ErrInvalidGateway is returned when the user provided default gateway (v4/v6) is not not valid.
ErrInvalidGateway = errors.New("default gateway ip must be part of the network")
// ErrInvalidContainerSubnet is returned when the container subnet (FixedCIDR) is not valid.
ErrInvalidContainerSubnet = errors.New("container subnet must be a subset of bridge network")
// ErrInvalidMtu is returned when the user provided MTU is not valid.
ErrInvalidMtu = errors.New("invalid MTU number")
// ErrIPFwdCfg is returned when ip forwarding setup is invoked when the configuration
// not enabled.
ErrIPFwdCfg = errors.New("unexpected request to enable IP Forwarding")
)
// ErrInvalidPort is returned when the container or host port specified in the port binding is not valid.
type ErrInvalidPort string
func (ip ErrInvalidPort) Error() string {
return fmt.Sprintf("invalid transport port: %s", string(ip))
}
// ErrUnsupportedAddressType is returned when the specified address type is not supported.
type ErrUnsupportedAddressType string
func (uat ErrUnsupportedAddressType) Error() string {
return fmt.Sprintf("unsupported address type: %s", string(uat))
}
// ErrInvalidAddressBinding is returned when the host address specified in the port binding is not valid.
type ErrInvalidAddressBinding string
func (iab ErrInvalidAddressBinding) Error() string {
return fmt.Sprintf("invalid host address in port binding: %s", string(iab))
}
// ActiveEndpointsError is returned when there are
// still active endpoints in the network being deleted.
type ActiveEndpointsError string
func (aee ActiveEndpointsError) Error() string {
return fmt.Sprintf("network %s has active endpoint", string(aee))
}
// InvalidNetworkIDError is returned when the passed
// network id for an existing network is not a known id.
type InvalidNetworkIDError string
func (inie InvalidNetworkIDError) Error() string {
return fmt.Sprintf("invalid network id %s", string(inie))
}
// InvalidEndpointIDError is returned when the passed
// endpoint id is not valid.
type InvalidEndpointIDError string
func (ieie InvalidEndpointIDError) Error() string {
return fmt.Sprintf("invalid endpoint id: %s", string(ieie))
}
// InvalidSandboxIDError is returned when the passed
// sandbox id valid.
type InvalidSandboxIDError string
func (isie InvalidSandboxIDError) Error() string {
return fmt.Sprintf("invalid sanbox id: %s", string(isie))
}
// EndpointNotFoundError is returned when the no endpoint
// with the passed endpoint id is found.
type EndpointNotFoundError string
func (enfe EndpointNotFoundError) Error() string {
return fmt.Sprintf("endpoint not found: %s", string(enfe))
}
// NonDefaultBridgeExistError is returned when a non-default
// bridge config is passed but it does not already exist.
type NonDefaultBridgeExistError string
func (ndbee NonDefaultBridgeExistError) Error() string {
return fmt.Sprintf("bridge device with non default name %s must be created manually", string(ndbee))
}
// FixedCIDRv4Error is returned when fixed-cidrv4 configuration
// failed.
type FixedCIDRv4Error struct {
net *net.IPNet
subnet *net.IPNet
err error
}
func (fcv4 *FixedCIDRv4Error) Error() string {
return fmt.Sprintf("setup FixedCIDRv4 failed for subnet %s in %s: %v", fcv4.subnet, fcv4.net, fcv4.err)
}
// FixedCIDRv6Error is returned when fixed-cidrv6 configuration
// failed.
type FixedCIDRv6Error struct {
net *net.IPNet
err error
}
func (fcv6 *FixedCIDRv6Error) Error() string {
return fmt.Sprintf("setup FixedCIDRv6 failed for subnet %s in %s: %v", fcv6.net, fcv6.net, fcv6.err)
}
type ipTableCfgError string
func (name ipTableCfgError) Error() string {
return fmt.Sprintf("unexpected request to set IP tables for interface: %s", string(name))
}
type invalidIPTablesCfgError string
func (action invalidIPTablesCfgError) Error() string {
return fmt.Sprintf("Invalid IPTables action '%s'", string(action))
}
// IPv4AddrRangeError is returned when a valid IP address range couldn't be found.
type IPv4AddrRangeError string
func (name IPv4AddrRangeError) Error() string {
return fmt.Sprintf("can't find an address range for interface %q", string(name))
}
// IPv4AddrAddError is returned when IPv4 address could not be added to the bridge.
type IPv4AddrAddError struct {
ip *net.IPNet
err error
}
func (ipv4 *IPv4AddrAddError) Error() string {
return fmt.Sprintf("failed to add IPv4 address %s to bridge: %v", ipv4.ip, ipv4.err)
}
// IPv6AddrAddError is returned when IPv6 address could not be added to the bridge.
type IPv6AddrAddError struct {
ip *net.IPNet
err error
}
func (ipv6 *IPv6AddrAddError) Error() string {
return fmt.Sprintf("failed to add IPv6 address %s to bridge: %v", ipv6.ip, ipv6.err)
}
// IPv4AddrNoMatchError is returned when the bridge's IPv4 address does not match configured.
type IPv4AddrNoMatchError struct {
ip net.IP
cfgIP net.IP
}
func (ipv4 *IPv4AddrNoMatchError) Error() string {
return fmt.Sprintf("bridge IPv4 (%s) does not match requested configuration %s", ipv4.ip, ipv4.cfgIP)
}
// IPv6AddrNoMatchError is returned when the bridge's IPv6 address does not match configured.
type IPv6AddrNoMatchError net.IPNet
func (ipv6 *IPv6AddrNoMatchError) Error() string {
return fmt.Sprintf("bridge IPv6 addresses do not match the expected bridge configuration %s", (*net.IPNet)(ipv6).String())
}
// InvalidLinkIPAddrError is returned when a link is configured to a container with an invalid ip address
type InvalidLinkIPAddrError string
func (address InvalidLinkIPAddrError) Error() string {
return fmt.Sprintf("Cannot link to a container with Invalid IP Address '%s'", string(address))
}
+341
View File
@@ -0,0 +1,341 @@
package bridge
import (
"fmt"
"net"
)
// ErrConfigExists error is returned when driver already has a config applied.
type ErrConfigExists struct{}
func (ece *ErrConfigExists) Error() string {
return "configuration already exists, bridge configuration can be applied only once"
}
// Forbidden denotes the type of this error
func (ece *ErrConfigExists) Forbidden() {}
// ErrInvalidDriverConfig error is returned when Bridge Driver is passed an invalid config
type ErrInvalidDriverConfig struct{}
func (eidc *ErrInvalidDriverConfig) Error() string {
return "Invalid configuration passed to Bridge Driver"
}
// BadRequest denotes the type of this error
func (eidc *ErrInvalidDriverConfig) BadRequest() {}
// ErrInvalidNetworkConfig error is returned when a network is created on a driver without valid config.
type ErrInvalidNetworkConfig struct{}
func (einc *ErrInvalidNetworkConfig) Error() string {
return "trying to create a network on a driver without valid config"
}
// Forbidden denotes the type of this error
func (einc *ErrInvalidNetworkConfig) Forbidden() {}
// ErrInvalidContainerConfig error is returned when a endpoint create is attempted with an invalid configuration.
type ErrInvalidContainerConfig struct{}
func (eicc *ErrInvalidContainerConfig) Error() string {
return "Error in joining a container due to invalid configuration"
}
// BadRequest denotes the type of this error
func (eicc *ErrInvalidContainerConfig) BadRequest() {}
// ErrInvalidEndpointConfig error is returned when a endpoint create is attempted with an invalid endpoint configuration.
type ErrInvalidEndpointConfig struct{}
func (eiec *ErrInvalidEndpointConfig) Error() string {
return "trying to create an endpoint with an invalid endpoint configuration"
}
// BadRequest denotes the type of this error
func (eiec *ErrInvalidEndpointConfig) BadRequest() {}
// ErrNetworkExists error is returned when a network already exists and another network is created.
type ErrNetworkExists struct{}
func (ene *ErrNetworkExists) Error() string {
return "network already exists, bridge can only have one network"
}
// Forbidden denotes the type of this error
func (ene *ErrNetworkExists) Forbidden() {}
// ErrIfaceName error is returned when a new name could not be generated.
type ErrIfaceName struct{}
func (ein *ErrIfaceName) Error() string {
return "failed to find name for new interface"
}
// InternalError denotes the type of this error
func (ein *ErrIfaceName) InternalError() {}
// ErrNoIPAddr error is returned when bridge has no IPv4 address configured.
type ErrNoIPAddr struct{}
func (enip *ErrNoIPAddr) Error() string {
return "bridge has no IPv4 address configured"
}
// InternalError denotes the type of this error
func (enip *ErrNoIPAddr) InternalError() {}
// ErrInvalidGateway is returned when the user provided default gateway (v4/v6) is not not valid.
type ErrInvalidGateway struct{}
func (eig *ErrInvalidGateway) Error() string {
return "default gateway ip must be part of the network"
}
// BadRequest denotes the type of this error
func (eig *ErrInvalidGateway) BadRequest() {}
// ErrInvalidContainerSubnet is returned when the container subnet (FixedCIDR) is not valid.
type ErrInvalidContainerSubnet struct{}
func (eis *ErrInvalidContainerSubnet) Error() string {
return "container subnet must be a subset of bridge network"
}
// BadRequest denotes the type of this error
func (eis *ErrInvalidContainerSubnet) BadRequest() {}
// ErrInvalidMtu is returned when the user provided MTU is not valid.
type ErrInvalidMtu int
func (eim ErrInvalidMtu) Error() string {
return fmt.Sprintf("invalid MTU number: %d", int(eim))
}
// BadRequest denotes the type of this error
func (eim ErrInvalidMtu) BadRequest() {}
// ErrIPFwdCfg is returned when ip forwarding setup is invoked when the configuration
// not enabled.
type ErrIPFwdCfg struct{}
func (eipf *ErrIPFwdCfg) Error() string {
return "unexpected request to enable IP Forwarding"
}
// BadRequest denotes the type of this error
func (eipf *ErrIPFwdCfg) BadRequest() {}
// ErrInvalidPort is returned when the container or host port specified in the port binding is not valid.
type ErrInvalidPort string
func (ip ErrInvalidPort) Error() string {
return fmt.Sprintf("invalid transport port: %s", string(ip))
}
// BadRequest denotes the type of this error
func (ip ErrInvalidPort) BadRequest() {}
// ErrUnsupportedAddressType is returned when the specified address type is not supported.
type ErrUnsupportedAddressType string
func (uat ErrUnsupportedAddressType) Error() string {
return fmt.Sprintf("unsupported address type: %s", string(uat))
}
// BadRequest denotes the type of this error
func (uat ErrUnsupportedAddressType) BadRequest() {}
// ErrInvalidAddressBinding is returned when the host address specified in the port binding is not valid.
type ErrInvalidAddressBinding string
func (iab ErrInvalidAddressBinding) Error() string {
return fmt.Sprintf("invalid host address in port binding: %s", string(iab))
}
// BadRequest denotes the type of this error
func (iab ErrInvalidAddressBinding) BadRequest() {}
// ActiveEndpointsError is returned when there are
// still active endpoints in the network being deleted.
type ActiveEndpointsError string
func (aee ActiveEndpointsError) Error() string {
return fmt.Sprintf("network %s has active endpoint", string(aee))
}
// Forbidden denotes the type of this error
func (aee ActiveEndpointsError) Forbidden() {}
// InvalidNetworkIDError is returned when the passed
// network id for an existing network is not a known id.
type InvalidNetworkIDError string
func (inie InvalidNetworkIDError) Error() string {
return fmt.Sprintf("invalid network id %s", string(inie))
}
// NotFound denotes the type of this error
func (inie InvalidNetworkIDError) NotFound() {}
// InvalidEndpointIDError is returned when the passed
// endpoint id is not valid.
type InvalidEndpointIDError string
func (ieie InvalidEndpointIDError) Error() string {
return fmt.Sprintf("invalid endpoint id: %s", string(ieie))
}
// BadRequest denotes the type of this error
func (ieie InvalidEndpointIDError) BadRequest() {}
// InvalidSandboxIDError is returned when the passed
// sandbox id is not valid.
type InvalidSandboxIDError string
func (isie InvalidSandboxIDError) Error() string {
return fmt.Sprintf("invalid sanbox id: %s", string(isie))
}
// BadRequest denotes the type of this error
func (isie InvalidSandboxIDError) BadRequest() {}
// EndpointNotFoundError is returned when the no endpoint
// with the passed endpoint id is found.
type EndpointNotFoundError string
func (enfe EndpointNotFoundError) Error() string {
return fmt.Sprintf("endpoint not found: %s", string(enfe))
}
// NotFound denotes the type of this error
func (enfe EndpointNotFoundError) NotFound() {}
// NonDefaultBridgeExistError is returned when a non-default
// bridge config is passed but it does not already exist.
type NonDefaultBridgeExistError string
func (ndbee NonDefaultBridgeExistError) Error() string {
return fmt.Sprintf("bridge device with non default name %s must be created manually", string(ndbee))
}
// Forbidden denotes the type of this error
func (ndbee NonDefaultBridgeExistError) Forbidden() {}
// FixedCIDRv4Error is returned when fixed-cidrv4 configuration
// failed.
type FixedCIDRv4Error struct {
Net *net.IPNet
Subnet *net.IPNet
Err error
}
func (fcv4 *FixedCIDRv4Error) Error() string {
return fmt.Sprintf("setup FixedCIDRv4 failed for subnet %s in %s: %v", fcv4.Subnet, fcv4.Net, fcv4.Err)
}
// InternalError denotes the type of this error
func (fcv4 *FixedCIDRv4Error) InternalError() {}
// FixedCIDRv6Error is returned when fixed-cidrv6 configuration
// failed.
type FixedCIDRv6Error struct {
Net *net.IPNet
Err error
}
func (fcv6 *FixedCIDRv6Error) Error() string {
return fmt.Sprintf("setup FixedCIDRv6 failed for subnet %s in %s: %v", fcv6.Net, fcv6.Net, fcv6.Err)
}
// InternalError denotes the type of this error
func (fcv6 *FixedCIDRv6Error) InternalError() {}
// IPTableCfgError is returned when an unexpected ip tables configuration is entered
type IPTableCfgError string
func (name IPTableCfgError) Error() string {
return fmt.Sprintf("unexpected request to set IP tables for interface: %s", string(name))
}
// BadRequest denotes the type of this error
func (name IPTableCfgError) BadRequest() {}
// InvalidIPTablesCfgError is returned when an invalid ip tables configuration is entered
type InvalidIPTablesCfgError string
func (action InvalidIPTablesCfgError) Error() string {
return fmt.Sprintf("Invalid IPTables action '%s'", string(action))
}
// BadRequest denotes the type of this error
func (action InvalidIPTablesCfgError) BadRequest() {}
// IPv4AddrRangeError is returned when a valid IP address range couldn't be found.
type IPv4AddrRangeError string
func (name IPv4AddrRangeError) Error() string {
return fmt.Sprintf("can't find an address range for interface %q", string(name))
}
// BadRequest denotes the type of this error
func (name IPv4AddrRangeError) BadRequest() {}
// IPv4AddrAddError is returned when IPv4 address could not be added to the bridge.
type IPv4AddrAddError struct {
IP *net.IPNet
Err error
}
func (ipv4 *IPv4AddrAddError) Error() string {
return fmt.Sprintf("failed to add IPv4 address %s to bridge: %v", ipv4.IP, ipv4.Err)
}
// InternalError denotes the type of this error
func (ipv4 *IPv4AddrAddError) InternalError() {}
// IPv6AddrAddError is returned when IPv6 address could not be added to the bridge.
type IPv6AddrAddError struct {
IP *net.IPNet
Err error
}
func (ipv6 *IPv6AddrAddError) Error() string {
return fmt.Sprintf("failed to add IPv6 address %s to bridge: %v", ipv6.IP, ipv6.Err)
}
// InternalError denotes the type of this error
func (ipv6 *IPv6AddrAddError) InternalError() {}
// IPv4AddrNoMatchError is returned when the bridge's IPv4 address does not match configured.
type IPv4AddrNoMatchError struct {
IP net.IP
CfgIP net.IP
}
func (ipv4 *IPv4AddrNoMatchError) Error() string {
return fmt.Sprintf("bridge IPv4 (%s) does not match requested configuration %s", ipv4.IP, ipv4.CfgIP)
}
// BadRequest denotes the type of this error
func (ipv4 *IPv4AddrNoMatchError) BadRequest() {}
// IPv6AddrNoMatchError is returned when the bridge's IPv6 address does not match configured.
type IPv6AddrNoMatchError net.IPNet
func (ipv6 *IPv6AddrNoMatchError) Error() string {
return fmt.Sprintf("bridge IPv6 addresses do not match the expected bridge configuration %s", (*net.IPNet)(ipv6).String())
}
// BadRequest denotes the type of this error
func (ipv6 *IPv6AddrNoMatchError) BadRequest() {}
// InvalidLinkIPAddrError is returned when a link is configured to a container with an invalid ip address
type InvalidLinkIPAddrError string
func (address InvalidLinkIPAddrError) Error() string {
return fmt.Sprintf("Cannot link to a container with Invalid IP Address '%s'", string(address))
}
// BadRequest denotes the type of this error
func (address InvalidLinkIPAddrError) BadRequest() {}
+5 -5
View File
@@ -6,13 +6,13 @@ import (
log "github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/iptables"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/types"
)
type link struct {
parentIP string
childIP string
ports []netutils.TransportPort
ports []types.TransportPort
bridge string
}
@@ -20,7 +20,7 @@ func (l *link) String() string {
return fmt.Sprintf("%s <-> %s [%v] on %s", l.parentIP, l.childIP, l.ports, l.bridge)
}
func newLink(parentIP, childIP string, ports []netutils.TransportPort, bridge string) *link {
func newLink(parentIP, childIP string, ports []types.TransportPort, bridge string) *link {
return &link{
childIP: childIP,
parentIP: parentIP,
@@ -45,7 +45,7 @@ func (l *link) Disable() {
// that returns typed errors
}
func linkContainers(action, parentIP, childIP string, ports []netutils.TransportPort, bridge string,
func linkContainers(action, parentIP, childIP string, ports []types.TransportPort, bridge string,
ignoreErrors bool) error {
var nfAction iptables.Action
@@ -57,7 +57,7 @@ func linkContainers(action, parentIP, childIP string, ports []netutils.Transport
case "-D":
nfAction = iptables.Delete
default:
return invalidIPTablesCfgError(action)
return InvalidIPTablesCfgError(action)
}
ip1 := net.ParseIP(parentIP)
+6 -6
View File
@@ -3,14 +3,14 @@ package bridge
import (
"testing"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/types"
)
func getPorts() []netutils.TransportPort {
return []netutils.TransportPort{
netutils.TransportPort{Proto: netutils.TCP, Port: uint16(5000)},
netutils.TransportPort{Proto: netutils.UDP, Port: uint16(400)},
netutils.TransportPort{Proto: netutils.TCP, Port: uint16(600)},
func getPorts() []types.TransportPort {
return []types.TransportPort{
types.TransportPort{Proto: types.TCP, Port: uint16(5000)},
types.TransportPort{Proto: types.UDP, Port: uint16(400)},
types.TransportPort{Proto: types.TCP, Port: uint16(600)},
}
}
+2 -2
View File
@@ -125,8 +125,8 @@ func TestLinkCreateTwo(t *testing.T) {
te2 := &testEndpoint{ifaces: []*testInterface{}}
err = d.CreateEndpoint("dummy", "ep", te2, nil)
if err != nil {
if err != driverapi.ErrEndpointExists {
t.Fatalf("Failed with a wrong error :%s", err.Error())
if _, ok := err.(driverapi.ErrEndpointExists); !ok {
t.Fatalf("Failed with a wrong error: %s", err.Error())
}
} else {
t.Fatalf("Expected to fail while trying to add same endpoint twice")
+7 -7
View File
@@ -7,15 +7,15 @@ import (
"net"
"github.com/Sirupsen/logrus"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/sandbox"
"github.com/docker/libnetwork/types"
)
var (
defaultBindingIP = net.IPv4(0, 0, 0, 0)
)
func allocatePorts(epConfig *EndpointConfiguration, intf *sandbox.Interface, reqDefBindIP net.IP, ulPxyEnabled bool) ([]netutils.PortBinding, error) {
func allocatePorts(epConfig *EndpointConfiguration, intf *sandbox.Interface, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
if epConfig == nil || epConfig.PortBindings == nil {
return nil, nil
}
@@ -28,8 +28,8 @@ func allocatePorts(epConfig *EndpointConfiguration, intf *sandbox.Interface, req
return allocatePortsInternal(epConfig.PortBindings, intf.Address.IP, defHostIP, ulPxyEnabled)
}
func allocatePortsInternal(bindings []netutils.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]netutils.PortBinding, error) {
bs := make([]netutils.PortBinding, 0, len(bindings))
func allocatePortsInternal(bindings []types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) {
bs := make([]types.PortBinding, 0, len(bindings))
for _, c := range bindings {
b := c.GetCopy()
if err := allocatePort(&b, containerIP, defHostIP, ulPxyEnabled); err != nil {
@@ -44,7 +44,7 @@ func allocatePortsInternal(bindings []netutils.PortBinding, containerIP, defHost
return bs, nil
}
func allocatePort(bnd *netutils.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) error {
func allocatePort(bnd *types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) error {
var (
host net.Addr
err error
@@ -98,7 +98,7 @@ func releasePorts(ep *bridgeEndpoint) error {
return releasePortsInternal(ep.portMapping)
}
func releasePortsInternal(bindings []netutils.PortBinding) error {
func releasePortsInternal(bindings []types.PortBinding) error {
var errorBuf bytes.Buffer
// Attempt to release all port bindings, do not stop on failure
@@ -114,7 +114,7 @@ func releasePortsInternal(bindings []netutils.PortBinding) error {
return nil
}
func releasePort(bnd netutils.PortBinding) error {
func releasePort(bnd types.PortBinding) error {
// Construct the host side transport address
host, err := bnd.HostAddr()
if err != nil {
+4 -3
View File
@@ -7,6 +7,7 @@ import (
"github.com/docker/docker/pkg/reexec"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/types"
)
func TestMain(m *testing.M) {
@@ -20,9 +21,9 @@ func TestPortMappingConfig(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
d := newDriver()
binding1 := netutils.PortBinding{Proto: netutils.UDP, Port: uint16(400), HostPort: uint16(54000)}
binding2 := netutils.PortBinding{Proto: netutils.TCP, Port: uint16(500), HostPort: uint16(65000)}
portBindings := []netutils.PortBinding{binding1, binding2}
binding1 := types.PortBinding{Proto: types.UDP, Port: uint16(400), HostPort: uint16(54000)}
binding2 := types.PortBinding{Proto: types.TCP, Port: uint16(500), HostPort: uint16(65000)}
portBindings := []types.PortBinding{binding1, binding2}
epOptions := make(map[string]interface{})
epOptions[netlabel.PortMap] = portBindings
+5 -6
View File
@@ -1,14 +1,15 @@
package bridge
import (
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/parsers/kernel"
"github.com/docker/libnetwork/netutils"
bri "github.com/docker/libcontainer/netlink"
"github.com/vishvananda/netlink"
)
// SetupDevice create a new bridge interface/
func setupDevice(config *NetworkConfiguration, i *bridgeInterface) error {
var setMac bool
// We only attempt to create the bridge when the requested device name is
// the default one.
if config.BridgeName != DefaultBridgeName && !config.AllowNonDefaultBridge {
@@ -26,12 +27,10 @@ func setupDevice(config *NetworkConfiguration, i *bridgeInterface) error {
// was not supported before that.
kv, err := kernel.GetKernelVersion()
if err == nil && (kv.Kernel >= 3 && kv.Major >= 3) {
i.Link.Attrs().HardwareAddr = netutils.GenerateRandomMAC()
log.Debugf("Setting bridge mac address to %s", i.Link.Attrs().HardwareAddr)
setMac = true
}
// Call out to netlink to create the device.
return netlink.LinkAdd(i.Link)
return bri.CreateBridge(config.BridgeName, setMac)
}
// SetupDeviceUp ups the given bridge interface.
+4 -2
View File
@@ -1,6 +1,8 @@
package bridge
import log "github.com/Sirupsen/logrus"
import (
log "github.com/Sirupsen/logrus"
)
func setupFixedCIDRv4(config *NetworkConfiguration, i *bridgeInterface) error {
addrv4, _, err := i.addresses()
@@ -10,7 +12,7 @@ func setupFixedCIDRv4(config *NetworkConfiguration, i *bridgeInterface) error {
log.Debugf("Using IPv4 subnet: %v", config.FixedCIDR)
if err := ipAllocator.RegisterSubnet(addrv4.IPNet, config.FixedCIDR); err != nil {
return &FixedCIDRv4Error{subnet: config.FixedCIDR, net: addrv4.IPNet, err: err}
return &FixedCIDRv4Error{Subnet: config.FixedCIDR, Net: addrv4.IPNet, Err: err}
}
return nil
+17 -2
View File
@@ -1,12 +1,27 @@
package bridge
import log "github.com/Sirupsen/logrus"
import (
"os"
log "github.com/Sirupsen/logrus"
"github.com/vishvananda/netlink"
)
func setupFixedCIDRv6(config *NetworkConfiguration, i *bridgeInterface) error {
log.Debugf("Using IPv6 subnet: %v", config.FixedCIDRv6)
if err := ipAllocator.RegisterSubnet(config.FixedCIDRv6, config.FixedCIDRv6); err != nil {
return &FixedCIDRv6Error{net: config.FixedCIDRv6, err: err}
return &FixedCIDRv6Error{Net: config.FixedCIDRv6, Err: err}
}
// Setting route to global IPv6 subnet
log.Debugf("Adding route to IPv6 network %s via device %s", config.FixedCIDRv6.String(), config.BridgeName)
err := netlink.RouteAdd(&netlink.Route{
Scope: netlink.SCOPE_UNIVERSE,
LinkIndex: i.Link.Attrs().Index,
Dst: config.FixedCIDRv6,
})
if err != nil && !os.IsExist(err) {
log.Errorf("Could not add route to IPv6 network %s via device %s", config.FixedCIDRv6.String(), config.BridgeName)
}
return nil
}
+1 -1
View File
@@ -13,7 +13,7 @@ const (
func setupIPForwarding(config *Configuration) error {
// Sanity Check
if config.EnableIPForwarding == false {
return ErrIPFwdCfg
return &ErrIPFwdCfg{}
}
// Enable IPv4 forwarding
+1 -1
View File
@@ -47,7 +47,7 @@ func TestUnexpectedSetupIPForwarding(t *testing.T) {
t.Fatal("Setup IP forwarding was expected to fail")
}
if err != ErrIPFwdCfg {
if _, ok := err.(*ErrIPFwdCfg); !ok {
t.Fatalf("Setup IP forwarding failed with unexpected error: %v", err)
}
}
+1 -1
View File
@@ -16,7 +16,7 @@ const (
func setupIPTables(config *NetworkConfiguration, i *bridgeInterface) error {
// Sanity check.
if config.EnableIPTables == false {
return ipTableCfgError(config.BridgeName)
return IPTableCfgError(config.BridgeName)
}
hairpinMode := !config.EnableUserlandProxy
+2 -2
View File
@@ -71,7 +71,7 @@ func setupBridgeIPv4(config *NetworkConfiguration, i *bridgeInterface) error {
log.Debugf("Creating bridge interface %q with network %s", config.BridgeName, bridgeIPv4)
if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv4}); err != nil {
return &IPv4AddrAddError{ip: bridgeIPv4, err: err}
return &IPv4AddrAddError{IP: bridgeIPv4, Err: err}
}
// Store bridge network and default gateway
@@ -114,7 +114,7 @@ func electBridgeIPv4(config *NetworkConfiguration) (*net.IPNet, error) {
func setupGatewayIPv4(config *NetworkConfiguration, i *bridgeInterface) error {
if !i.bridgeIPv4.Contains(config.DefaultGatewayIPv4) {
return ErrInvalidGateway
return &ErrInvalidGateway{}
}
if _, err := ipAllocator.RequestIP(i.bridgeIPv4, config.DefaultGatewayIPv4); err != nil {
return err
+20 -5
View File
@@ -5,12 +5,16 @@ import (
"io/ioutil"
"net"
"github.com/Sirupsen/logrus"
"github.com/vishvananda/netlink"
)
var bridgeIPv6 *net.IPNet
const bridgeIPv6Str = "fe80::1/64"
const (
bridgeIPv6Str = "fe80::1/64"
ipv6ForwardConfPerm = 0644
)
func init() {
// We allow ourselves to panic in this special case because we indicate a
@@ -25,7 +29,7 @@ func init() {
func setupBridgeIPv6(config *NetworkConfiguration, i *bridgeInterface) error {
// Enable IPv6 on the bridge
procFile := "/proc/sys/net/ipv6/conf/" + config.BridgeName + "/disable_ipv6"
if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, 0644); err != nil {
if err := ioutil.WriteFile(procFile, []byte{'0', '\n'}, ipv6ForwardConfPerm); err != nil {
return fmt.Errorf("Unable to enable IPv6 addresses on bridge: %v", err)
}
@@ -37,7 +41,7 @@ func setupBridgeIPv6(config *NetworkConfiguration, i *bridgeInterface) error {
// Add the default link local ipv6 address if it doesn't exist
if !findIPv6Address(netlink.Addr{IPNet: bridgeIPv6}, addrsv6) {
if err := netlink.AddrAdd(i.Link, &netlink.Addr{IPNet: bridgeIPv6}); err != nil {
return &IPv6AddrAddError{ip: bridgeIPv6, err: err}
return &IPv6AddrAddError{IP: bridgeIPv6, Err: err}
}
}
@@ -50,10 +54,10 @@ func setupBridgeIPv6(config *NetworkConfiguration, i *bridgeInterface) error {
func setupGatewayIPv6(config *NetworkConfiguration, i *bridgeInterface) error {
if config.FixedCIDRv6 == nil {
return ErrInvalidContainerSubnet
return &ErrInvalidContainerSubnet{}
}
if !config.FixedCIDRv6.Contains(config.DefaultGatewayIPv6) {
return ErrInvalidGateway
return &ErrInvalidGateway{}
}
if _, err := ipAllocator.RequestIP(config.FixedCIDRv6, config.DefaultGatewayIPv6); err != nil {
return err
@@ -64,3 +68,14 @@ func setupGatewayIPv6(config *NetworkConfiguration, i *bridgeInterface) error {
return nil
}
func setupIPv6Forwarding(config *NetworkConfiguration, i *bridgeInterface) error {
// Enable IPv6 forwarding
if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/default/forwarding", []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil {
logrus.Warnf("Unable to enable IPv6 default forwarding: %v", err)
}
if err := ioutil.WriteFile("/proc/sys/net/ipv6/conf/all/forwarding", []byte{'1', '\n'}, ipv6ForwardConfPerm); err != nil {
logrus.Warnf("Unable to enable IPv6 all forwarding: %v", err)
}
return nil
}
+5 -3
View File
@@ -1,6 +1,8 @@
package bridge
import "github.com/vishvananda/netlink"
import (
"github.com/vishvananda/netlink"
)
func setupVerifyAndReconcile(config *NetworkConfiguration, i *bridgeInterface) error {
// Fetch a single IPv4 and a slice of IPv6 addresses from the bridge.
@@ -11,12 +13,12 @@ func setupVerifyAndReconcile(config *NetworkConfiguration, i *bridgeInterface) e
// Verify that the bridge does have an IPv4 address.
if addrv4.IPNet == nil {
return ErrNoIPAddr
return &ErrNoIPAddr{}
}
// Verify that the bridge IPv4 address matches the requested configuration.
if config.AddressIPv4 != nil && !addrv4.IP.Equal(config.AddressIPv4.IP) {
return &IPv4AddrNoMatchError{ip: addrv4.IP, cfgIP: config.AddressIPv4.IP}
return &IPv4AddrNoMatchError{IP: addrv4.IP, CfgIP: config.AddressIPv4.IP}
}
// Verify that one of the bridge IPv6 addresses matches the requested
+5 -1
View File
@@ -40,7 +40,11 @@ func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{},
// Join method is invoked when a Sandbox is attached to an endpoint.
func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
return (jinfo.SetHostsPath("/etc/hosts"))
if err := jinfo.SetHostsPath("/etc/hosts"); err != nil {
return err
}
return jinfo.SetResolvConfPath("/etc/resolv.conf")
}
// Leave method is invoked when a Sandbox detaches from an endpoint.
+163 -19
View File
@@ -1,7 +1,8 @@
package remote
import (
"errors"
"fmt"
"net"
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/plugins"
@@ -9,59 +10,202 @@ import (
"github.com/docker/libnetwork/types"
)
var errNoCallback = errors.New("No Callback handler registered with Driver")
type driver struct {
endpoint *plugins.Client
networkType string
}
// Init does the necessary work to register remote drivers
func newDriver(name string, client *plugins.Client) driverapi.Driver {
return &driver{networkType: name, endpoint: client}
}
// Init makes sure a remote driver is registered when a network driver
// plugin is activated.
func Init(dc driverapi.DriverCallback) error {
plugins.Handle(driverapi.NetworkPluginEndpointType, func(name string, client *plugins.Client) {
// TODO : Handhake with the Remote Plugin goes here
newDriver := &driver{networkType: name, endpoint: client}
if err := dc.RegisterDriver(name, newDriver); err != nil {
log.Errorf("Error registering Driver for %s due to %v", name, err)
if err := dc.RegisterDriver(name, newDriver(name, client)); err != nil {
log.Errorf("error registering driver for %s due to %v", name, err)
}
})
return nil
}
// Config is not implemented for remote drivers, since it is assumed
// to be supplied to the remote process out-of-band (e.g., as command
// line arguments).
func (d *driver) Config(option map[string]interface{}) error {
return driverapi.ErrNotImplemented
return &driverapi.ErrNotImplemented{}
}
func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) error {
return driverapi.ErrNotImplemented
func (d *driver) call(methodName string, arg interface{}, retVal maybeError) error {
method := driverapi.NetworkPluginEndpointType + "." + methodName
err := d.endpoint.Call(method, arg, retVal)
if err != nil {
return err
}
if e := retVal.getError(); e != "" {
return fmt.Errorf("remote: %s", e)
}
return nil
}
func (d *driver) CreateNetwork(id types.UUID, options map[string]interface{}) error {
create := &createNetworkRequest{
NetworkID: string(id),
Options: options,
}
return d.call("CreateNetwork", create, &createNetworkResponse{})
}
func (d *driver) DeleteNetwork(nid types.UUID) error {
return driverapi.ErrNotImplemented
delete := &deleteNetworkRequest{NetworkID: string(nid)}
return d.call("DeleteNetwork", delete, &deleteNetworkResponse{})
}
func (d *driver) CreateEndpoint(nid, eid types.UUID, epInfo driverapi.EndpointInfo, epOptions map[string]interface{}) error {
return driverapi.ErrNotImplemented
if epInfo == nil {
return fmt.Errorf("must not be called with nil EndpointInfo")
}
reqIfaces := make([]*endpointInterface, len(epInfo.Interfaces()))
for i, iface := range epInfo.Interfaces() {
addr4 := iface.Address()
addr6 := iface.AddressIPv6()
reqIfaces[i] = &endpointInterface{
ID: iface.ID(),
Address: addr4.String(),
AddressIPv6: addr6.String(),
MacAddress: iface.MacAddress().String(),
}
}
create := &createEndpointRequest{
NetworkID: string(nid),
EndpointID: string(eid),
Interfaces: reqIfaces,
Options: epOptions,
}
var res createEndpointResponse
if err := d.call("CreateEndpoint", create, &res); err != nil {
return err
}
ifaces, err := res.parseInterfaces()
if err != nil {
return err
}
if len(reqIfaces) > 0 && len(ifaces) > 0 {
// We're not supposed to add interfaces if there already are
// some. Attempt to roll back
return errorWithRollback("driver attempted to add more interfaces", d.DeleteEndpoint(nid, eid))
}
for _, iface := range ifaces {
var addr4, addr6 net.IPNet
if iface.Address != nil {
addr4 = *(iface.Address)
}
if iface.AddressIPv6 != nil {
addr6 = *(iface.AddressIPv6)
}
if err := epInfo.AddInterface(iface.ID, iface.MacAddress, addr4, addr6); err != nil {
return errorWithRollback(fmt.Sprintf("failed to AddInterface %v: %s", iface, err), d.DeleteEndpoint(nid, eid))
}
}
return nil
}
func errorWithRollback(msg string, err error) error {
rollback := "rolled back"
if err != nil {
rollback = "failed to roll back: " + err.Error()
}
return fmt.Errorf("%s; %s", msg, rollback)
}
func (d *driver) DeleteEndpoint(nid, eid types.UUID) error {
return driverapi.ErrNotImplemented
delete := &deleteEndpointRequest{
NetworkID: string(nid),
EndpointID: string(eid),
}
return d.call("DeleteEndpoint", delete, &deleteEndpointResponse{})
}
func (d *driver) EndpointOperInfo(nid, eid types.UUID) (map[string]interface{}, error) {
return nil, driverapi.ErrNotImplemented
info := &endpointInfoRequest{
NetworkID: string(nid),
EndpointID: string(eid),
}
var res endpointInfoResponse
if err := d.call("EndpointOperInfo", info, &res); err != nil {
return nil, err
}
return res.Value, nil
}
// Join method is invoked when a Sandbox is attached to an endpoint.
func (d *driver) Join(nid, eid types.UUID, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
return driverapi.ErrNotImplemented
join := &joinRequest{
NetworkID: string(nid),
EndpointID: string(eid),
SandboxKey: sboxKey,
Options: options,
}
var (
res joinResponse
err error
)
if err = d.call("Join", join, &res); err != nil {
return err
}
// Expect each interface ID given by CreateEndpoint to have an
// entry at that index in the names supplied here. In other words,
// if you supply 0..n interfaces with IDs 0..n above, you should
// supply the names in the same order.
ifaceNames := res.InterfaceNames
for _, iface := range jinfo.InterfaceNames() {
i := iface.ID()
if i >= len(ifaceNames) || i < 0 {
return fmt.Errorf("no correlating interface %d in supplied interface names", i)
}
supplied := ifaceNames[i]
if err := iface.SetNames(supplied.SrcName, supplied.DstName); err != nil {
return errorWithRollback(fmt.Sprintf("failed to set interface name: %s", err), d.Leave(nid, eid))
}
}
var addr net.IP
if res.Gateway != "" {
if addr = net.ParseIP(res.Gateway); addr == nil {
return fmt.Errorf(`unable to parse Gateway "%s"`, res.Gateway)
}
if jinfo.SetGateway(addr) != nil {
return errorWithRollback(fmt.Sprintf("failed to set gateway: %v", addr), d.Leave(nid, eid))
}
}
if res.GatewayIPv6 != "" {
if addr = net.ParseIP(res.GatewayIPv6); addr == nil {
return fmt.Errorf(`unable to parse GatewayIPv6 "%s"`, res.GatewayIPv6)
}
if jinfo.SetGatewayIPv6(addr) != nil {
return errorWithRollback(fmt.Sprintf("failed to set gateway IPv6: %v", addr), d.Leave(nid, eid))
}
}
if jinfo.SetHostsPath(res.HostsPath) != nil {
return errorWithRollback(fmt.Sprintf("failed to set hosts path: %s", res.HostsPath), d.Leave(nid, eid))
}
if jinfo.SetResolvConfPath(res.ResolvConfPath) != nil {
return errorWithRollback(fmt.Sprintf("failed to set resolv.conf path: %s", res.ResolvConfPath), d.Leave(nid, eid))
}
return nil
}
// Leave method is invoked when a Sandbox detaches from an endpoint.
func (d *driver) Leave(nid, eid types.UUID) error {
return driverapi.ErrNotImplemented
leave := &leaveRequest{
NetworkID: string(nid),
EndpointID: string(eid),
}
return d.call("Leave", leave, &leaveResponse{})
}
func (d *driver) Type() string {
+397
View File
@@ -0,0 +1,397 @@
package remote
import (
"encoding/json"
"fmt"
"net"
"net/http"
"os"
"testing"
"github.com/docker/docker/pkg/plugins"
"github.com/docker/libnetwork/driverapi"
_ "github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/types"
)
func decodeToMap(r *http.Request) (res map[string]interface{}, err error) {
err = json.NewDecoder(r.Body).Decode(&res)
return
}
func handle(t *testing.T, mux *http.ServeMux, method string, h func(map[string]interface{}) interface{}) {
mux.HandleFunc(fmt.Sprintf("/%s.%s", driverapi.NetworkPluginEndpointType, method), func(w http.ResponseWriter, r *http.Request) {
ask, err := decodeToMap(r)
if err != nil {
t.Fatal(err)
}
answer := h(ask)
err = json.NewEncoder(w).Encode(&answer)
if err != nil {
t.Fatal(err)
}
})
}
func setupPlugin(t *testing.T, name string, mux *http.ServeMux) func() {
if err := os.MkdirAll("/usr/share/docker/plugins", 0755); err != nil {
t.Fatal(err)
}
listener, err := net.Listen("unix", fmt.Sprintf("/usr/share/docker/plugins/%s.sock", name))
if err != nil {
t.Fatal("Could not listen to the plugin socket")
}
mux.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `{"Implements": ["%s"]}`, driverapi.NetworkPluginEndpointType)
})
go http.Serve(listener, mux)
return func() {
listener.Close()
if err := os.RemoveAll("/usr/share/docker/plugins"); err != nil {
t.Fatal(err)
}
}
}
type testEndpoint struct {
t *testing.T
id int
src string
dst string
address string
addressIPv6 string
macAddress string
gateway string
gatewayIPv6 string
resolvConfPath string
hostsPath string
}
func (test *testEndpoint) Interfaces() []driverapi.InterfaceInfo {
// return an empty one so we don't trip the check for existing
// interfaces; we don't care about this after that
return []driverapi.InterfaceInfo{}
}
func (test *testEndpoint) AddInterface(ID int, mac net.HardwareAddr, ipv4 net.IPNet, ipv6 net.IPNet) error {
if ID != test.id {
test.t.Fatalf("Wrong ID passed to AddInterface: %d", ID)
}
ip4, net4, _ := net.ParseCIDR(test.address)
ip6, net6, _ := net.ParseCIDR(test.addressIPv6)
if ip4 != nil {
net4.IP = ip4
if !types.CompareIPNet(net4, &ipv4) {
test.t.Fatalf("Wrong address given %+v", ipv4)
}
}
if ip6 != nil {
net6.IP = ip6
if !types.CompareIPNet(net6, &ipv6) {
test.t.Fatalf("Wrong address (IPv6) given %+v", ipv6)
}
}
if test.macAddress != "" && mac.String() != test.macAddress {
test.t.Fatalf("Wrong MAC address given %v", mac)
}
return nil
}
func (test *testEndpoint) InterfaceNames() []driverapi.InterfaceNameInfo {
return []driverapi.InterfaceNameInfo{test}
}
func compareIPs(t *testing.T, kind string, shouldBe string, supplied net.IP) {
ip := net.ParseIP(shouldBe)
if ip == nil {
t.Fatalf(`Invalid IP to test against: "%s"`, shouldBe)
}
if !ip.Equal(supplied) {
t.Fatalf(`%s IPs are not equal: expected "%s", got %v`, kind, shouldBe, supplied)
}
}
func (test *testEndpoint) SetGateway(ipv4 net.IP) error {
compareIPs(test.t, "Gateway", test.gateway, ipv4)
return nil
}
func (test *testEndpoint) SetGatewayIPv6(ipv6 net.IP) error {
compareIPs(test.t, "GatewayIPv6", test.gatewayIPv6, ipv6)
return nil
}
func (test *testEndpoint) SetHostsPath(p string) error {
if p != test.hostsPath {
test.t.Fatalf(`Wrong HostsPath; expected "%s", got "%s"`, test.hostsPath, p)
}
return nil
}
func (test *testEndpoint) SetResolvConfPath(p string) error {
if p != test.resolvConfPath {
test.t.Fatalf(`Wrong ResolvConfPath; expected "%s", got "%s"`, test.resolvConfPath, p)
}
return nil
}
func (test *testEndpoint) SetNames(src string, dst string) error {
if test.src != src {
test.t.Fatalf(`Wrong SrcName; expected "%s", got "%s"`, test.src, src)
}
if test.dst != dst {
test.t.Fatalf(`Wrong DstName; expected "%s", got "%s"`, test.dst, dst)
}
return nil
}
func (test *testEndpoint) ID() int {
return test.id
}
func TestRemoteDriver(t *testing.T) {
var plugin = "test-net-driver"
ep := &testEndpoint{
t: t,
src: "vethsrc",
dst: "vethdst",
address: "192.168.5.7/16",
addressIPv6: "2001:DB8::5:7/48",
macAddress: "7a:56:78:34:12:da",
gateway: "192.168.0.1",
gatewayIPv6: "2001:DB8::1",
hostsPath: "/here/comes/the/host/path",
resolvConfPath: "/there/goes/the/resolv/conf",
}
mux := http.NewServeMux()
defer setupPlugin(t, plugin, mux)()
var networkID string
handle(t, mux, "CreateNetwork", func(msg map[string]interface{}) interface{} {
nid := msg["NetworkID"]
var ok bool
if networkID, ok = nid.(string); !ok {
t.Fatal("RPC did not include network ID string")
}
return map[string]interface{}{}
})
handle(t, mux, "DeleteNetwork", func(msg map[string]interface{}) interface{} {
if nid, ok := msg["NetworkID"]; !ok || nid != networkID {
t.Fatal("Network ID missing or does not match that created")
}
return map[string]interface{}{}
})
handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
iface := map[string]interface{}{
"ID": ep.id,
"Address": ep.address,
"AddressIPv6": ep.addressIPv6,
"MacAddress": ep.macAddress,
}
return map[string]interface{}{
"Interfaces": []interface{}{iface},
}
})
handle(t, mux, "Join", func(msg map[string]interface{}) interface{} {
options := msg["Options"].(map[string]interface{})
foo, ok := options["foo"].(string)
if !ok || foo != "fooValue" {
t.Fatalf("Did not receive expected foo string in request options: %+v", msg)
}
return map[string]interface{}{
"Gateway": ep.gateway,
"GatewayIPv6": ep.gatewayIPv6,
"HostsPath": ep.hostsPath,
"ResolvConfPath": ep.resolvConfPath,
"InterfaceNames": []map[string]interface{}{
map[string]interface{}{
"SrcName": ep.src,
"DstName": ep.dst,
},
},
}
})
handle(t, mux, "Leave", func(msg map[string]interface{}) interface{} {
return map[string]string{}
})
handle(t, mux, "DeleteEndpoint", func(msg map[string]interface{}) interface{} {
return map[string]interface{}{}
})
handle(t, mux, "EndpointOperInfo", func(msg map[string]interface{}) interface{} {
return map[string]interface{}{
"Value": map[string]string{
"Arbitrary": "key",
"Value": "pairs?",
},
}
})
p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
if err != nil {
t.Fatal(err)
}
driver := newDriver(plugin, p.Client)
if driver.Type() != plugin {
t.Fatal("Driver type does not match that given")
}
netID := types.UUID("dummy-network")
err = driver.CreateNetwork(netID, map[string]interface{}{})
if err != nil {
t.Fatal(err)
}
endID := types.UUID("dummy-endpoint")
err = driver.CreateEndpoint(netID, endID, ep, map[string]interface{}{})
if err != nil {
t.Fatal(err)
}
joinOpts := map[string]interface{}{"foo": "fooValue"}
err = driver.Join(netID, endID, "sandbox-key", ep, joinOpts)
if err != nil {
t.Fatal(err)
}
if _, err = driver.EndpointOperInfo(netID, endID); err != nil {
t.Fatal(err)
}
if err = driver.Leave(netID, endID); err != nil {
t.Fatal(err)
}
if err = driver.DeleteEndpoint(netID, endID); err != nil {
t.Fatal(err)
}
if err = driver.DeleteNetwork(netID); err != nil {
t.Fatal(err)
}
}
type failEndpoint struct {
t *testing.T
}
func (f *failEndpoint) Interfaces() []*driverapi.InterfaceInfo {
f.t.Fatal("Unexpected call of Interfaces")
return nil
}
func (f *failEndpoint) AddInterface(int, net.HardwareAddr, net.IPNet, net.IPNet) error {
f.t.Fatal("Unexpected call of AddInterface")
return nil
}
func TestDriverError(t *testing.T) {
var plugin = "test-net-driver-error"
mux := http.NewServeMux()
defer setupPlugin(t, plugin, mux)()
handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
return map[string]interface{}{
"Err": "this should get raised as an error",
}
})
p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
if err != nil {
t.Fatal(err)
}
driver := newDriver(plugin, p.Client)
if err := driver.CreateEndpoint(types.UUID("dummy"), types.UUID("dummy"), &testEndpoint{t: t}, map[string]interface{}{}); err == nil {
t.Fatalf("Expected error from driver")
}
}
func TestMissingValues(t *testing.T) {
var plugin = "test-net-driver-missing"
mux := http.NewServeMux()
defer setupPlugin(t, plugin, mux)()
ep := &testEndpoint{
t: t,
id: 0,
}
handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
iface := map[string]interface{}{
"ID": ep.id,
"Address": ep.address,
"AddressIPv6": ep.addressIPv6,
"MacAddress": ep.macAddress,
}
return map[string]interface{}{
"Interfaces": []interface{}{iface},
}
})
p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
if err != nil {
t.Fatal(err)
}
driver := newDriver(plugin, p.Client)
if err := driver.CreateEndpoint(types.UUID("dummy"), types.UUID("dummy"), ep, map[string]interface{}{}); err != nil {
t.Fatal(err)
}
}
type rollbackEndpoint struct {
}
func (r *rollbackEndpoint) Interfaces() []driverapi.InterfaceInfo {
return []driverapi.InterfaceInfo{}
}
func (r *rollbackEndpoint) AddInterface(_ int, _ net.HardwareAddr, _ net.IPNet, _ net.IPNet) error {
return fmt.Errorf("fail this to trigger a rollback")
}
func TestRollback(t *testing.T) {
var plugin = "test-net-driver-rollback"
mux := http.NewServeMux()
defer setupPlugin(t, plugin, mux)()
rolledback := false
handle(t, mux, "CreateEndpoint", func(msg map[string]interface{}) interface{} {
iface := map[string]interface{}{
"ID": 0,
"Address": "192.168.4.5/16",
"AddressIPv6": "",
"MacAddress": "7a:12:34:56:78:90",
}
return map[string]interface{}{
"Interfaces": []interface{}{iface},
}
})
handle(t, mux, "DeleteEndpoint", func(msg map[string]interface{}) interface{} {
rolledback = true
return map[string]interface{}{}
})
p, err := plugins.Get(plugin, driverapi.NetworkPluginEndpointType)
if err != nil {
t.Fatal(err)
}
driver := newDriver(plugin, p.Client)
ep := &rollbackEndpoint{}
if err := driver.CreateEndpoint(types.UUID("dummy"), types.UUID("dummy"), ep, map[string]interface{}{}); err == nil {
t.Fatalf("Expected error from driver")
}
if !rolledback {
t.Fatalf("Expected to have had DeleteEndpoint called")
}
}
+143
View File
@@ -0,0 +1,143 @@
package remote
import "net"
type response struct {
Err string
}
type maybeError interface {
getError() string
}
func (r *response) getError() string {
return r.Err
}
type createNetworkRequest struct {
NetworkID string
Options map[string]interface{}
}
type createNetworkResponse struct {
response
}
type deleteNetworkRequest struct {
NetworkID string
}
type deleteNetworkResponse struct {
response
}
type createEndpointRequest struct {
NetworkID string
EndpointID string
Interfaces []*endpointInterface
Options map[string]interface{}
}
type endpointInterface struct {
ID int
Address string
AddressIPv6 string
MacAddress string
}
type createEndpointResponse struct {
response
Interfaces []*endpointInterface
}
func toAddr(ipAddr string) (*net.IPNet, error) {
ip, ipnet, err := net.ParseCIDR(ipAddr)
if err != nil {
return nil, err
}
ipnet.IP = ip
return ipnet, nil
}
type iface struct {
ID int
Address *net.IPNet
AddressIPv6 *net.IPNet
MacAddress net.HardwareAddr
}
func (r *createEndpointResponse) parseInterfaces() ([]*iface, error) {
var (
ifaces = make([]*iface, len(r.Interfaces))
)
for i, inIf := range r.Interfaces {
var err error
outIf := &iface{ID: inIf.ID}
if inIf.Address != "" {
if outIf.Address, err = toAddr(inIf.Address); err != nil {
return nil, err
}
}
if inIf.AddressIPv6 != "" {
if outIf.AddressIPv6, err = toAddr(inIf.AddressIPv6); err != nil {
return nil, err
}
}
if inIf.MacAddress != "" {
if outIf.MacAddress, err = net.ParseMAC(inIf.MacAddress); err != nil {
return nil, err
}
}
ifaces[i] = outIf
}
return ifaces, nil
}
type deleteEndpointRequest struct {
NetworkID string
EndpointID string
}
type deleteEndpointResponse struct {
response
}
type endpointInfoRequest struct {
NetworkID string
EndpointID string
}
type endpointInfoResponse struct {
response
Value map[string]interface{}
}
type joinRequest struct {
NetworkID string
EndpointID string
SandboxKey string
Options map[string]interface{}
}
type ifaceName struct {
SrcName string
DstName string
}
type joinResponse struct {
response
InterfaceNames []*ifaceName
Gateway string
GatewayIPv6 string
HostsPath string
ResolvConfPath string
}
type leaveRequest struct {
NetworkID string
EndpointID string
}
type leaveResponse struct {
response
}
+66 -13
View File
@@ -2,6 +2,7 @@ package libnetwork
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path"
@@ -12,7 +13,6 @@ import (
"github.com/docker/docker/pkg/ioutils"
"github.com/docker/libnetwork/etchosts"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/resolvconf"
"github.com/docker/libnetwork/sandbox"
"github.com/docker/libnetwork/types"
@@ -46,6 +46,9 @@ type Endpoint interface {
// Delete and detaches this endpoint from the network.
Delete() error
// Retrieve the interfaces' statistics from the sandbox
Statistics() (map[string]*sandbox.InterfaceStatistics, error)
}
// EndpointOption is a option setter function type used to pass varios options to Network
@@ -106,7 +109,7 @@ type endpoint struct {
iFaces []*endpointInterface
joinInfo *endpointJoinInfo
container *containerInfo
exposedPorts []netutils.TransportPort
exposedPorts []types.TransportPort
generic map[string]interface{}
joinLeaveDone chan struct{}
sync.Mutex
@@ -217,7 +220,7 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*Contai
ep.Lock()
if ep.container != nil {
ep.Unlock()
return nil, ErrInvalidJoin
return nil, ErrInvalidJoin{}
}
ep.container = &containerInfo{
@@ -292,7 +295,7 @@ func (ep *endpoint) Join(containerID string, options ...EndpointOption) (*Contai
for _, i := range ifaces {
iface := &sandbox.Interface{
SrcName: i.srcName,
DstName: i.dstName,
DstName: i.dstPrefix,
Address: &i.addr,
}
if i.addrv6.IP.To16() != nil {
@@ -335,7 +338,7 @@ func (ep *endpoint) Leave(containerID string, options ...EndpointOption) error {
if container == nil || container.id == "" ||
containerID == "" || container.id != containerID {
if container == nil {
err = ErrNoContainer
err = ErrNoContainer{}
} else {
err = InvalidContainerIDError(containerID)
}
@@ -403,6 +406,33 @@ func (ep *endpoint) Delete() error {
return err
}
func (ep *endpoint) Statistics() (map[string]*sandbox.InterfaceStatistics, error) {
m := make(map[string]*sandbox.InterfaceStatistics)
ep.Lock()
n := ep.network
skey := ep.container.data.SandboxKey
ep.Unlock()
n.Lock()
c := n.ctrlr
n.Unlock()
sbox := c.sandboxGet(skey)
if sbox == nil {
return m, nil
}
var err error
for _, i := range sbox.Interfaces() {
if m[i.DstName], err = i.Statistics(); err != nil {
return m, err
}
}
return m, nil
}
func (ep *endpoint) buildHostsFiles() error {
var extraContent []etchosts.Record
@@ -413,7 +443,7 @@ func (ep *endpoint) buildHostsFiles() error {
ep.Unlock()
if container == nil {
return ErrNoContainer
return ErrNoContainer{}
}
if container.config.hostsPath == "" {
@@ -463,7 +493,7 @@ func (ep *endpoint) updateParentHosts() error {
ep.Unlock()
if container == nil {
return ErrNoContainer
return ErrNoContainer{}
}
for _, update := range container.config.parentUpdates {
@@ -496,7 +526,7 @@ func (ep *endpoint) updateDNS(resolvConf []byte) error {
ep.Unlock()
if container == nil {
return ErrNoContainer
return ErrNoContainer{}
}
oldHash := []byte{}
@@ -548,6 +578,11 @@ func (ep *endpoint) updateDNS(resolvConf []byte) error {
return err
}
// Change the perms to 0644 since ioutil.TempFile creates it by default as 0600
if err := os.Chmod(tmpResolvFile.Name(), 0644); err != nil {
return err
}
// write the updates to the temp files
if err = ioutil.WriteFile(tmpHashFile.Name(), []byte(newHash), 0644); err != nil {
return err
@@ -563,13 +598,23 @@ func (ep *endpoint) updateDNS(resolvConf []byte) error {
return os.Rename(tmpResolvFile.Name(), container.config.resolvConfPath)
}
func copyFile(src, dst string) error {
sBytes, err := ioutil.ReadFile(src)
if err != nil {
return err
}
return ioutil.WriteFile(dst, sBytes, 0644)
}
func (ep *endpoint) setupDNS() error {
ep.Lock()
container := ep.container
joinInfo := ep.joinInfo
ep.Unlock()
if container == nil {
return ErrNoContainer
return ErrNoContainer{}
}
if container.config.resolvConfPath == "" {
@@ -582,6 +627,14 @@ func (ep *endpoint) setupDNS() error {
return err
}
if joinInfo.resolvConfPath != "" {
if err := copyFile(joinInfo.resolvConfPath, container.config.resolvConfPath); err != nil {
return fmt.Errorf("could not copy source resolv.conf file %s to %s: %v", joinInfo.resolvConfPath, container.config.resolvConfPath, err)
}
return nil
}
resolvConf, err := resolvconf.Get()
if err != nil {
return err
@@ -692,10 +745,10 @@ func JoinOptionUseDefaultSandbox() EndpointOption {
// CreateOptionExposedPorts function returns an option setter for the container exposed
// ports option to be passed to network.CreateEndpoint() method.
func CreateOptionExposedPorts(exposedPorts []netutils.TransportPort) EndpointOption {
func CreateOptionExposedPorts(exposedPorts []types.TransportPort) EndpointOption {
return func(ep *endpoint) {
// Defensive copy
eps := make([]netutils.TransportPort, len(exposedPorts))
eps := make([]types.TransportPort, len(exposedPorts))
copy(eps, exposedPorts)
// Store endpoint label and in generic because driver needs it
ep.exposedPorts = eps
@@ -705,10 +758,10 @@ func CreateOptionExposedPorts(exposedPorts []netutils.TransportPort) EndpointOpt
// CreateOptionPortMapping function returns an option setter for the mapping
// ports option to be passed to network.CreateEndpoint() method.
func CreateOptionPortMapping(portBindings []netutils.PortBinding) EndpointOption {
func CreateOptionPortMapping(portBindings []types.PortBinding) EndpointOption {
return func(ep *endpoint) {
// Store a copy of the bindings as generic data to pass to the driver
pbs := make([]netutils.PortBinding, len(portBindings))
pbs := make([]types.PortBinding, len(portBindings))
copy(pbs, portBindings)
ep.generic[netlabel.PortMap] = pbs
}
+19 -19
View File
@@ -4,7 +4,7 @@ import (
"net"
"github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/types"
)
// EndpointInfo provides an interface to retrieve network resources bound to the endpoint.
@@ -40,12 +40,12 @@ type InterfaceInfo interface {
}
type endpointInterface struct {
id int
mac net.HardwareAddr
addr net.IPNet
addrv6 net.IPNet
srcName string
dstName string
id int
mac net.HardwareAddr
addr net.IPNet
addrv6 net.IPNet
srcName string
dstPrefix string
}
type endpointJoinInfo struct {
@@ -105,10 +105,10 @@ func (ep *endpoint) AddInterface(id int, mac net.HardwareAddr, ipv4 net.IPNet, i
iface := &endpointInterface{
id: id,
addr: *netutils.GetIPNetCopy(&ipv4),
addrv6: *netutils.GetIPNetCopy(&ipv6),
addr: *types.GetIPNetCopy(&ipv4),
addrv6: *types.GetIPNetCopy(&ipv6),
}
iface.mac = netutils.GetMacCopy(mac)
iface.mac = types.GetMacCopy(mac)
ep.iFaces = append(ep.iFaces, iface)
return nil
@@ -119,20 +119,20 @@ func (i *endpointInterface) ID() int {
}
func (i *endpointInterface) MacAddress() net.HardwareAddr {
return netutils.GetMacCopy(i.mac)
return types.GetMacCopy(i.mac)
}
func (i *endpointInterface) Address() net.IPNet {
return (*netutils.GetIPNetCopy(&i.addr))
return (*types.GetIPNetCopy(&i.addr))
}
func (i *endpointInterface) AddressIPv6() net.IPNet {
return (*netutils.GetIPNetCopy(&i.addrv6))
return (*types.GetIPNetCopy(&i.addrv6))
}
func (i *endpointInterface) SetNames(srcName string, dstName string) error {
func (i *endpointInterface) SetNames(srcName string, dstPrefix string) error {
i.srcName = srcName
i.dstName = dstName
i.dstPrefix = dstPrefix
return nil
}
@@ -168,7 +168,7 @@ func (ep *endpoint) Gateway() net.IP {
return net.IP{}
}
return netutils.GetIPCopy(ep.joinInfo.gw)
return types.GetIPCopy(ep.joinInfo.gw)
}
func (ep *endpoint) GatewayIPv6() net.IP {
@@ -179,14 +179,14 @@ func (ep *endpoint) GatewayIPv6() net.IP {
return net.IP{}
}
return netutils.GetIPCopy(ep.joinInfo.gw6)
return types.GetIPCopy(ep.joinInfo.gw6)
}
func (ep *endpoint) SetGateway(gw net.IP) error {
ep.Lock()
defer ep.Unlock()
ep.joinInfo.gw = netutils.GetIPCopy(gw)
ep.joinInfo.gw = types.GetIPCopy(gw)
return nil
}
@@ -194,7 +194,7 @@ func (ep *endpoint) SetGatewayIPv6(gw6 net.IP) error {
ep.Lock()
defer ep.Unlock()
ep.joinInfo.gw6 = netutils.GetIPCopy(gw6)
ep.joinInfo.gw6 = types.GetIPCopy(gw6)
return nil
}
+97 -27
View File
@@ -1,34 +1,83 @@
package libnetwork
import (
"errors"
"fmt"
)
var (
// ErrNoSuchNetwork is returned when a network query finds no result
ErrNoSuchNetwork = errors.New("network not found")
// ErrNoSuchEndpoint is returned when a endpoint query finds no result
ErrNoSuchEndpoint = errors.New("endpoint not found")
// ErrNilNetworkDriver is returned if a nil network driver
// is passed to NewNetwork api.
ErrNilNetworkDriver = errors.New("nil NetworkDriver instance")
// ErrInvalidNetworkDriver is returned if an invalid driver
// instance is passed.
ErrInvalidNetworkDriver = errors.New("invalid driver bound to network")
// ErrInvalidJoin is returned if a join is attempted on an endpoint
// which already has a container joined.
ErrInvalidJoin = errors.New("a container has already joined the endpoint")
// ErrNoContainer is returned when the endpoint has no container
// attached to it.
ErrNoContainer = errors.New("no container attached to the endpoint")
// ErrInvalidID is returned when a query-by-id method is being invoked
// with an empty id parameter
ErrInvalidID = errors.New("invalid ID")
// ErrInvalidName is returned when a query-by-name or resource create method is
// invoked with an empty name parameter
ErrInvalidName = errors.New("invalid Name")
)
// ErrNoSuchNetwork is returned when a network query finds no result
type ErrNoSuchNetwork string
func (nsn ErrNoSuchNetwork) Error() string {
return fmt.Sprintf("network %s not found", string(nsn))
}
// BadRequest denotes the type of this error
func (nsn ErrNoSuchNetwork) BadRequest() {}
// ErrNoSuchEndpoint is returned when a endpoint query finds no result
type ErrNoSuchEndpoint string
func (nse ErrNoSuchEndpoint) Error() string {
return fmt.Sprintf("endpoint %s not found", string(nse))
}
// BadRequest denotes the type of this error
func (nse ErrNoSuchEndpoint) BadRequest() {}
// ErrInvalidNetworkDriver is returned if an invalid driver
// name is passed.
type ErrInvalidNetworkDriver string
func (ind ErrInvalidNetworkDriver) Error() string {
return fmt.Sprintf("invalid driver bound to network: %s", string(ind))
}
// BadRequest denotes the type of this error
func (ind ErrInvalidNetworkDriver) BadRequest() {}
// ErrInvalidJoin is returned if a join is attempted on an endpoint
// which already has a container joined.
type ErrInvalidJoin struct{}
func (ij ErrInvalidJoin) Error() string {
return "a container has already joined the endpoint"
}
// BadRequest denotes the type of this error
func (ij ErrInvalidJoin) BadRequest() {}
// ErrNoContainer is returned when the endpoint has no container
// attached to it.
type ErrNoContainer struct{}
func (nc ErrNoContainer) Error() string {
return "a container has already joined the endpoint"
}
// Maskable denotes the type of this error
func (nc ErrNoContainer) Maskable() {}
// ErrInvalidID is returned when a query-by-id method is being invoked
// with an empty id parameter
type ErrInvalidID string
func (ii ErrInvalidID) Error() string {
return fmt.Sprintf("invalid id: %s", string(ii))
}
// BadRequest denotes the type of this error
func (ii ErrInvalidID) BadRequest() {}
// ErrInvalidName is returned when a query-by-name or resource create method is
// invoked with an empty name parameter
type ErrInvalidName string
func (in ErrInvalidName) Error() string {
return fmt.Sprintf("invalid name: %s", string(in))
}
// BadRequest denotes the type of this error
func (in ErrInvalidName) BadRequest() {}
// NetworkTypeError type is returned when the network type string is not
// known to libnetwork.
@@ -38,13 +87,19 @@ func (nt NetworkTypeError) Error() string {
return fmt.Sprintf("unknown driver %q", string(nt))
}
// NotFound denotes the type of this error
func (nt NetworkTypeError) NotFound() {}
// NetworkNameError is returned when a network with the same name already exists.
type NetworkNameError string
func (name NetworkNameError) Error() string {
return fmt.Sprintf("network with name %s already exists", string(name))
func (nnr NetworkNameError) Error() string {
return fmt.Sprintf("network with name %s already exists", string(nnr))
}
// Forbidden denotes the type of this error
func (nnr NetworkNameError) Forbidden() {}
// UnknownNetworkError is returned when libnetwork could not find in it's database
// a network with the same name and id.
type UnknownNetworkError struct {
@@ -56,6 +111,9 @@ func (une *UnknownNetworkError) Error() string {
return fmt.Sprintf("unknown network %s id %s", une.name, une.id)
}
// NotFound denotes the type of this error
func (une *UnknownNetworkError) NotFound() {}
// ActiveEndpointsError is returned when a network is deleted which has active
// endpoints in it.
type ActiveEndpointsError struct {
@@ -67,6 +125,9 @@ func (aee *ActiveEndpointsError) Error() string {
return fmt.Sprintf("network with name %s id %s has active endpoints", aee.name, aee.id)
}
// Forbidden denotes the type of this error
func (aee *ActiveEndpointsError) Forbidden() {}
// UnknownEndpointError is returned when libnetwork could not find in it's database
// an endpoint with the same name and id.
type UnknownEndpointError struct {
@@ -78,6 +139,9 @@ func (uee *UnknownEndpointError) Error() string {
return fmt.Sprintf("unknown endpoint %s id %s", uee.name, uee.id)
}
// NotFound denotes the type of this error
func (uee *UnknownEndpointError) NotFound() {}
// ActiveContainerError is returned when an endpoint is deleted which has active
// containers attached to it.
type ActiveContainerError struct {
@@ -89,6 +153,9 @@ func (ace *ActiveContainerError) Error() string {
return fmt.Sprintf("endpoint with name %s id %s has active containers", ace.name, ace.id)
}
// Forbidden denotes the type of this error
func (ace *ActiveContainerError) Forbidden() {}
// InvalidContainerIDError is returned when an invalid container id is passed
// in Join/Leave
type InvalidContainerIDError string
@@ -96,3 +163,6 @@ type InvalidContainerIDError string
func (id InvalidContainerIDError) Error() string {
return fmt.Sprintf("invalid container id %s", string(id))
}
// BadRequest denotes the type of this error
func (id InvalidContainerIDError) BadRequest() {}
+51
View File
@@ -0,0 +1,51 @@
package libnetwork
import (
"testing"
"github.com/docker/libnetwork/types"
)
func TestErrorInterfaces(t *testing.T) {
badRequestErrorList := []error{ErrInvalidID(""), ErrInvalidName(""), ErrInvalidJoin{}, ErrInvalidNetworkDriver(""), InvalidContainerIDError(""), ErrNoSuchNetwork(""), ErrNoSuchEndpoint("")}
for _, err := range badRequestErrorList {
switch u := err.(type) {
case types.BadRequestError:
return
default:
t.Fatalf("Failed to detect err %v is of type BadRequestError. Got type: %T", err, u)
}
}
maskableErrorList := []error{ErrNoContainer{}}
for _, err := range maskableErrorList {
switch u := err.(type) {
case types.MaskableError:
return
default:
t.Fatalf("Failed to detect err %v is of type MaskableError. Got type: %T", err, u)
}
}
notFoundErrorList := []error{NetworkTypeError(""), &UnknownNetworkError{}, &UnknownEndpointError{}}
for _, err := range notFoundErrorList {
switch u := err.(type) {
case types.NotFoundError:
return
default:
t.Fatalf("Failed to detect err %v is of type NotFoundError. Got type: %T", err, u)
}
}
forbiddenErrorList := []error{NetworkTypeError(""), &UnknownNetworkError{}, &UnknownEndpointError{}}
for _, err := range forbiddenErrorList {
switch u := err.(type) {
case types.ForbiddenError:
return
default:
t.Fatalf("Failed to detect err %v is of type ForbiddenError. Got type: %T", err, u)
}
}
}
+23 -15
View File
@@ -44,9 +44,10 @@ var (
// Chain defines the iptables chain.
type Chain struct {
Name string
Bridge string
Table Table
Name string
Bridge string
Table Table
HairpinMode bool
}
// ChainError is returned to represent errors during ip table operation.
@@ -75,9 +76,10 @@ func initCheck() error {
// NewChain adds a new chain to ip table.
func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error) {
c := &Chain{
Name: name,
Bridge: bridge,
Table: table,
Name: name,
Bridge: bridge,
Table: table,
HairpinMode: hairpinMode,
}
if string(c.Table) == "" {
@@ -97,7 +99,8 @@ func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error
case Nat:
preroute := []string{
"-m", "addrtype",
"--dst-type", "LOCAL"}
"--dst-type", "LOCAL",
"-j", c.Name}
if !Exists(Nat, "PREROUTING", preroute...) {
if err := c.Prerouting(Append, preroute...); err != nil {
return nil, fmt.Errorf("Failed to inject docker in PREROUTING chain: %s", err)
@@ -105,7 +108,8 @@ func NewChain(name, bridge string, table Table, hairpinMode bool) (*Chain, error
}
output := []string{
"-m", "addrtype",
"--dst-type", "LOCAL"}
"--dst-type", "LOCAL",
"-j", c.Name}
if !hairpinMode {
output = append(output, "!", "--dst", "127.0.0.0/8")
}
@@ -151,12 +155,16 @@ func (c *Chain) Forward(action Action, ip net.IP, port int, proto, destAddr stri
// value" by both iptables and ip6tables.
daddr = "0/0"
}
if output, err := Raw("-t", string(Nat), string(action), c.Name,
args := []string{"-t", string(Nat), string(action), c.Name,
"-p", proto,
"-d", daddr,
"--dport", strconv.Itoa(port),
"-j", "DNAT",
"--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))); err != nil {
"--to-destination", net.JoinHostPort(destAddr, strconv.Itoa(destPort))}
if !c.HairpinMode {
args = append(args, "!", "-i", c.Bridge)
}
if output, err := Raw(args...); err != nil {
return err
} else if len(output) != 0 {
return ChainError{Chain: "FORWARD", Output: output}
@@ -222,7 +230,7 @@ func (c *Chain) Prerouting(action Action, args ...string) error {
if len(args) > 0 {
a = append(a, args...)
}
if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
if output, err := Raw(a...); err != nil {
return err
} else if len(output) != 0 {
return ChainError{Chain: "PREROUTING", Output: output}
@@ -236,7 +244,7 @@ func (c *Chain) Output(action Action, args ...string) error {
if len(args) > 0 {
a = append(a, args...)
}
if output, err := Raw(append(a, "-j", c.Name)...); err != nil {
if output, err := Raw(a...); err != nil {
return err
} else if len(output) != 0 {
return ChainError{Chain: "OUTPUT", Output: output}
@@ -248,9 +256,9 @@ func (c *Chain) Output(action Action, args ...string) error {
func (c *Chain) Remove() error {
// Ignore errors - This could mean the chains were never set up
if c.Table == Nat {
c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL")
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8")
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL") // Created in versions <= 0.1.6
c.Prerouting(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "-j", c.Name)
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "!", "--dst", "127.0.0.0/8", "-j", c.Name)
c.Output(Delete, "-m", "addrtype", "--dst-type", "LOCAL", "-j", c.Name) // Created in versions <= 0.1.6
c.Prerouting(Delete)
c.Output(Delete)
+6 -15
View File
@@ -11,7 +11,7 @@ import (
_ "github.com/docker/libnetwork/netutils"
)
const chainName = "DOCKER-TEST"
const chainName = "DOCKEREST"
var natChain *Chain
var filterChain *Chain
@@ -48,6 +48,7 @@ func TestForward(t *testing.T) {
"--dport", strconv.Itoa(port),
"-j", "DNAT",
"--to-destination", dstAddr + ":" + strconv.Itoa(dstPort),
"!", "-i", natChain.Bridge,
}
if !Exists(natChain.Table, natChain.Name, dnatRule...) {
@@ -130,16 +131,11 @@ func TestPrerouting(t *testing.T) {
t.Fatal(err)
}
rule := []string{
"-j", natChain.Name}
rule = append(rule, args...)
if !Exists(natChain.Table, "PREROUTING", rule...) {
if !Exists(natChain.Table, "PREROUTING", args...) {
t.Fatalf("rule does not exist")
}
delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, rule...)
delRule := append([]string{"-D", "PREROUTING", "-t", string(Nat)}, args...)
if _, err = Raw(delRule...); err != nil {
t.Fatal(err)
}
@@ -155,17 +151,12 @@ func TestOutput(t *testing.T) {
t.Fatal(err)
}
rule := []string{
"-j", natChain.Name}
rule = append(rule, args...)
if !Exists(natChain.Table, "OUTPUT", rule...) {
if !Exists(natChain.Table, "OUTPUT", args...) {
t.Fatalf("rule does not exist")
}
delRule := append([]string{"-D", "OUTPUT", "-t",
string(natChain.Table)}, rule...)
string(natChain.Table)}, args...)
if _, err = Raw(delRule...); err != nil {
t.Fatal(err)
}
+155 -26
View File
@@ -22,6 +22,8 @@ import (
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/options"
"github.com/docker/libnetwork/types"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
)
@@ -65,11 +67,11 @@ func getEmptyGenericOption() map[string]interface{} {
return genericOption
}
func getPortMapping() []netutils.PortBinding {
return []netutils.PortBinding{
netutils.PortBinding{Proto: netutils.TCP, Port: uint16(230), HostPort: uint16(23000)},
netutils.PortBinding{Proto: netutils.UDP, Port: uint16(200), HostPort: uint16(22000)},
netutils.PortBinding{Proto: netutils.TCP, Port: uint16(120), HostPort: uint16(12000)},
func getPortMapping() []types.PortBinding {
return []types.PortBinding{
types.PortBinding{Proto: types.TCP, Port: uint16(230), HostPort: uint16(23000)},
types.PortBinding{Proto: types.UDP, Port: uint16(200), HostPort: uint16(22000)},
types.PortBinding{Proto: types.TCP, Port: uint16(120), HostPort: uint16(12000)},
}
}
@@ -245,7 +247,7 @@ func TestBridge(t *testing.T) {
if !ok {
t.Fatalf("Could not find expected info in endpoint data")
}
pm, ok := pmd.([]netutils.PortBinding)
pm, ok := pmd.([]types.PortBinding)
if !ok {
t.Fatalf("Unexpected format for port mapping in endpoint operational data")
}
@@ -289,7 +291,7 @@ func TestNilRemoteDriver(t *testing.T) {
t.Fatal("Expected to fail. But instead succeeded")
}
if err != plugins.ErrNotFound {
if _, ok := err.(types.NotFoundError); !ok {
t.Fatalf("Did not fail with expected error. Actual error: %v", err)
}
}
@@ -337,8 +339,9 @@ func TestNetworkName(t *testing.T) {
if err == nil {
t.Fatal("Expected to fail. But instead succeeded")
}
if err != libnetwork.ErrInvalidName {
t.Fatal("Expected to fail with ErrInvalidName error")
if _, ok := err.(libnetwork.ErrInvalidName); !ok {
t.Fatalf("Expected to fail with ErrInvalidName error. Got %v", err)
}
networkName := "testnetwork"
@@ -474,8 +477,8 @@ func TestUnknownEndpoint(t *testing.T) {
if err == nil {
t.Fatal("Expected to fail. But instead succeeded")
}
if err != libnetwork.ErrInvalidName {
t.Fatal("Expected to fail with ErrInvalidName error")
if _, ok := err.(libnetwork.ErrInvalidName); !ok {
t.Fatalf("Expected to fail with ErrInvalidName error. Actual error: %v", err)
}
ep, err := network.CreateEndpoint("testep")
@@ -612,15 +615,15 @@ func TestControllerQuery(t *testing.T) {
if err == nil {
t.Fatalf("NetworkByName() succeeded with invalid target name")
}
if err != libnetwork.ErrInvalidName {
t.Fatalf("NetworkByName() failed with unexpected error: %v", err)
if _, ok := err.(libnetwork.ErrInvalidName); !ok {
t.Fatalf("Expected NetworkByName() to fail with ErrInvalidName error. Got: %v", err)
}
_, err = controller.NetworkByID("")
if err == nil {
t.Fatalf("NetworkByID() succeeded with invalid target id")
}
if err != libnetwork.ErrInvalidID {
if _, ok := err.(libnetwork.ErrInvalidID); !ok {
t.Fatalf("NetworkByID() failed with unexpected error: %v", err)
}
@@ -628,7 +631,7 @@ func TestControllerQuery(t *testing.T) {
if err == nil {
t.Fatalf("Unexpected success for NetworkByID(): %v", g)
}
if err != libnetwork.ErrNoSuchNetwork {
if _, ok := err.(libnetwork.ErrNoSuchNetwork); !ok {
t.Fatalf("NetworkByID() failed with unexpected error: %v", err)
}
@@ -694,15 +697,15 @@ func TestNetworkQuery(t *testing.T) {
if err == nil {
t.Fatalf("EndpointByName() succeeded with invalid target name")
}
if err != libnetwork.ErrInvalidName {
t.Fatalf("EndpointByName() failed with unexpected error: %v", err)
if _, ok := err.(libnetwork.ErrInvalidName); !ok {
t.Fatalf("Expected EndpointByName() to fail with ErrInvalidName error. Got: %v", err)
}
e, err = net1.EndpointByName("IamNotAnEndpoint")
if err == nil {
t.Fatalf("EndpointByName() succeeded with unknown target name")
}
if err != libnetwork.ErrNoSuchEndpoint {
if _, ok := err.(libnetwork.ErrNoSuchEndpoint); !ok {
t.Fatal(err)
}
if e != nil {
@@ -721,13 +724,42 @@ func TestNetworkQuery(t *testing.T) {
if err == nil {
t.Fatalf("EndpointByID() succeeded with invalid target id")
}
if err != libnetwork.ErrInvalidID {
if _, ok := err.(libnetwork.ErrInvalidID); !ok {
t.Fatalf("EndpointByID() failed with unexpected error: %v", err)
}
}
const containerID = "valid_container"
func checkSandbox(t *testing.T, info libnetwork.EndpointInfo) {
origns, err := netns.Get()
if err != nil {
t.Fatalf("Could not get the current netns: %v", err)
}
defer origns.Close()
key := info.SandboxKey()
f, err := os.OpenFile(key, os.O_RDONLY, 0)
if err != nil {
t.Fatalf("Failed to open network namespace path %q: %v", key, err)
}
defer f.Close()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
nsFD := f.Fd()
if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
t.Fatalf("Setting to the namespace pointed to by the sandbox %s failed: %v", key, err)
}
defer netns.Set(origns)
_, err = netlink.LinkByName("eth0")
if err != nil {
t.Fatalf("Could not find the interface eth0 inside the sandbox: %v", err)
}
}
func TestEndpointJoin(t *testing.T) {
if !netutils.IsRunningInContainer() {
defer netutils.SetupTestNetNS(t)()
@@ -784,6 +816,17 @@ func TestEndpointJoin(t *testing.T) {
if info.SandboxKey() == "" {
t.Fatalf("Expected an non-empty sandbox key for a joined endpoint. Instead found a empty sandbox key")
}
// Attempt retrieval of endpoint interfaces statistics
stats, err := ep.Statistics()
if err != nil {
t.Fatal(err)
}
if _, ok := stats["eth0"]; !ok {
t.Fatalf("Did not find eth0 statistics")
}
checkSandbox(t, info)
}
func TestEndpointJoinInvalidContainerId(t *testing.T) {
@@ -890,7 +933,7 @@ func TestEndpointMultipleJoins(t *testing.T) {
t.Fatal("Expected to fail multiple joins for the same endpoint")
}
if err != libnetwork.ErrInvalidJoin {
if _, ok := err.(libnetwork.ErrInvalidJoin); !ok {
t.Fatalf("Failed for unexpected reason: %v", err)
}
}
@@ -916,7 +959,7 @@ func TestEndpointInvalidLeave(t *testing.T) {
}
if _, ok := err.(libnetwork.InvalidContainerIDError); !ok {
if err != libnetwork.ErrNoContainer {
if _, ok := err.(libnetwork.ErrNoContainer); !ok {
t.Fatalf("Failed for unexpected reason: %v", err)
}
}
@@ -1085,6 +1128,74 @@ func TestEnableIPv6(t *testing.T) {
}
}
func TestResolvConfHost(t *testing.T) {
if !netutils.IsRunningInContainer() {
defer netutils.SetupTestNetNS(t)()
}
tmpResolvConf := []byte("search localhost.net\nnameserver 127.0.0.1\nnameserver 2001:4860:4860::8888")
//take a copy of resolv.conf for restoring after test completes
resolvConfSystem, err := ioutil.ReadFile("/etc/resolv.conf")
if err != nil {
t.Fatal(err)
}
//cleanup
defer func() {
if err := ioutil.WriteFile("/etc/resolv.conf", resolvConfSystem, 0644); err != nil {
t.Fatal(err)
}
}()
n, err := createTestNetwork("host", "testnetwork", options.Generic{}, options.Generic{})
if err != nil {
t.Fatal(err)
}
ep1, err := n.CreateEndpoint("ep1", nil)
if err != nil {
t.Fatal(err)
}
if err := ioutil.WriteFile("/etc/resolv.conf", tmpResolvConf, 0644); err != nil {
t.Fatal(err)
}
resolvConfPath := "/tmp/libnetwork_test/resolv.conf"
defer os.Remove(resolvConfPath)
_, err = ep1.Join(containerID,
libnetwork.JoinOptionResolvConfPath(resolvConfPath))
if err != nil {
t.Fatal(err)
}
defer func() {
err = ep1.Leave(containerID)
if err != nil {
t.Fatal(err)
}
}()
finfo, err := os.Stat(resolvConfPath)
if err != nil {
t.Fatal(err)
}
fmode := (os.FileMode)(0644)
if finfo.Mode() != fmode {
t.Fatalf("Expected file mode %s, got %s", fmode.String(), finfo.Mode().String())
}
content, err := ioutil.ReadFile(resolvConfPath)
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(content, tmpResolvConf) {
t.Fatalf("Expected %s, Got %s", string(tmpResolvConf), string(content))
}
}
func TestResolvConf(t *testing.T) {
if !netutils.IsRunningInContainer() {
defer netutils.SetupTestNetNS(t)()
@@ -1137,6 +1248,16 @@ func TestResolvConf(t *testing.T) {
}
}()
finfo, err := os.Stat(resolvConfPath)
if err != nil {
t.Fatal(err)
}
fmode := (os.FileMode)(0644)
if finfo.Mode() != fmode {
t.Fatalf("Expected file mode %s, got %s", fmode.String(), finfo.Mode().String())
}
content, err := ioutil.ReadFile(resolvConfPath)
if err != nil {
t.Fatal(err)
@@ -1265,6 +1386,10 @@ func TestValidRemoteDriver(t *testing.T) {
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
fmt.Fprintf(w, `{"Implements": ["%s"]}`, driverapi.NetworkPluginEndpointType)
})
mux.HandleFunc(fmt.Sprintf("/%s.CreateNetwork", driverapi.NetworkPluginEndpointType), func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/vnd.docker.plugins.v1+json")
fmt.Fprintf(w, "null")
})
if err := os.MkdirAll("/usr/share/docker/plugins", 0755); err != nil {
t.Fatal(err)
@@ -1286,7 +1411,7 @@ func TestValidRemoteDriver(t *testing.T) {
_, err = controller.NewNetwork("valid-network-driver", "dummy",
libnetwork.NetworkOptionGeneric(getEmptyGenericOption()))
if err != nil && err != driverapi.ErrNotImplemented {
if err != nil {
t.Fatal(err)
}
}
@@ -1360,8 +1485,10 @@ func parallelJoin(t *testing.T, ep libnetwork.Endpoint, thrNumber int) {
_, err := ep.Join("racing_container")
runtime.LockOSThread()
if err != nil {
if err != libnetwork.ErrNoContainer && err != libnetwork.ErrInvalidJoin {
t.Fatal(err)
if _, ok := err.(libnetwork.ErrNoContainer); !ok {
if _, ok := err.(libnetwork.ErrInvalidJoin); !ok {
t.Fatal(err)
}
}
debugf("JE%d(%v).", thrNumber, err)
}
@@ -1373,8 +1500,10 @@ func parallelLeave(t *testing.T, ep libnetwork.Endpoint, thrNumber int) {
err := ep.Leave("racing_container")
runtime.LockOSThread()
if err != nil {
if err != libnetwork.ErrNoContainer && err != libnetwork.ErrInvalidJoin {
t.Fatal(err)
if _, ok := err.(libnetwork.ErrNoContainer); !ok {
if _, ok := err.(libnetwork.ErrInvalidJoin); !ok {
t.Fatal(err)
}
}
debugf("LE%d(%v).", thrNumber, err)
}
-175
View File
@@ -3,14 +3,12 @@
package netutils
import (
"bytes"
"crypto/rand"
"encoding/hex"
"errors"
"fmt"
"io"
"net"
"strings"
"github.com/vishvananda/netlink"
)
@@ -26,144 +24,6 @@ var (
networkGetRoutesFct = netlink.RouteList
)
// ErrInvalidProtocolBinding is returned when the port binding protocol is not valid.
type ErrInvalidProtocolBinding string
func (ipb ErrInvalidProtocolBinding) Error() string {
return fmt.Sprintf("invalid transport protocol: %s", string(ipb))
}
// TransportPort represent a local Layer 4 endpoint
type TransportPort struct {
Proto Protocol
Port uint16
}
// GetCopy returns a copy of this TransportPort structure instance
func (t *TransportPort) GetCopy() TransportPort {
return TransportPort{Proto: t.Proto, Port: t.Port}
}
// PortBinding represent a port binding between the container an the host
type PortBinding struct {
Proto Protocol
IP net.IP
Port uint16
HostIP net.IP
HostPort uint16
}
// HostAddr returns the host side transport address
func (p PortBinding) HostAddr() (net.Addr, error) {
switch p.Proto {
case UDP:
return &net.UDPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
case TCP:
return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
default:
return nil, ErrInvalidProtocolBinding(p.Proto.String())
}
}
// ContainerAddr returns the container side transport address
func (p PortBinding) ContainerAddr() (net.Addr, error) {
switch p.Proto {
case UDP:
return &net.UDPAddr{IP: p.IP, Port: int(p.Port)}, nil
case TCP:
return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil
default:
return nil, ErrInvalidProtocolBinding(p.Proto.String())
}
}
// GetCopy returns a copy of this PortBinding structure instance
func (p *PortBinding) GetCopy() PortBinding {
return PortBinding{
Proto: p.Proto,
IP: GetIPCopy(p.IP),
Port: p.Port,
HostIP: GetIPCopy(p.HostIP),
HostPort: p.HostPort,
}
}
// Equal checks if this instance of PortBinding is equal to the passed one
func (p *PortBinding) Equal(o *PortBinding) bool {
if p == o {
return true
}
if o == nil {
return false
}
if p.Proto != o.Proto || p.Port != o.Port || p.HostPort != o.HostPort {
return false
}
if p.IP != nil {
if !p.IP.Equal(o.IP) {
return false
}
} else {
if o.IP != nil {
return false
}
}
if p.HostIP != nil {
if !p.HostIP.Equal(o.HostIP) {
return false
}
} else {
if o.HostIP != nil {
return false
}
}
return true
}
const (
// ICMP is for the ICMP ip protocol
ICMP = 1
// TCP is for the TCP ip protocol
TCP = 6
// UDP is for the UDP ip protocol
UDP = 17
)
// Protocol represents a IP protocol number
type Protocol uint8
func (p Protocol) String() string {
switch p {
case ICMP:
return "icmp"
case TCP:
return "tcp"
case UDP:
return "udp"
default:
return fmt.Sprintf("%d", p)
}
}
// ParseProtocol returns the respective Protocol type for the passed string
func ParseProtocol(s string) Protocol {
switch strings.ToLower(s) {
case "icmp":
return ICMP
case "udp":
return UDP
case "tcp":
return TCP
default:
return 0
}
}
// CheckNameserverOverlaps checks whether the passed network overlaps with any of the nameservers
func CheckNameserverOverlaps(nameservers []string, toCheck *net.IPNet) error {
if len(nameservers) > 0 {
@@ -287,38 +147,3 @@ func GenerateRandomName(prefix string, size int) (string, error) {
}
return prefix + hex.EncodeToString(id)[:size], nil
}
// GetMacCopy returns a copy of the passed MAC address
func GetMacCopy(from net.HardwareAddr) net.HardwareAddr {
to := make(net.HardwareAddr, len(from))
copy(to, from)
return to
}
// GetIPCopy returns a copy of the passed IP address
func GetIPCopy(from net.IP) net.IP {
to := make(net.IP, len(from))
copy(to, from)
return to
}
// GetIPNetCopy returns a copy of the passed IP Network
func GetIPNetCopy(from *net.IPNet) *net.IPNet {
if from == nil {
return nil
}
bm := make(net.IPMask, len(from.Mask))
copy(bm, from.Mask)
return &net.IPNet{IP: GetIPCopy(from.IP), Mask: bm}
}
// CompareIPNet returns equal if the two IP Networks are equal
func CompareIPNet(a, b *net.IPNet) bool {
if a == b {
return true
}
if a == nil || b == nil {
return false
}
return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask)
}
-132
View File
@@ -209,135 +209,3 @@ func TestUtilGenerateRandomMAC(t *testing.T) {
t.Fatalf("mac1 %s should not equal mac2 %s", mac1, mac2)
}
}
func TestCompareIPNet(t *testing.T) {
if CompareIPNet(nil, nil) == false {
t.Fatalf("Failed to detect two nil net.IPNets are equal")
}
_, net1, _ := net.ParseCIDR("192.168.30.22/24")
if CompareIPNet(net1, net1) == false {
t.Fatalf("Failed to detect same net.IPNet pointers equality")
}
_, net2, _ := net.ParseCIDR("192.168.30.22/24")
if CompareIPNet(net1, net2) == false {
t.Fatalf("Failed to detect same net.IPNet object equality")
}
_, net3, _ := net.ParseCIDR("192.168.30.33/24")
if CompareIPNet(net1, net3) == false {
t.Fatalf("Failed to detect semantically equivalent net.IPNets")
}
_, net3, _ = net.ParseCIDR("192.168.31.33/24")
if CompareIPNet(net2, net3) == true {
t.Fatalf("Failed to detect different net.IPNets")
}
}
func TestIPCopyFunctions(t *testing.T) {
ip := net.ParseIP("172.28.30.134")
cp := GetIPCopy(ip)
if !ip.Equal(cp) {
t.Fatalf("Failed to return a copy of net.IP")
}
if &ip == &cp {
t.Fatalf("Failed to return a true copy of net.IP")
}
}
func TestNetIPCopyFunctions(t *testing.T) {
_, net, _ := net.ParseCIDR("192.168.30.23/24")
cp := GetIPNetCopy(net)
if CompareIPNet(net, cp) == false {
t.Fatalf("Failed to return a copy of net.IPNet")
}
if net == cp {
t.Fatalf("Failed to return a true copy of net.IPNet")
}
}
func TestPortBindingEqual(t *testing.T) {
pb1 := &PortBinding{
Proto: TCP,
IP: net.ParseIP("172.17.0.1"),
Port: 80,
HostIP: net.ParseIP("192.168.100.1"),
HostPort: 8080,
}
pb2 := &PortBinding{
Proto: UDP,
IP: net.ParseIP("172.17.0.1"),
Port: 22,
HostIP: net.ParseIP("192.168.100.1"),
HostPort: 2222,
}
if !pb1.Equal(pb1) {
t.Fatalf("PortBinding.Equal() returned false negative")
}
if pb1.Equal(nil) {
t.Fatalf("PortBinding.Equal() returned false negative")
}
if pb1.Equal(pb2) {
t.Fatalf("PortBinding.Equal() returned false positive")
}
if pb1.Equal(pb2) != pb2.Equal(pb1) {
t.Fatalf("PortBinding.Equal() failed commutative check")
}
}
func TestPortBindingGetCopy(t *testing.T) {
pb := &PortBinding{
Proto: TCP,
IP: net.ParseIP("172.17.0.1"),
Port: 80,
HostIP: net.ParseIP("192.168.100.1"),
HostPort: 8080,
}
cp := pb.GetCopy()
if !pb.Equal(&cp) {
t.Fatalf("Failed to return a copy of PortBinding")
}
if pb == &cp {
t.Fatalf("Failed to return a true copy of PortBinding")
}
}
func TestPortBindingContainerAddr(t *testing.T) {
pb := PortBinding{
Proto: TCP,
IP: net.ParseIP("172.17.0.1"),
Port: 80,
HostIP: net.ParseIP("192.168.100.1"),
HostPort: 8080,
}
container, err := pb.ContainerAddr()
if err != nil {
t.Fatal(err)
}
switch netAddr := container.(type) {
case *net.TCPAddr:
if !pb.IP.Equal(netAddr.IP) {
t.Fatalf("PortBinding.ContainerAddr() Failed to return a ContainerAddr")
}
if int(pb.Port) != netAddr.Port {
t.Fatalf("PortBinding.ContainerAddr() Failed to return a ContainerAddr")
}
case *net.UDPAddr:
t.Fatalf("PortBinding.ContainerAddr() Failed to check correct proto")
}
}
+14 -5
View File
@@ -60,14 +60,23 @@ type network struct {
}
func (n *network) Name() string {
n.Lock()
defer n.Unlock()
return n.name
}
func (n *network) ID() string {
n.Lock()
defer n.Unlock()
return string(n.id)
}
func (n *network) Type() string {
n.Lock()
defer n.Unlock()
if n.driver == nil {
return ""
}
@@ -133,7 +142,7 @@ func (n *network) Delete() error {
func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoint, error) {
if name == "" {
return nil, ErrInvalidName
return nil, ErrInvalidName(name)
}
ep := &endpoint{name: name, iFaces: []*endpointInterface{}, generic: make(map[string]interface{})}
ep.id = types.UUID(stringid.GenerateRandomID())
@@ -173,7 +182,7 @@ func (n *network) WalkEndpoints(walker EndpointWalker) {
func (n *network) EndpointByName(name string) (Endpoint, error) {
if name == "" {
return nil, ErrInvalidName
return nil, ErrInvalidName(name)
}
var e Endpoint
@@ -188,7 +197,7 @@ func (n *network) EndpointByName(name string) (Endpoint, error) {
n.WalkEndpoints(s)
if e == nil {
return nil, ErrNoSuchEndpoint
return nil, ErrNoSuchEndpoint(name)
}
return e, nil
@@ -196,12 +205,12 @@ func (n *network) EndpointByName(name string) (Endpoint, error) {
func (n *network) EndpointByID(id string) (Endpoint, error) {
if id == "" {
return nil, ErrInvalidID
return nil, ErrInvalidID(id)
}
n.Lock()
defer n.Unlock()
if e, ok := n.endpoints[types.UUID(id)]; ok {
return e, nil
}
return nil, ErrNoSuchEndpoint
return nil, ErrNoSuchEndpoint(id)
}
-3
View File
@@ -7,8 +7,6 @@ import (
"net"
"os"
"sync"
"github.com/Sirupsen/logrus"
)
const (
@@ -94,7 +92,6 @@ func Get() *PortAllocator {
func newInstance() *PortAllocator {
start, end, err := getDynamicPortRange()
if err != nil {
logrus.Warn(err)
start, end = DefaultPortRangeStart, DefaultPortRangeEnd
}
return &PortAllocator{
+9 -9
View File
@@ -84,6 +84,8 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, usePr
if useProxy {
m.userlandProxy = newProxy(proto, hostIP, allocatedHostPort, container.(*net.TCPAddr).IP, container.(*net.TCPAddr).Port)
} else {
m.userlandProxy = newDummyProxy(proto, hostIP, allocatedHostPort)
}
case *net.UDPAddr:
proto = "udp"
@@ -99,6 +101,8 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, usePr
if useProxy {
m.userlandProxy = newProxy(proto, hostIP, allocatedHostPort, container.(*net.UDPAddr).IP, container.(*net.UDPAddr).Port)
} else {
m.userlandProxy = newDummyProxy(proto, hostIP, allocatedHostPort)
}
default:
return nil, ErrUnknownBackendAddressType
@@ -123,9 +127,7 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, usePr
cleanup := func() error {
// need to undo the iptables rules before we return
if m.userlandProxy != nil {
m.userlandProxy.Stop()
}
m.userlandProxy.Stop()
pm.forward(iptables.Delete, m.proto, hostIP, allocatedHostPort, containerIP.String(), containerPort)
if err := pm.Allocator.ReleasePort(hostIP, m.proto, allocatedHostPort); err != nil {
return err
@@ -134,13 +136,11 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int, usePr
return nil
}
if m.userlandProxy != nil {
if err := m.userlandProxy.Start(); err != nil {
if err := cleanup(); err != nil {
return nil, fmt.Errorf("Error during port allocation cleanup: %v", err)
}
return nil, err
if err := m.userlandProxy.Start(); err != nil {
if err := cleanup(); err != nil {
return nil, fmt.Errorf("Error during port allocation cleanup: %v", err)
}
return nil, err
}
pm.currentMappings[key] = m
+81 -37
View File
@@ -2,20 +2,16 @@ package portmapper
import (
"net"
"os"
"strings"
"testing"
"time"
"github.com/docker/docker/pkg/reexec"
"github.com/docker/libnetwork/iptables"
"github.com/docker/libnetwork/netutils"
_ "github.com/docker/libnetwork/netutils"
)
func TestMain(m *testing.M) {
if reexec.Init() {
return
}
os.Exit(m.Run())
func init() {
// override this func to mock out the proxy server
newProxy = newMockProxyCommand
}
func TestSetIptablesChain(t *testing.T) {
@@ -37,7 +33,6 @@ func TestSetIptablesChain(t *testing.T) {
}
func TestMapTCPPorts(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
pm := New()
dstIP1 := net.ParseIP("192.168.0.1")
dstIP2 := net.ParseIP("192.168.0.2")
@@ -117,7 +112,6 @@ func TestGetUDPIPAndPort(t *testing.T) {
}
func TestMapUDPPorts(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
pm := New()
dstIP1 := net.ParseIP("192.168.0.1")
dstIP2 := net.ParseIP("192.168.0.2")
@@ -164,11 +158,6 @@ func TestMapUDPPorts(t *testing.T) {
}
func TestMapAllPortsSingleInterface(t *testing.T) {
newProxy = newMockProxyCommand
defer func() {
newProxy = newProxyCommand
}()
defer netutils.SetupTestNetNS(t)()
pm := New()
dstIP1 := net.ParseIP("0.0.0.0")
srcAddr1 := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")}
@@ -177,6 +166,12 @@ func TestMapAllPortsSingleInterface(t *testing.T) {
var host net.Addr
var err error
defer func() {
for _, val := range hosts {
pm.Unmap(val)
}
}()
for i := 0; i < 10; i++ {
start, end := pm.Allocator.Begin, pm.Allocator.End
for i := start; i < end; i++ {
@@ -201,27 +196,76 @@ func TestMapAllPortsSingleInterface(t *testing.T) {
}
}
func TestExecProxy(t *testing.T) {
defer netutils.SetupTestNetNS(t)()
args := []string{
userlandProxyCommandName,
"-proto", "tcp",
"-host-ip", "0.0.0.0",
"-host-port", "9999",
"-container-ip", "172.168.1.1",
"-container-port", "8888",
}
os.Args = args
doneChan := make(chan bool)
go func() {
execProxy()
doneChan <- true
}()
func TestMapTCPDummyListen(t *testing.T) {
pm := New()
dstIP := net.ParseIP("0.0.0.0")
dstAddr := &net.TCPAddr{IP: dstIP, Port: 80}
select {
case <-doneChan:
t.Fatal("execProxy is not supposed to exit")
case <-time.After(3 * time.Second):
return
// no-op for dummy
srcAddr := &net.TCPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")}
addrEqual := func(addr1, addr2 net.Addr) bool {
return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String())
}
if host, err := pm.Map(srcAddr, dstIP, 80, false); err != nil {
t.Fatalf("Failed to allocate port: %s", err)
} else if !addrEqual(dstAddr, host) {
t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s",
dstAddr.String(), dstAddr.Network(), host.String(), host.Network())
}
if _, err := net.Listen("tcp", "0.0.0.0:80"); err == nil {
t.Fatal("Listen on mapped port without proxy should fail")
} else {
if !strings.Contains(err.Error(), "address already in use") {
t.Fatalf("Error should be about address already in use, got %v", err)
}
}
if _, err := net.Listen("tcp", "0.0.0.0:81"); err != nil {
t.Fatal(err)
}
if host, err := pm.Map(srcAddr, dstIP, 81, false); err == nil {
t.Fatalf("Bound port shouldn't be allocated, but it was on: %v", host)
} else {
if !strings.Contains(err.Error(), "address already in use") {
t.Fatalf("Error should be about address already in use, got %v", err)
}
}
}
func TestMapUDPDummyListen(t *testing.T) {
pm := New()
dstIP := net.ParseIP("0.0.0.0")
dstAddr := &net.UDPAddr{IP: dstIP, Port: 80}
// no-op for dummy
srcAddr := &net.UDPAddr{Port: 1080, IP: net.ParseIP("172.16.0.1")}
addrEqual := func(addr1, addr2 net.Addr) bool {
return (addr1.Network() == addr2.Network()) && (addr1.String() == addr2.String())
}
if host, err := pm.Map(srcAddr, dstIP, 80, false); err != nil {
t.Fatalf("Failed to allocate port: %s", err)
} else if !addrEqual(dstAddr, host) {
t.Fatalf("Incorrect mapping result: expected %s:%s, got %s:%s",
dstAddr.String(), dstAddr.Network(), host.String(), host.Network())
}
if _, err := net.ListenUDP("udp", &net.UDPAddr{IP: dstIP, Port: 80}); err == nil {
t.Fatal("Listen on mapped port without proxy should fail")
} else {
if !strings.Contains(err.Error(), "address already in use") {
t.Fatalf("Error should be about address already in use, got %v", err)
}
}
if _, err := net.ListenUDP("udp", &net.UDPAddr{IP: dstIP, Port: 81}); err != nil {
t.Fatal(err)
}
if host, err := pm.Map(srcAddr, dstIP, 81, false); err == nil {
t.Fatalf("Bound port shouldn't be allocated, but it was on: %v", host)
} else {
if !strings.Contains(err.Error(), "address already in use") {
t.Fatalf("Error should be about address already in use, got %v", err)
}
}
}
+48
View File
@@ -3,6 +3,7 @@ package portmapper
import (
"flag"
"fmt"
"io"
"io/ioutil"
"log"
"net"
@@ -159,3 +160,50 @@ func (p *proxyCommand) Stop() error {
}
return nil
}
// dummyProxy just listen on some port, it is needed to prevent accidental
// port allocations on bound port, because without userland proxy we using
// iptables rules and not net.Listen
type dummyProxy struct {
listener io.Closer
addr net.Addr
}
func newDummyProxy(proto string, hostIP net.IP, hostPort int) userlandProxy {
switch proto {
case "tcp":
addr := &net.TCPAddr{IP: hostIP, Port: hostPort}
return &dummyProxy{addr: addr}
case "udp":
addr := &net.UDPAddr{IP: hostIP, Port: hostPort}
return &dummyProxy{addr: addr}
}
return nil
}
func (p *dummyProxy) Start() error {
switch addr := p.addr.(type) {
case *net.TCPAddr:
l, err := net.ListenTCP("tcp", addr)
if err != nil {
return err
}
p.listener = l
case *net.UDPAddr:
l, err := net.ListenUDP("udp", addr)
if err != nil {
return err
}
p.listener = l
default:
return fmt.Errorf("Unknown addr type: %T", p.addr)
}
return nil
}
func (p *dummyProxy) Stop() error {
if p.listener != nil {
return p.listener.Close()
}
return nil
}
+146 -27
View File
@@ -4,24 +4,41 @@ import (
"fmt"
"net"
"os"
"os/exec"
"runtime"
"sync"
"syscall"
"time"
log "github.com/Sirupsen/logrus"
"github.com/docker/docker/pkg/reexec"
"github.com/vishvananda/netlink"
"github.com/vishvananda/netns"
)
const prefix = "/var/run/docker/netns"
var once sync.Once
var (
once sync.Once
garbagePathMap = make(map[string]bool)
gpmLock sync.Mutex
gpmWg sync.WaitGroup
gpmCleanupPeriod = 60 * time.Second
gpmChan = make(chan chan struct{})
)
// The networkNamespace type is the linux implementation of the Sandbox
// interface. It represents a linux network namespace, and moves an interface
// into it when called on method AddInterface or sets the gateway etc.
type networkNamespace struct {
path string
sinfo *Info
path string
sinfo *Info
nextIfIndex int
sync.Mutex
}
func init() {
reexec.Register("netns-create", reexecCreateNamespace)
}
func createBasePath() {
@@ -29,6 +46,78 @@ func createBasePath() {
if err != nil && !os.IsExist(err) {
panic("Could not create net namespace path directory")
}
// Start the garbage collection go routine
go removeUnusedPaths()
}
func removeUnusedPaths() {
gpmLock.Lock()
period := gpmCleanupPeriod
gpmLock.Unlock()
ticker := time.NewTicker(period)
for {
var (
gc chan struct{}
gcOk bool
)
select {
case <-ticker.C:
case gc, gcOk = <-gpmChan:
}
gpmLock.Lock()
pathList := make([]string, 0, len(garbagePathMap))
for path := range garbagePathMap {
pathList = append(pathList, path)
}
garbagePathMap = make(map[string]bool)
gpmWg.Add(1)
gpmLock.Unlock()
for _, path := range pathList {
os.Remove(path)
}
gpmWg.Done()
if gcOk {
close(gc)
}
}
}
func addToGarbagePaths(path string) {
gpmLock.Lock()
garbagePathMap[path] = true
gpmLock.Unlock()
}
func removeFromGarbagePaths(path string) {
gpmLock.Lock()
delete(garbagePathMap, path)
gpmLock.Unlock()
}
// GC triggers garbage collection of namespace path right away
// and waits for it.
func GC() {
gpmLock.Lock()
if len(garbagePathMap) == 0 {
// No need for GC if map is empty
gpmLock.Unlock()
return
}
gpmLock.Unlock()
// if content exists in the garbage paths
// we can trigger GC to run, providing a
// channel to be notified on completion
waitGC := make(chan struct{})
gpmChan <- waitGC
// wait for GC completion
<-waitGC
}
// GenerateKey generates a sandbox key based on the passed
@@ -53,6 +142,20 @@ func NewSandbox(key string, osCreate bool) (Sandbox, error) {
return &networkNamespace{path: key, sinfo: info}, nil
}
func reexecCreateNamespace() {
if len(os.Args) < 2 {
log.Fatal("no namespace path provided")
}
if err := syscall.Mount("/proc/self/ns/net", os.Args[1], "bind", syscall.MS_BIND, ""); err != nil {
log.Fatal(err)
}
if err := loopbackUp(); err != nil {
log.Fatal(err)
}
}
func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
@@ -67,23 +170,18 @@ func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
return nil, err
}
if osCreate {
defer netns.Set(origns)
newns, err := netns.New()
if err != nil {
return nil, err
}
defer newns.Close()
if err := loopbackUp(); err != nil {
return nil, err
}
cmd := &exec.Cmd{
Path: reexec.Self(),
Args: append([]string{"netns-create"}, path),
Stdout: os.Stdout,
Stderr: os.Stderr,
}
procNet := fmt.Sprintf("/proc/%d/task/%d/ns/net", os.Getpid(), syscall.Gettid())
if err := syscall.Mount(procNet, path, "bind", syscall.MS_BIND, ""); err != nil {
return nil, err
if osCreate {
cmd.SysProcAttr = &syscall.SysProcAttr{}
cmd.SysProcAttr.Cloneflags = syscall.CLONE_NEWNET
}
if err := cmd.Run(); err != nil {
return nil, fmt.Errorf("namespace creation reexec command failed: %v", err)
}
interfaces := []*Interface{}
@@ -91,10 +189,9 @@ func createNetworkNamespace(path string, osCreate bool) (*Info, error) {
return info, nil
}
func cleanupNamespaceFile(path string) {
func unmountNamespaceFile(path string) {
if _, err := os.Stat(path); err == nil {
n := &networkNamespace{path: path}
n.Destroy()
syscall.Unmount(path, syscall.MNT_DETACH)
}
}
@@ -102,11 +199,20 @@ func createNamespaceFile(path string) (err error) {
var f *os.File
once.Do(createBasePath)
// cleanup namespace file if it already exists because of a previous ungraceful exit.
cleanupNamespaceFile(path)
// Remove it from garbage collection list if present
removeFromGarbagePaths(path)
// If the path is there unmount it first
unmountNamespaceFile(path)
// wait for garbage collection to complete if it is in progress
// before trying to create the file.
gpmWg.Wait()
if f, err = os.Create(path); err == nil {
f.Close()
}
return err
}
@@ -167,6 +273,12 @@ func (n *networkNamespace) RemoveInterface(i *Interface) error {
}
func (n *networkNamespace) AddInterface(i *Interface) error {
n.Lock()
i.DstName = fmt.Sprintf("%s%d", i.DstName, n.nextIfIndex)
n.nextIfIndex++
path := n.path
n.Unlock()
runtime.LockOSThread()
defer runtime.UnlockOSThread()
@@ -176,9 +288,9 @@ func (n *networkNamespace) AddInterface(i *Interface) error {
}
defer origns.Close()
f, err := os.OpenFile(n.path, os.O_RDONLY, 0)
f, err := os.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
return fmt.Errorf("failed get network namespace %q: %v", n.path, err)
return fmt.Errorf("failed get network namespace %q: %v", path, err)
}
defer f.Close()
@@ -214,7 +326,12 @@ func (n *networkNamespace) AddInterface(i *Interface) error {
return err
}
i.sandboxKey = path
n.Lock()
n.sinfo.Interfaces = append(n.sinfo.Interfaces, i)
n.Unlock()
return nil
}
@@ -259,5 +376,7 @@ func (n *networkNamespace) Destroy() error {
return err
}
return os.Remove(n.path)
// Stash it into the garbage collection list
addToGarbagePaths(n.path)
return nil
}
+114 -9
View File
@@ -1,9 +1,15 @@
package sandbox
import (
"fmt"
"net"
"os"
"os/exec"
"regexp"
"runtime"
"github.com/docker/libnetwork/netutils"
"github.com/docker/libnetwork/types"
"github.com/vishvananda/netns"
)
// Sandbox represents a network sandbox, identified by a specific key. It
@@ -20,7 +26,9 @@ type Sandbox interface {
// Add an existing Interface to this sandbox. The operation will rename
// from the Interface SrcName to DstName as it moves, and reconfigure the
// interface according to the specified settings.
// interface according to the specified settings. The caller is expected
// to only provide a prefix for DstName. The AddInterface api will auto-generate
// an appropriate suffix for the DstName to disambiguate.
AddInterface(*Interface) error
// Remove an interface from the sandbox by renamin to original name
@@ -62,7 +70,9 @@ type Interface struct {
SrcName string
// The name that will be assigned to the interface once moves inside a
// network namespace.
// network namespace. When the caller passes in a DstName, it is only
// expected to pass a prefix. The name will modified with an appropriately
// auto-generated suffix.
DstName string
// IPv4 address for the interface.
@@ -70,6 +80,9 @@ type Interface struct {
// IPv6 address for the interface.
AddressIPv6 *net.IPNet
// Parent sandbox's key
sandboxKey string
}
// GetCopy returns a copy of this Interface structure
@@ -77,8 +90,8 @@ func (i *Interface) GetCopy() *Interface {
return &Interface{
SrcName: i.SrcName,
DstName: i.DstName,
Address: netutils.GetIPNetCopy(i.Address),
AddressIPv6: netutils.GetIPNetCopy(i.AddressIPv6),
Address: types.GetIPNetCopy(i.Address),
AddressIPv6: types.GetIPNetCopy(i.AddressIPv6),
}
}
@@ -96,11 +109,11 @@ func (i *Interface) Equal(o *Interface) bool {
return false
}
if !netutils.CompareIPNet(i.Address, o.Address) {
if !types.CompareIPNet(i.Address, o.Address) {
return false
}
if !netutils.CompareIPNet(i.AddressIPv6, o.AddressIPv6) {
if !types.CompareIPNet(i.AddressIPv6, o.AddressIPv6) {
return false
}
@@ -113,8 +126,8 @@ func (s *Info) GetCopy() *Info {
for i, iface := range s.Interfaces {
list[i] = iface.GetCopy()
}
gw := netutils.GetIPCopy(s.Gateway)
gw6 := netutils.GetIPCopy(s.GatewayIPv6)
gw := types.GetIPCopy(s.Gateway)
gw6 := types.GetIPCopy(s.GatewayIPv6)
return &Info{Interfaces: list, Gateway: gw, GatewayIPv6: gw6}
}
@@ -153,3 +166,95 @@ func (s *Info) Equal(o *Info) bool {
return true
}
func nsInvoke(path string, inNsfunc func(callerFD int) error) error {
runtime.LockOSThread()
defer runtime.UnlockOSThread()
origns, err := netns.Get()
if err != nil {
return err
}
defer origns.Close()
f, err := os.OpenFile(path, os.O_RDONLY, 0)
if err != nil {
return fmt.Errorf("failed get network namespace %q: %v", path, err)
}
defer f.Close()
nsFD := f.Fd()
if err = netns.Set(netns.NsHandle(nsFD)); err != nil {
return err
}
defer netns.Set(origns)
// Invoked after the namespace switch.
return inNsfunc(int(origns))
}
// Statistics returns the statistics for this interface
func (i *Interface) Statistics() (*InterfaceStatistics, error) {
s := &InterfaceStatistics{}
err := nsInvoke(i.sandboxKey, func(callerFD int) error {
// For some reason ioutil.ReadFile(netStatsFile) reads the file in
// the default netns when this code is invoked from docker.
// Executing "cat <netStatsFile>" works as expected.
data, err := exec.Command("cat", netStatsFile).Output()
if err != nil {
return fmt.Errorf("failed to open %s: %v", netStatsFile, err)
}
return scanInterfaceStats(string(data), i.DstName, s)
})
if err != nil {
err = fmt.Errorf("failed to retrieve the statistics for %s in netns %s: %v", i.DstName, i.sandboxKey, err)
}
return s, err
}
// InterfaceStatistics represents the interface's statistics
type InterfaceStatistics struct {
RxBytes uint64
RxPackets uint64
RxErrors uint64
RxDropped uint64
TxBytes uint64
TxPackets uint64
TxErrors uint64
TxDropped uint64
}
func (is *InterfaceStatistics) String() string {
return fmt.Sprintf("\nRxBytes: %d, RxPackets: %d, RxErrors: %d, RxDropped: %d, TxBytes: %d, TxPackets: %d, TxErrors: %d, TxDropped: %d",
is.RxBytes, is.RxPackets, is.RxErrors, is.RxDropped, is.TxBytes, is.TxPackets, is.TxErrors, is.TxDropped)
}
// In older kernels (like the one in Centos 6.6 distro) sysctl does not have netns support. Therefore
// we cannot gather the statistics from /sys/class/net/<dev>/statistics/<counter> files. Per-netns stats
// are naturally found in /proc/net/dev in kernels which support netns (ifconfig relyes on that).
const (
netStatsFile = "/proc/net/dev"
base = "[ ]*%s:([ ]+[0-9]+){16}"
)
func scanInterfaceStats(data, ifName string, i *InterfaceStatistics) error {
var (
bktStr string
bkt uint64
)
regex := fmt.Sprintf(base, ifName)
re := regexp.MustCompile(regex)
line := re.FindString(data)
_, err := fmt.Sscanf(line, "%s %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
&bktStr, &i.RxBytes, &i.RxPackets, &i.RxErrors, &i.RxDropped, &bkt, &bkt, &bkt,
&bkt, &i.TxBytes, &i.TxPackets, &i.TxErrors, &i.TxDropped, &bkt, &bkt, &bkt, &bkt)
return err
}
+91 -11
View File
@@ -6,6 +6,7 @@ import (
"path/filepath"
"runtime"
"testing"
"time"
"github.com/docker/libnetwork/netutils"
"github.com/vishvananda/netlink"
@@ -15,6 +16,8 @@ import (
const (
vethName1 = "wierdlongname1"
vethName2 = "wierdlongname2"
vethName3 = "wierdlongname3"
vethName4 = "wierdlongname4"
sboxIfaceName = "containername"
)
@@ -29,6 +32,11 @@ func newKey(t *testing.T) (string, error) {
return "", err
}
// Set the rpmCleanupPeriod to be low to make the test run quicker
gpmLock.Lock()
gpmCleanupPeriod = 2 * time.Second
gpmLock.Unlock()
return name, nil
}
@@ -36,33 +44,59 @@ func newInfo(t *testing.T) (*Info, error) {
veth := &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{Name: vethName1, TxQLen: 0},
PeerName: vethName2}
err := netlink.LinkAdd(veth)
if err != nil {
if err := netlink.LinkAdd(veth); err != nil {
return nil, err
}
// Store the sandbox side pipe interface
// This is needed for cleanup on DeleteEndpoint()
intf := &Interface{}
intf.SrcName = vethName2
intf.DstName = sboxIfaceName
intf1 := &Interface{}
intf1.SrcName = vethName2
intf1.DstName = sboxIfaceName
ip4, addr, err := net.ParseCIDR("192.168.1.100/24")
if err != nil {
return nil, err
}
intf.Address = addr
intf.Address.IP = ip4
intf1.Address = addr
intf1.Address.IP = ip4
// ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48")
ip6, addrv6, err := net.ParseCIDR("fe80::2/64")
if err != nil {
return nil, err
}
intf.AddressIPv6 = addrv6
intf.AddressIPv6.IP = ip6
intf1.AddressIPv6 = addrv6
intf1.AddressIPv6.IP = ip6
sinfo := &Info{Interfaces: []*Interface{intf}}
veth = &netlink.Veth{
LinkAttrs: netlink.LinkAttrs{Name: vethName3, TxQLen: 0},
PeerName: vethName4}
if err := netlink.LinkAdd(veth); err != nil {
return nil, err
}
intf2 := &Interface{}
intf2.SrcName = vethName4
intf2.DstName = sboxIfaceName
ip4, addr, err = net.ParseCIDR("192.168.2.100/24")
if err != nil {
return nil, err
}
intf2.Address = addr
intf2.Address.IP = ip4
// ip6, addrv6, err := net.ParseCIDR("2001:DB8::ABCD/48")
ip6, addrv6, err = net.ParseCIDR("fe80::3/64")
if err != nil {
return nil, err
}
intf2.AddressIPv6 = addrv6
intf2.AddressIPv6.IP = ip6
sinfo := &Info{Interfaces: []*Interface{intf1, intf2}}
sinfo.Gateway = net.ParseIP("192.168.1.1")
// sinfo.GatewayIPv6 = net.ParseIP("2001:DB8::1")
sinfo.GatewayIPv6 = net.ParseIP("fe80::1")
@@ -97,9 +131,55 @@ func verifySandbox(t *testing.T, s Sandbox) {
}
defer netns.Set(origns)
_, err = netlink.LinkByName(sboxIfaceName)
_, err = netlink.LinkByName(sboxIfaceName + "0")
if err != nil {
t.Fatalf("Could not find the interface %s inside the sandbox: %v", sboxIfaceName,
err)
}
_, err = netlink.LinkByName(sboxIfaceName + "1")
if err != nil {
t.Fatalf("Could not find the interface %s inside the sandbox: %v", sboxIfaceName,
err)
}
}
func verifyCleanup(t *testing.T, s Sandbox, wait bool) {
if wait {
time.Sleep(time.Duration(gpmCleanupPeriod * 2))
}
if _, err := os.Stat(s.Key()); err == nil {
if wait {
t.Fatalf("The sandbox path %s is not getting cleaned up even after twice the cleanup period", s.Key())
} else {
t.Fatalf("The sandbox path %s is not cleaned up after running gc", s.Key())
}
}
}
func TestScanStatistics(t *testing.T) {
data :=
"Inter-| Receive | Transmit\n" +
" face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed\n" +
" eth0: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n" +
" wlan0: 7787685 11141 0 0 0 0 0 0 1681390 7220 0 0 0 0 0 0\n" +
" lo: 783782 1853 0 0 0 0 0 0 783782 1853 0 0 0 0 0 0\n" +
"lxcbr0: 0 0 0 0 0 0 0 0 9006 61 0 0 0 0 0 0\n"
i := &InterfaceStatistics{}
if err := scanInterfaceStats(data, "wlan0", i); err != nil {
t.Fatal(err)
}
if i.TxBytes != 1681390 || i.TxPackets != 7220 || i.RxBytes != 7787685 || i.RxPackets != 11141 {
t.Fatalf("Error scanning the statistics")
}
if err := scanInterfaceStats(data, "lxcbr0", i); err != nil {
t.Fatal(err)
}
if i.TxBytes != 9006 || i.TxPackets != 61 || i.RxBytes != 0 || i.RxPackets != 0 {
t.Fatalf("Error scanning the statistics")
}
}
+28
View File
@@ -2,9 +2,19 @@ package sandbox
import (
"net"
"os"
"testing"
"github.com/docker/docker/pkg/reexec"
)
func TestMain(m *testing.M) {
if reexec.Init() {
return
}
os.Exit(m.Run())
}
func TestSandboxCreate(t *testing.T) {
key, err := newKey(t)
if err != nil {
@@ -44,6 +54,7 @@ func TestSandboxCreate(t *testing.T) {
verifySandbox(t, s)
s.Destroy()
verifyCleanup(t, s, true)
}
func TestSandboxCreateTwice(t *testing.T) {
@@ -66,6 +77,23 @@ func TestSandboxCreateTwice(t *testing.T) {
s.Destroy()
}
func TestSandboxGC(t *testing.T) {
key, err := newKey(t)
if err != nil {
t.Fatalf("Failed to obtain a key: %v", err)
}
s, err := NewSandbox(key, true)
if err != nil {
t.Fatalf("Failed to create a new sandbox: %v", err)
}
s.Destroy()
GC()
verifyCleanup(t, s, false)
}
func TestInterfaceEqual(t *testing.T) {
list := getInterfaceList()
+340
View File
@@ -1,5 +1,345 @@
// Package types contains types that are common across libnetwork project
package types
import (
"bytes"
"fmt"
"net"
"strings"
)
// UUID represents a globally unique ID of various resources like network and endpoint
type UUID string
// TransportPort represent a local Layer 4 endpoint
type TransportPort struct {
Proto Protocol
Port uint16
}
// GetCopy returns a copy of this TransportPort structure instance
func (t *TransportPort) GetCopy() TransportPort {
return TransportPort{Proto: t.Proto, Port: t.Port}
}
// PortBinding represent a port binding between the container an the host
type PortBinding struct {
Proto Protocol
IP net.IP
Port uint16
HostIP net.IP
HostPort uint16
}
// HostAddr returns the host side transport address
func (p PortBinding) HostAddr() (net.Addr, error) {
switch p.Proto {
case UDP:
return &net.UDPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
case TCP:
return &net.TCPAddr{IP: p.HostIP, Port: int(p.HostPort)}, nil
default:
return nil, ErrInvalidProtocolBinding(p.Proto.String())
}
}
// ContainerAddr returns the container side transport address
func (p PortBinding) ContainerAddr() (net.Addr, error) {
switch p.Proto {
case UDP:
return &net.UDPAddr{IP: p.IP, Port: int(p.Port)}, nil
case TCP:
return &net.TCPAddr{IP: p.IP, Port: int(p.Port)}, nil
default:
return nil, ErrInvalidProtocolBinding(p.Proto.String())
}
}
// GetCopy returns a copy of this PortBinding structure instance
func (p *PortBinding) GetCopy() PortBinding {
return PortBinding{
Proto: p.Proto,
IP: GetIPCopy(p.IP),
Port: p.Port,
HostIP: GetIPCopy(p.HostIP),
HostPort: p.HostPort,
}
}
// Equal checks if this instance of PortBinding is equal to the passed one
func (p *PortBinding) Equal(o *PortBinding) bool {
if p == o {
return true
}
if o == nil {
return false
}
if p.Proto != o.Proto || p.Port != o.Port || p.HostPort != o.HostPort {
return false
}
if p.IP != nil {
if !p.IP.Equal(o.IP) {
return false
}
} else {
if o.IP != nil {
return false
}
}
if p.HostIP != nil {
if !p.HostIP.Equal(o.HostIP) {
return false
}
} else {
if o.HostIP != nil {
return false
}
}
return true
}
// ErrInvalidProtocolBinding is returned when the port binding protocol is not valid.
type ErrInvalidProtocolBinding string
func (ipb ErrInvalidProtocolBinding) Error() string {
return fmt.Sprintf("invalid transport protocol: %s", string(ipb))
}
const (
// ICMP is for the ICMP ip protocol
ICMP = 1
// TCP is for the TCP ip protocol
TCP = 6
// UDP is for the UDP ip protocol
UDP = 17
)
// Protocol represents a IP protocol number
type Protocol uint8
func (p Protocol) String() string {
switch p {
case ICMP:
return "icmp"
case TCP:
return "tcp"
case UDP:
return "udp"
default:
return fmt.Sprintf("%d", p)
}
}
// ParseProtocol returns the respective Protocol type for the passed string
func ParseProtocol(s string) Protocol {
switch strings.ToLower(s) {
case "icmp":
return ICMP
case "udp":
return UDP
case "tcp":
return TCP
default:
return 0
}
}
// GetMacCopy returns a copy of the passed MAC address
func GetMacCopy(from net.HardwareAddr) net.HardwareAddr {
to := make(net.HardwareAddr, len(from))
copy(to, from)
return to
}
// GetIPCopy returns a copy of the passed IP address
func GetIPCopy(from net.IP) net.IP {
to := make(net.IP, len(from))
copy(to, from)
return to
}
// GetIPNetCopy returns a copy of the passed IP Network
func GetIPNetCopy(from *net.IPNet) *net.IPNet {
if from == nil {
return nil
}
bm := make(net.IPMask, len(from.Mask))
copy(bm, from.Mask)
return &net.IPNet{IP: GetIPCopy(from.IP), Mask: bm}
}
// CompareIPNet returns equal if the two IP Networks are equal
func CompareIPNet(a, b *net.IPNet) bool {
if a == b {
return true
}
if a == nil || b == nil {
return false
}
return a.IP.Equal(b.IP) && bytes.Equal(a.Mask, b.Mask)
}
/******************************
* Well-known Error Interfaces
******************************/
// MaskableError is an interface for errors which can be ignored by caller
type MaskableError interface {
// Maskable makes implementer into MaskableError type
Maskable()
}
// BadRequestError is an interface for errors originated by a bad request
type BadRequestError interface {
// BadRequest makes implementer into BadRequestError type
BadRequest()
}
// NotFoundError is an interface for errors raised because a needed resource is not available
type NotFoundError interface {
// NotFound makes implementer into NotFoundError type
NotFound()
}
// ForbiddenError is an interface for errors which denote an valid request that cannot be honored
type ForbiddenError interface {
// Forbidden makes implementer into ForbiddenError type
Forbidden()
}
// NoServiceError is an interface for errors returned when the required service is not available
type NoServiceError interface {
// NoService makes implementer into NoServiceError type
NoService()
}
// TimeoutError is an interface for errors raised because of timeout
type TimeoutError interface {
// Timeout makes implementer into TimeoutError type
Timeout()
}
// NotImplementedError is an interface for errors raised because of requested functionality is not yet implemented
type NotImplementedError interface {
// NotImplemented makes implementer into NotImplementedError type
NotImplemented()
}
// InternalError is an interface for errors raised because of an internal error
type InternalError interface {
// Internal makes implementer into InternalError type
Internal()
}
/******************************
* Weel-known Error Formatters
******************************/
// BadRequestErrorf creates an instance of BadRequestError
func BadRequestErrorf(format string, params ...interface{}) error {
return badRequest(fmt.Sprintf(format, params...))
}
// NotFoundErrorf creates an instance of NotFoundError
func NotFoundErrorf(format string, params ...interface{}) error {
return notFound(fmt.Sprintf(format, params...))
}
// ForbiddenErrorf creates an instance of ForbiddenError
func ForbiddenErrorf(format string, params ...interface{}) error {
return forbidden(fmt.Sprintf(format, params...))
}
// NoServiceErrorf creates an instance of NoServiceError
func NoServiceErrorf(format string, params ...interface{}) error {
return noService(fmt.Sprintf(format, params...))
}
// NotImplementedErrorf creates an instance of NotImplementedError
func NotImplementedErrorf(format string, params ...interface{}) error {
return notImpl(fmt.Sprintf(format, params...))
}
// TimeoutErrorf creates an instance of TimeoutError
func TimeoutErrorf(format string, params ...interface{}) error {
return timeout(fmt.Sprintf(format, params...))
}
// InternalErrorf creates an instance of InternalError
func InternalErrorf(format string, params ...interface{}) error {
return internal(fmt.Sprintf(format, params...))
}
// InternalMaskableErrorf creates an instance of InternalError and MaskableError
func InternalMaskableErrorf(format string, params ...interface{}) error {
return maskInternal(fmt.Sprintf(format, params...))
}
/***********************
* Internal Error Types
***********************/
type badRequest string
func (br badRequest) Error() string {
return string(br)
}
func (br badRequest) BadRequest() {}
type maskBadRequest string
type notFound string
func (nf notFound) Error() string {
return string(nf)
}
func (nf notFound) NotFound() {}
type forbidden string
func (frb forbidden) Error() string {
return string(frb)
}
func (frb forbidden) Forbidden() {}
type noService string
func (ns noService) Error() string {
return string(ns)
}
func (ns noService) NoService() {}
type maskNoService string
type timeout string
func (to timeout) Error() string {
return string(to)
}
func (to timeout) Timeout() {}
type notImpl string
func (ni notImpl) Error() string {
return string(ni)
}
func (ni notImpl) NotImplemented() {}
type internal string
func (nt internal) Error() string {
return string(nt)
}
func (nt internal) Internal() {}
type maskInternal string
func (mnt maskInternal) Error() string {
return string(mnt)
}
func (mnt maskInternal) Internal() {}
func (mnt maskInternal) Maskable() {}
+99
View File
@@ -0,0 +1,99 @@
package types
import (
"testing"
_ "github.com/docker/libnetwork/netutils"
)
func TestErrorConstructors(t *testing.T) {
var err error
err = BadRequestErrorf("Io ho %d uccello", 1)
if err.Error() != "Io ho 1 uccello" {
t.Fatal(err)
}
if _, ok := err.(BadRequestError); !ok {
t.Fatal(err)
}
if _, ok := err.(MaskableError); ok {
t.Fatal(err)
}
err = NotFoundErrorf("Can't find the %s", "keys")
if err.Error() != "Can't find the keys" {
t.Fatal(err)
}
if _, ok := err.(NotFoundError); !ok {
t.Fatal(err)
}
if _, ok := err.(MaskableError); ok {
t.Fatal(err)
}
err = ForbiddenErrorf("Can't open door %d", 2)
if err.Error() != "Can't open door 2" {
t.Fatal(err)
}
if _, ok := err.(ForbiddenError); !ok {
t.Fatal(err)
}
if _, ok := err.(MaskableError); ok {
t.Fatal(err)
}
err = NotImplementedErrorf("Functionality %s is not implemented", "x")
if err.Error() != "Functionality x is not implemented" {
t.Fatal(err)
}
if _, ok := err.(NotImplementedError); !ok {
t.Fatal(err)
}
if _, ok := err.(MaskableError); ok {
t.Fatal(err)
}
err = TimeoutErrorf("Process %s timed out", "abc")
if err.Error() != "Process abc timed out" {
t.Fatal(err)
}
if _, ok := err.(TimeoutError); !ok {
t.Fatal(err)
}
if _, ok := err.(MaskableError); ok {
t.Fatal(err)
}
err = NoServiceErrorf("Driver %s is not available", "mh")
if err.Error() != "Driver mh is not available" {
t.Fatal(err)
}
if _, ok := err.(NoServiceError); !ok {
t.Fatal(err)
}
if _, ok := err.(MaskableError); ok {
t.Fatal(err)
}
err = InternalErrorf("Not sure what happened")
if err.Error() != "Not sure what happened" {
t.Fatal(err)
}
if _, ok := err.(InternalError); !ok {
t.Fatal(err)
}
if _, ok := err.(MaskableError); ok {
t.Fatal(err)
}
err = InternalMaskableErrorf("Minor issue, it can be ignored")
if err.Error() != "Minor issue, it can be ignored" {
t.Fatal(err)
}
if _, ok := err.(InternalError); !ok {
t.Fatal(err)
}
if _, ok := err.(MaskableError); !ok {
t.Fatal(err)
}
}