diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index bbf2cd2fd6..ecfd5d14c5 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -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 diff --git a/libnetwork/drivers/bridge/bridge_test.go b/libnetwork/drivers/bridge/bridge_test.go index e1891af8fe..04e0a82e72 100644 --- a/libnetwork/drivers/bridge/bridge_test.go +++ b/libnetwork/drivers/bridge/bridge_test.go @@ -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, nil, 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() diff --git a/libnetwork/drivers/bridge/labels.go b/libnetwork/drivers/bridge/labels.go new file mode 100644 index 0000000000..7447bd3f93 --- /dev/null +++ b/libnetwork/drivers/bridge/labels.go @@ -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" +) diff --git a/libnetwork/netlabel/labels.go b/libnetwork/netlabel/labels.go index 5a9ff79998..71dff08632 100644 --- a/libnetwork/netlabel/labels.go +++ b/libnetwork/netlabel/labels.go @@ -1,7 +1,6 @@ package netlabel import ( - "fmt" "strings" ) @@ -31,6 +30,9 @@ const ( //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" @@ -77,35 +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) (string, string, error) { - kv := strings.SplitN(label, "=", 2) - if len(kv) != 2 { - return "", "", fmt.Errorf("invalid label: %s", label) +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 kv[0], kv[1], nil + return } -// ToMap converts a list of labels in amap of (key,value) pairs +// 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 { - if k, v, err := KeyValue(l); err == nil { - m[k] = v - } + 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 +} diff --git a/libnetwork/netlabel/labels_test.go b/libnetwork/netlabel/labels_test.go index e000f6e4f9..892dfe5b33 100644 --- a/libnetwork/netlabel/labels_test.go +++ b/libnetwork/netlabel/labels_test.go @@ -6,44 +6,30 @@ import ( _ "github.com/docker/libnetwork/testutils" ) -func TestKeyValue(t *testing.T) { - input := []struct { - label string - key string - value string - good bool - }{ - {"name=joe", "name", "joe", true}, - {"age=24", "age", "24", true}, - {"address:1234 First st.", "", "", false}, - {"girlfriend=", "girlfriend", "", true}, - {"nickname=o=u=8", "nickname", "o=u=8", true}, - {"", "", "", false}, - } +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, err := KeyValue(i.label) - if k != i.key || v != i.value || i.good != (err == nil) { - t.Fatalf("unexpected: %s, %s, %v", k, v, err) + k, v := KeyValue(i.label) + if k != i.key || v != i.value { + t.Fatalf("unexpected: %s, %s", k, v) } } } func TestToMap(t *testing.T) { - input := []struct { - label string - key string - value string - good bool - }{ - {"name=joe", "name", "joe", true}, - {"age=24", "age", "24", true}, - {"address:1234 First st.", "", "", false}, - {"girlfriend=", "girlfriend", "", true}, - {"nickname=o=u=8", "nickname", "o=u=8", true}, - {"", "", "", false}, - } - lista := make([]string, len(input)) for ind, i := range input { lista[ind] = i.label @@ -51,19 +37,41 @@ func TestToMap(t *testing.T) { mappa := ToMap(lista) - if len(mappa) != len(lista)-2 { + if len(mappa) != len(lista) { t.Fatalf("Incorrect map length. Expected %d. Got %d", len(lista), len(mappa)) } for _, i := range input { - if i.good { - if v, ok := mappa[i.key]; !ok || v != i.value { - t.Fatalf("Cannot find key or value for key: %s", i.key) - } - } else { - if _, ok := mappa[i.key]; ok { - t.Fatalf("Found invalid key in map: %s", i.key) - } + 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) } } } diff --git a/libnetwork/network.go b/libnetwork/network.go index 4537273d2f..da2be66902 100644 --- a/libnetwork/network.go +++ b/libnetwork/network.go @@ -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() +} diff --git a/libnetwork/types/types.go b/libnetwork/types/types.go index 22463c9540..7ada9643c0 100644 --- a/libnetwork/types/types.go +++ b/libnetwork/types/types.go @@ -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 { diff --git a/libnetwork/types/types_test.go b/libnetwork/types/types_test.go index e5a94d22c2..c144df1f5c 100644 --- a/libnetwork/types/types_test.go +++ b/libnetwork/types/types_test.go @@ -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