mirror of
https://github.com/clearlinux/libnetwork.git
synced 2026-06-16 02:25:48 +00:00
Compare commits
77 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3daf672705 | |||
| 96afee8b4c | |||
| fc286182cd | |||
| d82b7a9573 | |||
| 75716d8756 | |||
| 0c4a707c98 | |||
| 4d49958029 | |||
| eb4ecdf537 | |||
| 30d1b7861f | |||
| 7465ee11d3 | |||
| 6cacb70196 | |||
| cb9c2bfaa2 | |||
| 36bcacd1c6 | |||
| 7b9631cd46 | |||
| b116b5c0d2 | |||
| 78fef19fc6 | |||
| 6c07f504c3 | |||
| c32ef60c4b | |||
| e578e95aa1 | |||
| 9548cbe674 | |||
| 90638ec9cf | |||
| 22f8e6bbab | |||
| 8b3374153e | |||
| 9c6e929b63 | |||
| ace6b57623 | |||
| 68d42b860c | |||
| c624d72ce9 | |||
| 6532b64a07 | |||
| f72ad20491 | |||
| 2133cdc219 | |||
| 005bc475ee | |||
| d1d67dca84 | |||
| 4ded6fe364 | |||
| effb423db7 | |||
| a5ac79f562 | |||
| 694754890c | |||
| 7c2a2c8a87 | |||
| 1ab46c7c4b | |||
| 2da2dc055d | |||
| 83799f7458 | |||
| 60da50e183 | |||
| b028371e0a | |||
| fc2dfe5201 | |||
| 051f4ccdad | |||
| a4d595b6e7 | |||
| 05462c27b2 | |||
| 6743808072 | |||
| 8b61195508 | |||
| 74c93b8c68 | |||
| ed7cba986e | |||
| 377066c62c | |||
| ba22b50a6a | |||
| 198f0ef7cf | |||
| 429678548b | |||
| 3892c2db42 | |||
| 1f0f737948 | |||
| 17bfe1f3f5 | |||
| 3253d89aed | |||
| 3dec1a68e0 | |||
| bc56eb5053 | |||
| 647975a330 | |||
| eb8273e466 | |||
| b18eff4940 | |||
| 6a48128ebc | |||
| 7c707bc574 | |||
| 9909a379b9 | |||
| 1bf2e96b08 | |||
| 4a6f3c2377 | |||
| fc02890e06 | |||
| 9fa618958a | |||
| 50964c9948 | |||
| 39888c3836 | |||
| e2bdf92d6b | |||
| 7783fcd13f | |||
| b39597744b | |||
| a5ed1b5bd4 | |||
| ee9ebf0e89 |
Generated
+8
-3
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
Michael Crosby <michael@crosbymichael.com> (@crosbymichael)
|
||||
Guillaume J. Charmes <guillaume@docker.com> (@creack)
|
||||
+31
@@ -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
|
||||
}
|
||||
+1307
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
package netlink
|
||||
|
||||
func ifrDataByte(b byte) uint8 {
|
||||
return uint8(b)
|
||||
}
|
||||
Generated
Vendored
+7
@@ -0,0 +1,7 @@
|
||||
// +build !arm
|
||||
|
||||
package netlink
|
||||
|
||||
func ifrDataByte(b byte) int8 {
|
||||
return int8(b)
|
||||
}
|
||||
Generated
Vendored
+408
@@ -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)
|
||||
}
|
||||
}
|
||||
Generated
Vendored
+88
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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.
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// +build linux,386
|
||||
|
||||
package netns
|
||||
|
||||
const (
|
||||
|
||||
Generated
Vendored
+2
@@ -1,3 +1,5 @@
|
||||
// +build linux,amd64
|
||||
|
||||
package netns
|
||||
|
||||
const (
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
// +build linux,arm
|
||||
|
||||
package netns
|
||||
|
||||
const (
|
||||
SYS_SETNS = 374
|
||||
SYS_SETNS = 375
|
||||
)
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// +build linux,arm64
|
||||
|
||||
package netns
|
||||
|
||||
const (
|
||||
SYS_SETNS = 268
|
||||
)
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
// +build linux,ppc64le
|
||||
|
||||
package netns
|
||||
|
||||
const (
|
||||
SYS_SETNS = 350
|
||||
)
|
||||
+6
-6
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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
@@ -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)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// +build !experimental
|
||||
|
||||
package client
|
||||
|
||||
var (
|
||||
serviceCommands = []command{}
|
||||
)
|
||||
@@ -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
@@ -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
@@ -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
@@ -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")
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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{}
|
||||
}
|
||||
|
||||
@@ -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)},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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() {}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ const (
|
||||
func setupIPForwarding(config *Configuration) error {
|
||||
// Sanity Check
|
||||
if config.EnableIPForwarding == false {
|
||||
return ErrIPFwdCfg
|
||||
return &ErrIPFwdCfg{}
|
||||
}
|
||||
|
||||
// Enable IPv4 forwarding
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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 {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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{
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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() {}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user