1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Merge pull request #222 from aboch/nlb

Labels support for Network Create
This commit is contained in:
Jana Radhakrishnan 2015-10-09 01:16:48 -07:00
commit b5145e597b
14 changed files with 494 additions and 176 deletions

View file

@ -8,7 +8,6 @@ import (
"strings"
"github.com/docker/libnetwork"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/types"
"github.com/gorilla/mux"
)
@ -52,9 +51,6 @@ const (
urlSbPID = "sandbox-partial-id"
urlCnID = "container-id"
urlCnPID = "container-partial-id"
// BridgeNetworkDriver is the built-in default for Network Driver
BridgeNetworkDriver = "bridge"
)
// NewHTTPHandler creates and initialize the HTTP handler to serve the requests for libnetwork
@ -222,16 +218,6 @@ func buildSandboxResource(sb libnetwork.Sandbox) *sandboxResource {
Options Parsers
*****************/
func (nc *networkCreate) parseOptions() []libnetwork.NetworkOption {
var setFctList []libnetwork.NetworkOption
if nc.Options != nil {
setFctList = append(setFctList, libnetwork.NetworkOptionGeneric(nc.Options))
}
return setFctList
}
func (sc *sandboxCreate) parseOptions() []libnetwork.SandboxOption {
var setFctList []libnetwork.SandboxOption
if sc.HostName != "" {
@ -278,21 +264,6 @@ func processCreateDefaults(c libnetwork.NetworkController, nc *networkCreate) {
if nc.NetworkType == "" {
nc.NetworkType = c.Config().Daemon.DefaultDriver
}
if nc.NetworkType == BridgeNetworkDriver {
if nc.Options == nil {
nc.Options = make(map[string]interface{})
}
genericData, ok := nc.Options[netlabel.GenericData]
if !ok {
genericData = make(map[string]interface{})
}
gData := genericData.(map[string]interface{})
if _, ok := gData["BridgeName"]; !ok {
gData["BridgeName"] = nc.Name
}
nc.Options[netlabel.GenericData] = genericData
}
}
/***************************
@ -307,7 +278,7 @@ func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, b
}
processCreateDefaults(c, &create)
nw, err := c.NewNetwork(create.NetworkType, create.Name, create.parseOptions()...)
nw, err := c.NewNetwork(create.NetworkType, create.Name, libnetwork.NetworkOptionLabels(create.Labels))
if err != nil {
return "", convertNetworkError(err)
}

View file

@ -15,6 +15,7 @@ import (
"github.com/docker/docker/pkg/reexec"
"github.com/docker/libnetwork"
"github.com/docker/libnetwork/datastore"
"github.com/docker/libnetwork/drivers/bridge"
"github.com/docker/libnetwork/netlabel"
"github.com/docker/libnetwork/options"
"github.com/docker/libnetwork/testutils"
@ -26,12 +27,6 @@ const (
bridgeName = "docker0"
)
func getEmptyGenericOption() map[string]interface{} {
genericOption := make(map[string]interface{})
genericOption[netlabel.GenericData] = options.Generic{}
return genericOption
}
func i2s(i interface{}) string {
s, ok := i.(string)
if !ok {
@ -111,6 +106,22 @@ func createTestNetwork(t *testing.T, network string) (libnetwork.NetworkControll
return c, nw
}
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() []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)},
}
}
func TestMain(m *testing.M) {
if reexec.Init() {
return
@ -214,15 +225,11 @@ func TestCreateDeleteNetwork(t *testing.T) {
t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp)
}
ops := options.Generic{
netlabel.EnableIPv6: true,
netlabel.GenericData: map[string]string{
"BridgeName": "abc",
"FixedCIDRv6": "fe80::1/64",
"AddressIP": "172.28.30.254/24",
},
ops := []string{
bridge.BridgeName + "=abc",
netlabel.EnableIPv6 + "=true",
}
nc := networkCreate{Name: "network_1", NetworkType: bridgeNetType, Options: ops}
nc := networkCreate{Name: "network_1", NetworkType: bridgeNetType, Labels: ops}
goodBody, err := json.Marshal(nc)
if err != nil {
t.Fatal(err)
@ -250,6 +257,29 @@ func TestCreateDeleteNetwork(t *testing.T) {
if errRsp != &successResponse {
t.Fatalf("Unexepected failure: %v", errRsp)
}
// Create with labels
labels := []string{
netlabel.EnableIPv6 + "=true",
bridge.BridgeName + "=abc",
}
nc = networkCreate{Name: "network_2", NetworkType: bridgeNetType, Labels: labels}
goodBody, err = json.Marshal(nc)
if err != nil {
t.Fatal(err)
}
_, errRsp = procCreateNetwork(c, vars, goodBody)
if errRsp != &createdResponse {
t.Fatalf("Unexepected failure: %v", errRsp)
}
vars[urlNwName] = "network_2"
_, errRsp = procDeleteNetwork(c, vars, nil)
if errRsp != &successResponse {
t.Fatalf("Unexepected failure: %v", errRsp)
}
}
func TestGetNetworksAndEndpoints(t *testing.T) {
@ -264,13 +294,10 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
}
defer c.Stop()
ops := options.Generic{
netlabel.GenericData: map[string]string{
"BridgeName": "api_test_nw",
},
ops := []string{
bridge.BridgeName + "=api_test_nw",
}
nc := networkCreate{Name: "sh", NetworkType: bridgeNetType, Options: ops}
nc := networkCreate{Name: "sh", NetworkType: bridgeNetType, Labels: ops}
body, err := json.Marshal(nc)
if err != nil {
t.Fatal(err)
@ -287,17 +314,9 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
}
ec1 := endpointCreate{
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: []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)},
},
Name: "ep1",
ExposedPorts: getExposedPorts(),
PortMapping: getPortMapping(),
}
b1, err := json.Marshal(ec1)
if err != nil {
@ -439,10 +458,10 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
nr1 := i2n(inr1)
delete(vars, urlNwName)
vars[urlNwID] = "cacca"
vars[urlNwID] = "acacac"
_, errRsp = procGetNetwork(c, vars, nil)
if errRsp == &successResponse {
t.Fatalf("Unexepected failure: %v", errRsp)
t.Fatalf("Expected failure. Got: %v", errRsp)
}
vars[urlNwID] = nid
inr2, errRsp := procGetNetwork(c, vars, nil)
@ -825,18 +844,10 @@ func TestProcPublishUnpublishService(t *testing.T) {
}
sp := servicePublish{
Name: "web",
Network: "network",
ExposedPorts: []types.TransportPort{
types.TransportPort{Proto: types.TCP, Port: uint16(6000)},
types.TransportPort{Proto: types.UDP, Port: uint16(500)},
types.TransportPort{Proto: types.TCP, Port: uint16(700)},
},
PortMapping: []types.PortBinding{
types.PortBinding{Proto: types.TCP, Port: uint16(1230), HostPort: uint16(37000)},
types.PortBinding{Proto: types.UDP, Port: uint16(1200), HostPort: uint16(36000)},
types.PortBinding{Proto: types.TCP, Port: uint16(1120), HostPort: uint16(35000)},
},
Name: "web",
Network: "network",
ExposedPorts: getExposedPorts(),
PortMapping: getPortMapping(),
}
b, err = json.Marshal(sp)
if err != nil {
@ -1330,6 +1341,7 @@ func TestJoinLeave(t *testing.T) {
t.Fatalf("Expected failure, got: %v", errRsp)
}
// bad labels
vars[urlEpName] = "endpoint"
key, errRsp := procJoinEndpoint(c, vars, jlb)
if errRsp != &successResponse {
@ -1818,21 +1830,14 @@ func TestEndToEnd(t *testing.T) {
handleRequest := NewHTTPHandler(c)
ops := options.Generic{
netlabel.EnableIPv6: true,
netlabel.GenericData: map[string]string{
"BridgeName": "cdef",
"FixedCIDRv6": "fe80:2000::1/64",
"EnableIPv6": "true",
"Mtu": "1460",
"EnableIPTables": "true",
"AddressIP": "172.28.30.254/16",
"EnableUserlandProxy": "true",
},
ops := []string{
bridge.BridgeName + "=cdef",
netlabel.EnableIPv6 + "=true",
netlabel.DriverMTU + "=1460",
}
// Create network
nc := networkCreate{Name: "network-fiftyfive", NetworkType: bridgeNetType, Options: ops}
nc := networkCreate{Name: "network-fiftyfive", NetworkType: bridgeNetType, Labels: ops}
body, err := json.Marshal(nc)
if err != nil {
t.Fatal(err)

View file

@ -26,7 +26,6 @@ type sandboxResource struct {
ID string `json:"id"`
Key string `json:"key"`
ContainerID string `json:"container_id"`
// will add more fields once labels change is in
}
/***********
@ -35,9 +34,9 @@ type sandboxResource struct {
// networkCreate is the expected body of the "create network" http request message
type networkCreate struct {
Name string `json:"name"`
NetworkType string `json:"network_type"`
Options map[string]interface{} `json:"options"`
Name string `json:"name"`
NetworkType string `json:"network_type"`
Labels []string `json:"labels"`
}
// endpointCreate represents the body of the "create endpoint" http request message

View file

@ -48,8 +48,8 @@ func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error {
}
// Construct network create request body
ops := make(map[string]interface{})
nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver, Options: ops}
var labels []string
nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver, Labels: labels}
obj, _, err := readBody(cli.call("POST", "/networks", nc, nil))
if err != nil {
return err

View file

@ -34,9 +34,9 @@ type SandboxResource struct {
// networkCreate is the expected body of the "create network" http request message
type networkCreate struct {
Name string `json:"name"`
NetworkType string `json:"network_type"`
Options map[string]interface{} `json:"options"`
Name string `json:"name"`
NetworkType string `json:"network_type"`
Labels []string `json:"labels"`
}
// serviceCreate represents the body of the "publish service" http request message

View file

@ -184,7 +184,7 @@ func createDefaultNetwork(c libnetwork.NetworkController) {
if nw != "" && d != "" {
// Bridge driver is special due to legacy reasons
if d == "bridge" {
genericOption[netlabel.GenericData] = map[string]interface{}{
genericOption[netlabel.GenericData] = map[string]string{
"BridgeName": "docker0",
"DefaultBridge": "true",
}

View file

@ -178,7 +178,7 @@ func (c *networkConfiguration) Conflicts(o *networkConfiguration) error {
// Also empty, becasue only one network with empty name is allowed
if c.BridgeName == o.BridgeName {
return fmt.Errorf("networks have same name")
return fmt.Errorf("networks have same bridge name")
}
// They must be in different subnets
@ -196,79 +196,46 @@ func (c *networkConfiguration) Conflicts(o *networkConfiguration) error {
return nil
}
// fromMap retrieve the configuration data from the map form.
func (c *networkConfiguration) fromMap(data map[string]interface{}) error {
func (c *networkConfiguration) fromLabels(labels map[string]string) error {
var err error
if i, ok := data["BridgeName"]; ok && i != nil {
if c.BridgeName, ok = i.(string); !ok {
return types.BadRequestErrorf("invalid type for BridgeName value")
}
}
if i, ok := data["Mtu"]; ok && i != nil {
if s, ok := i.(string); ok {
if c.Mtu, err = strconv.Atoi(s); err != nil {
return types.BadRequestErrorf("failed to parse Mtu value: %s", err.Error())
for label, value := range labels {
switch label {
case BridgeName:
c.BridgeName = value
case netlabel.DriverMTU:
if c.Mtu, err = strconv.Atoi(value); err != nil {
return parseErr(label, value, err.Error())
}
} else {
return types.BadRequestErrorf("invalid type for Mtu value")
}
}
if i, ok := data["EnableIPv6"]; ok && i != nil {
if s, ok := i.(string); ok {
if c.EnableIPv6, err = strconv.ParseBool(s); err != nil {
return types.BadRequestErrorf("failed to parse EnableIPv6 value: %s", err.Error())
case netlabel.EnableIPv6:
if c.EnableIPv6, err = strconv.ParseBool(value); err != nil {
return parseErr(label, value, err.Error())
}
} else {
return types.BadRequestErrorf("invalid type for EnableIPv6 value")
}
}
if i, ok := data["EnableIPMasquerade"]; ok && i != nil {
if s, ok := i.(string); ok {
if c.EnableIPMasquerade, err = strconv.ParseBool(s); err != nil {
return types.BadRequestErrorf("failed to parse EnableIPMasquerade value: %s", err.Error())
case EnableIPMasquerade:
if c.EnableIPMasquerade, err = strconv.ParseBool(value); err != nil {
return parseErr(label, value, err.Error())
}
} else {
return types.BadRequestErrorf("invalid type for EnableIPMasquerade value")
}
}
if i, ok := data["EnableICC"]; ok && i != nil {
if s, ok := i.(string); ok {
if c.EnableICC, err = strconv.ParseBool(s); err != nil {
return types.BadRequestErrorf("failed to parse EnableICC value: %s", err.Error())
case EnableICC:
if c.EnableICC, err = strconv.ParseBool(value); err != nil {
return parseErr(label, value, err.Error())
}
} else {
return types.BadRequestErrorf("invalid type for EnableICC value")
}
}
if i, ok := data["DefaultBridge"]; ok && i != nil {
if s, ok := i.(string); ok {
if c.DefaultBridge, err = strconv.ParseBool(s); err != nil {
return types.BadRequestErrorf("failed to parse DefaultBridge value: %s", err.Error())
case DefaultBridge:
if c.DefaultBridge, err = strconv.ParseBool(value); err != nil {
return parseErr(label, value, err.Error())
}
} else {
return types.BadRequestErrorf("invalid type for DefaultBridge value")
}
}
if i, ok := data["DefaultBindingIP"]; ok && i != nil {
if s, ok := i.(string); ok {
if c.DefaultBindingIP = net.ParseIP(s); c.DefaultBindingIP == nil {
return types.BadRequestErrorf("failed to parse DefaultBindingIP value")
case DefaultBindingIP:
if c.DefaultBindingIP = net.ParseIP(value); c.DefaultBindingIP == nil {
return parseErr(label, value, "nil ip")
}
} else {
return types.BadRequestErrorf("invalid type for DefaultBindingIP value")
}
}
return nil
}
func parseErr(label, value, errString string) error {
return types.BadRequestErrorf("failed to parse %s value: %v (%s)", label, value, errString)
}
func (n *bridgeNetwork) getDriverChains() (*iptables.ChainInfo, *iptables.ChainInfo, error) {
n.Lock()
defer n.Unlock()
@ -442,12 +409,12 @@ func parseNetworkGenericOptions(data interface{}) (*networkConfiguration, error)
switch opt := data.(type) {
case *networkConfiguration:
config = opt
case map[string]interface{}:
case map[string]string:
config = &networkConfiguration{
EnableICC: true,
EnableIPMasquerade: true,
}
err = config.fromMap(opt)
err = config.fromLabels(opt)
case options.Generic:
var opaqueConfig interface{}
if opaqueConfig, err = options.GenerateFromModel(opt, config); err == nil {
@ -491,8 +458,10 @@ func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []d
}
func parseNetworkOptions(id string, option options.Generic) (*networkConfiguration, error) {
var err error
config := &networkConfiguration{}
var (
err error
config = &networkConfiguration{}
)
// Parse generic label first, config will be re-assigned
if genData, ok := option[netlabel.GenericData]; ok && genData != nil {
@ -502,8 +471,8 @@ func parseNetworkOptions(id string, option options.Generic) (*networkConfigurati
}
// Process well-known labels next
if _, ok := option[netlabel.EnableIPv6]; ok {
config.EnableIPv6 = option[netlabel.EnableIPv6].(bool)
if val, ok := option[netlabel.EnableIPv6]; ok {
config.EnableIPv6 = val.(bool)
}
// Finally validate the configuration

View file

@ -97,6 +97,58 @@ func TestCreateNoConfig(t *testing.T) {
}
}
func TestCreateFullOptionsLabels(t *testing.T) {
defer testutils.SetupTestOSContext(t)()
d := newDriver()
config := &configuration{
EnableIPForwarding: true,
}
genericOption := make(map[string]interface{})
genericOption[netlabel.GenericData] = config
if err := d.configure(genericOption); err != nil {
t.Fatalf("Failed to setup driver config: %v", err)
}
labels := map[string]string{
BridgeName: "cu",
netlabel.EnableIPv6: "true",
EnableICC: "true",
EnableIPMasquerade: "true",
DefaultBindingIP: "127.0.0.1",
}
netOption := make(map[string]interface{})
netOption[netlabel.GenericData] = labels
err := d.CreateNetwork("dummy", netOption, getIPv4Data(t), nil)
if err != nil {
t.Fatalf("Failed to create bridge: %v", err)
}
nw, ok := d.networks["dummy"]
if !ok {
t.Fatalf("Cannot find dummy network in bridge driver")
}
if nw.config.BridgeName != "cu" {
t.Fatalf("incongruent name in bridge network")
}
if !nw.config.EnableIPv6 {
t.Fatalf("incongruent EnableIPv6 in bridge network")
}
if !nw.config.EnableICC {
t.Fatalf("incongruent EnableICC in bridge network")
}
if !nw.config.EnableIPMasquerade {
t.Fatalf("incongruent EnableIPMasquerade in bridge network")
}
}
func TestCreate(t *testing.T) {
defer testutils.SetupTestOSContext(t)()
d := newDriver()

View file

@ -0,0 +1,18 @@
package bridge
const (
// BridgeName label for bridge driver
BridgeName = "com.docker.network.bridge.name"
// EnableIPMasquerade label for bridge driver
EnableIPMasquerade = "com.docker.network.bridge.enable_ip_masquerade"
// EnableICC label
EnableICC = "com.docker.network.bridge.enable_icc"
// DefaultBindingIP label
DefaultBindingIP = "com.docker.network.bridge.host_binding_ipv4"
// DefaultBridge label
DefaultBridge = "com.docker.network.bridge.default_bridge"
)

View file

@ -1,6 +1,8 @@
package netlabel
import "strings"
import (
"strings"
)
const (
// Prefix constant marks the reserved label space for libnetwork
@ -22,12 +24,15 @@ const (
// MacAddress constant represents Mac Address config of a Container
MacAddress = Prefix + ".endpoint.macaddress"
// ExposedPorts constant represents exposedports of a Container
// ExposedPorts constant represents the container's Exposed Ports
ExposedPorts = Prefix + ".endpoint.exposedports"
//EnableIPv6 constant represents enabling IPV6 at network level
EnableIPv6 = Prefix + ".enable_ipv6"
// DriverMTU constant represents the MTU size for the network driver
DriverMTU = DriverPrefix + ".mtu"
// OverlayBindInterface constant represents overlay driver bind interface
OverlayBindInterface = DriverPrefix + ".overlay.bind_interface"
@ -74,15 +79,51 @@ func MakeKVProviderConfig(scope string) string {
}
// Key extracts the key portion of the label
func Key(label string) string {
kv := strings.SplitN(label, "=", 2)
return kv[0]
func Key(label string) (key string) {
if kv := strings.SplitN(label, "=", 2); len(kv) > 0 {
key = kv[0]
}
return
}
// Value extracts the value portion of the label
func Value(label string) string {
kv := strings.SplitN(label, "=", 2)
return kv[1]
func Value(label string) (value string) {
if kv := strings.SplitN(label, "=", 2); len(kv) > 1 {
value = kv[1]
}
return
}
// KeyValue decomposes the label in the (key,value) pair
func KeyValue(label string) (key string, value string) {
if kv := strings.SplitN(label, "=", 2); len(kv) > 0 {
key = kv[0]
if len(kv) > 1 {
value = kv[1]
}
}
return
}
// ToMap converts a list of labels in a map of (key,value) pairs
func ToMap(labels []string) map[string]string {
m := make(map[string]string, len(labels))
for _, l := range labels {
k, v := KeyValue(l)
m[k] = v
}
return m
}
// FromMap converts a map of (key,value) pairs in a lsit of labels
func FromMap(m map[string]string) []string {
l := make([]string, 0, len(m))
for k, v := range m {
s := k
if v != "" {
s = s + "=" + v
}
l = append(l, s)
}
return l
}

View file

@ -0,0 +1,77 @@
package netlabel
import (
"testing"
_ "github.com/docker/libnetwork/testutils"
)
var input = []struct {
label string
key string
value string
}{
{"com.directory.person.name=joe", "com.directory.person.name", "joe"},
{"com.directory.person.age=24", "com.directory.person.age", "24"},
{"com.directory.person.address=1234 First st.", "com.directory.person.address", "1234 First st."},
{"com.directory.person.friends=", "com.directory.person.friends", ""},
{"com.directory.person.nickname=o=u=8", "com.directory.person.nickname", "o=u=8"},
{"", "", ""},
{"com.directory.person.student", "com.directory.person.student", ""},
}
func TestKeyValue(t *testing.T) {
for _, i := range input {
k, v := KeyValue(i.label)
if k != i.key || v != i.value {
t.Fatalf("unexpected: %s, %s", k, v)
}
}
}
func TestToMap(t *testing.T) {
lista := make([]string, len(input))
for ind, i := range input {
lista[ind] = i.label
}
mappa := ToMap(lista)
if len(mappa) != len(lista) {
t.Fatalf("Incorrect map length. Expected %d. Got %d", len(lista), len(mappa))
}
for _, i := range input {
if v, ok := mappa[i.key]; !ok || v != i.value {
t.Fatalf("Cannot find key or value for key: %s", i.key)
}
}
}
func TestFromMap(t *testing.T) {
var m map[string]string
lbls := FromMap(m)
if len(lbls) != 0 {
t.Fatalf("unexpected lbls length")
}
m = make(map[string]string, 3)
m["peso"] = "85"
m["statura"] = "170"
m["maschio"] = ""
lbls = FromMap(m)
if len(lbls) != 3 {
t.Fatalf("unexpected lbls length")
}
for _, l := range lbls {
switch l {
case "peso=85":
case "statura=170":
case "maschio":
default:
t.Fatalf("unexpected label: %s", l)
}
}
}

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net"
"strconv"
"sync"
log "github.com/Sirupsen/logrus"
@ -49,6 +50,15 @@ type Network interface {
// EndpointByID returns the Endpoint which has the passed id. If not found, the error ErrNoSuchEndpoint is returned.
EndpointByID(id string) (Endpoint, error)
// Return certain operational data belonging to this network
Info() NetworkInfo
}
// NetworkInfo returns operational information about the network
type NetworkInfo interface {
Labels() []string
Scope() string
}
// EndpointWalker is a client provided function which will be used to walk the Endpoints.
@ -150,7 +160,6 @@ type network struct {
dbExists bool
persist bool
stopWatchCh chan struct{}
scope string
drvOnce *sync.Once
sync.Mutex
}
@ -382,6 +391,18 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
if v, ok := netMap["generic"]; ok {
n.generic = v.(map[string]interface{})
// Restore labels in their map[string]string form
if v, ok := n.generic[netlabel.GenericData]; ok {
var lmap map[string]string
ba, err := json.Marshal(v)
if err != nil {
return err
}
if err := json.Unmarshal(ba, &lmap); err != nil {
return err
}
n.generic[netlabel.GenericData] = lmap
}
}
if v, ok := netMap["persist"]; ok {
n.persist = v.(bool)
@ -391,7 +412,6 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
} else {
n.ipamType = ipamapi.DefaultIPAM
}
if v, ok := netMap["addrSpace"]; ok {
n.addrSpace = v.(string)
}
@ -451,6 +471,25 @@ func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ip
}
}
// NetworkOptionLabels function returns an option setter for any parameter described by a map
func NetworkOptionLabels(labels []string) NetworkOption {
return func(n *network) {
if n.generic == nil {
n.generic = make(map[string]interface{})
}
opts := netlabel.ToMap(labels)
// Store the options
n.generic[netlabel.GenericData] = opts
// Decode and store the endpoint options of libnetwork interest
if val, ok := opts[netlabel.EnableIPv6]; ok {
var err error
if n.enableIPv6, err = strconv.ParseBool(val); err != nil {
log.Warnf("Failed to parse %s' value: %s (%s)", netlabel.EnableIPv6, val, err.Error())
}
}
}
}
func (n *network) processOptions(options ...NetworkOption) {
for _, opt := range options {
if opt != nil {
@ -1003,3 +1042,22 @@ func (n *network) deriveAddressSpace() (string, error) {
}
return ipd.defaultLocalAddressSpace, nil
}
func (n *network) Info() NetworkInfo {
return n
}
func (n *network) Labels() []string {
n.Lock()
defer n.Unlock()
if n.generic != nil {
if m, ok := n.generic[netlabel.GenericData]; ok {
return netlabel.FromMap(m.(map[string]string))
}
}
return []string{}
}
func (n *network) Scope() string {
return n.driverScope()
}

View file

@ -5,6 +5,7 @@ import (
"bytes"
"fmt"
"net"
"strconv"
"strings"
)
@ -17,11 +18,46 @@ type TransportPort struct {
Port uint16
}
// Equal checks if this instance of Transportport is equal to the passed one
func (t *TransportPort) Equal(o *TransportPort) bool {
if t == o {
return true
}
if o == nil {
return false
}
if t.Proto != o.Proto || t.Port != o.Port {
return false
}
return true
}
// GetCopy returns a copy of this TransportPort structure instance
func (t *TransportPort) GetCopy() TransportPort {
return TransportPort{Proto: t.Proto, Port: t.Port}
}
// String returns the TransportPort structure in string form
func (t *TransportPort) String() string {
return fmt.Sprintf("%s/%d", t.Proto.String(), t.Port)
}
// FromString reads the TransportPort structure from string
func (t *TransportPort) FromString(s string) error {
ps := strings.Split(s, "/")
if len(ps) == 2 {
t.Proto = ParseProtocol(ps[0])
if p, err := strconv.ParseUint(ps[1], 10, 16); err == nil {
t.Port = uint16(p)
return nil
}
}
return BadRequestErrorf("invalid format for transport port: %s", s)
}
// PortBinding represent a port binding between the container and the host
type PortBinding struct {
Proto Protocol
@ -68,6 +104,62 @@ func (p *PortBinding) GetCopy() PortBinding {
}
}
// String return the PortBinding structure in string form
func (p *PortBinding) String() string {
ret := fmt.Sprintf("%s/", p.Proto)
if p.IP != nil {
ret = fmt.Sprintf("%s%s", ret, p.IP.String())
}
ret = fmt.Sprintf("%s:%d/", ret, p.Port)
if p.HostIP != nil {
ret = fmt.Sprintf("%s%s", ret, p.HostIP.String())
}
ret = fmt.Sprintf("%s:%d", ret, p.HostPort)
return ret
}
// FromString reads the TransportPort structure from string
func (p *PortBinding) FromString(s string) error {
ps := strings.Split(s, "/")
if len(ps) != 3 {
return BadRequestErrorf("invalid format for port binding: %s", s)
}
p.Proto = ParseProtocol(ps[0])
var err error
if p.IP, p.Port, err = parseIPPort(ps[1]); err != nil {
return BadRequestErrorf("failed to parse Container IP/Port in port binding: %s", err.Error())
}
if p.HostIP, p.HostPort, err = parseIPPort(ps[2]); err != nil {
return BadRequestErrorf("failed to parse Host IP/Port in port binding: %s", err.Error())
}
return nil
}
func parseIPPort(s string) (net.IP, uint16, error) {
pp := strings.Split(s, ":")
if len(pp) != 2 {
return nil, 0, BadRequestErrorf("invalid format: %s", s)
}
var ip net.IP
if pp[0] != "" {
if ip = net.ParseIP(pp[0]); ip == nil {
return nil, 0, BadRequestErrorf("invalid ip: %s", pp[0])
}
}
port, err := strconv.ParseUint(pp[1], 10, 16)
if err != nil {
return nil, 0, BadRequestErrorf("invalid port: %s", pp[1])
}
return ip, uint16(port), nil
}
// Equal checks if this instance of PortBinding is equal to the passed one
func (p *PortBinding) Equal(o *PortBinding) bool {
if p == o {

View file

@ -8,6 +8,42 @@ import (
var runningInContainer = flag.Bool("incontainer", false, "Indicates if the test is running in a container")
func TestTransportPortConv(t *testing.T) {
sform := "tcp/23"
tp := &TransportPort{Proto: TCP, Port: uint16(23)}
if sform != tp.String() {
t.Fatalf("String() method failed")
}
rc := new(TransportPort)
if err := rc.FromString(sform); err != nil {
t.Fatal(err)
}
if !tp.Equal(rc) {
t.Fatalf("FromString() method failed")
}
}
func TestTransportPortBindingConv(t *testing.T) {
sform := "tcp/172.28.30.23:80/112.0.43.56:8001"
pb := &PortBinding{
Proto: TCP,
IP: net.IPv4(172, 28, 30, 23),
Port: uint16(80),
HostIP: net.IPv4(112, 0, 43, 56),
HostPort: uint16(8001),
}
rc := new(PortBinding)
if err := rc.FromString(sform); err != nil {
t.Fatal(err)
}
if !pb.Equal(rc) {
t.Fatalf("FromString() method failed")
}
}
func TestErrorConstructors(t *testing.T) {
var err error