Enable Network labels in backend
- Allow labels to be passed to network driver during network create Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
parent
b7c2b8111f
commit
02386e85d5
8 changed files with 378 additions and 127 deletions
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
18
libnetwork/drivers/bridge/labels.go
Normal file
18
libnetwork/drivers/bridge/labels.go
Normal 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"
|
||||
)
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue