diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index b307667531..9461abc452 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -50,6 +50,7 @@ type Configuration struct { type EndpointConfiguration struct { MacAddress net.HardwareAddr PortBindings []netutils.PortBinding + ExposedPorts []netutils.TransportPort } // ContainerConfiguration represents the user specified configuration for a container @@ -663,7 +664,7 @@ func (d *driver) link(nid, eid types.UUID, options map[string]interface{}, enabl return nil } - if endpoint.config != nil && endpoint.config.PortBindings != nil { + if endpoint.config != nil && endpoint.config.ExposedPorts != nil { for _, p := range cc.ParentEndpoints { var parentEndpoint *bridgeEndpoint parentEndpoint, err = network.getEndpoint(types.UUID(p)) @@ -677,7 +678,7 @@ func (d *driver) link(nid, eid types.UUID, options map[string]interface{}, enabl l := newLink(parentEndpoint.intf.Address.IP.String(), endpoint.intf.Address.IP.String(), - endpoint.config.PortBindings, d.config.BridgeName) + endpoint.config.ExposedPorts, d.config.BridgeName) if enable { err = l.Enable() if err != nil { @@ -704,12 +705,12 @@ func (d *driver) link(nid, eid types.UUID, options map[string]interface{}, enabl err = InvalidEndpointIDError(c) return err } - if childEndpoint.config == nil || childEndpoint.config.PortBindings == nil { + if childEndpoint.config == nil || childEndpoint.config.ExposedPorts == nil { continue } l := newLink(endpoint.intf.Address.IP.String(), childEndpoint.intf.Address.IP.String(), - childEndpoint.config.PortBindings, d.config.BridgeName) + childEndpoint.config.ExposedPorts, d.config.BridgeName) if enable { err = l.Enable() if err != nil { @@ -754,6 +755,14 @@ func parseEndpointOptions(epOptions map[string]interface{}) (*EndpointConfigurat } } + if opt, ok := epOptions[options.ExposedPorts]; ok { + if ports, ok := opt.([]netutils.TransportPort); ok { + ec.ExposedPorts = ports + } else { + return nil, ErrInvalidEndpointConfig + } + } + return ec, nil } diff --git a/libnetwork/drivers/bridge/bridge_test.go b/libnetwork/drivers/bridge/bridge_test.go index d53d96deb2..94c3fe6b53 100644 --- a/libnetwork/drivers/bridge/bridge_test.go +++ b/libnetwork/drivers/bridge/bridge_test.go @@ -171,6 +171,14 @@ 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 getPortMapping() []netutils.PortBinding { return []netutils.PortBinding{ netutils.PortBinding{Proto: netutils.TCP, Port: uint16(230), HostPort: uint16(23000)}, @@ -201,9 +209,9 @@ func TestLinkContainers(t *testing.T) { t.Fatalf("Failed to create bridge: %v", err) } - portMappings := getPortMapping() + exposedPorts := getExposedPorts() epOptions := make(map[string]interface{}) - epOptions[options.PortMap] = portMappings + epOptions[options.ExposedPorts] = exposedPorts sinfo, err := d.CreateEndpoint("net1", "ep1", epOptions) if err != nil { @@ -235,13 +243,12 @@ func TestLinkContainers(t *testing.T) { t.Fatalf("Failed to link ep1 and ep2") } - out, err := iptables.Raw("-L", "DOCKER") - for _, pm := range portMappings { + out, err := iptables.Raw("-L", DockerChain) + for _, pm := range exposedPorts { regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port) re := regexp.MustCompile(regex) matches := re.FindAllString(string(out[:]), -1) - // There will be 2 matches : Port-Mapping and Linking table rules - if len(matches) < 2 { + if len(matches) != 1 { t.Fatalf("IP Tables programming failed %s", string(out[:])) } @@ -257,13 +264,12 @@ func TestLinkContainers(t *testing.T) { t.Fatalf("Failed to unlink ep1 and ep2") } - out, err = iptables.Raw("-L", "DOCKER") - for _, pm := range portMappings { + out, err = iptables.Raw("-L", DockerChain) + for _, pm := range exposedPorts { regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port) re := regexp.MustCompile(regex) matches := re.FindAllString(string(out[:]), -1) - // There will be 1 match : Port-Mapping - if len(matches) > 1 { + if len(matches) != 0 { t.Fatalf("Leave should have deleted relevant IPTables rules %s", string(out[:])) } @@ -282,13 +288,12 @@ func TestLinkContainers(t *testing.T) { _, err = d.Join("net1", "ep2", "", genericOption) if err != nil { - out, err = iptables.Raw("-L", "DOCKER") - for _, pm := range portMappings { + out, err = iptables.Raw("-L", DockerChain) + for _, pm := range exposedPorts { regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port) re := regexp.MustCompile(regex) matches := re.FindAllString(string(out[:]), -1) - // There must be 1 match : Port-Mapping - if len(matches) > 1 { + if len(matches) != 0 { t.Fatalf("Error handling should rollback relevant IPTables rules %s", string(out[:])) } @@ -298,6 +303,8 @@ func TestLinkContainers(t *testing.T) { t.Fatalf("Error handling should rollback relevant IPTables rules %s", string(out[:])) } } + } else { + t.Fatalf("Expected Join to fail given link conditions are not satisfied") } } diff --git a/libnetwork/drivers/bridge/link.go b/libnetwork/drivers/bridge/link.go index 774cd89397..20ecca04d2 100644 --- a/libnetwork/drivers/bridge/link.go +++ b/libnetwork/drivers/bridge/link.go @@ -12,7 +12,7 @@ import ( type link struct { parentIP string childIP string - ports []netutils.PortBinding + ports []netutils.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.PortBinding, bridge string) *link { +func newLink(parentIP, childIP string, ports []netutils.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.PortBinding, bridge string, +func linkContainers(action, parentIP, childIP string, ports []netutils.TransportPort, bridge string, ignoreErrors bool) error { var nfAction iptables.Action diff --git a/libnetwork/endpoint.go b/libnetwork/endpoint.go index 605616064a..70dd600ef3 100644 --- a/libnetwork/endpoint.go +++ b/libnetwork/endpoint.go @@ -482,22 +482,27 @@ func JoinOptionUseDefaultSandbox() EndpointOption { } } -// CreateOptionPortMapping function returns an option setter for the container exposed +// 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 { + return func(ep *endpoint) { + // Defensive copy + eps := make([]netutils.TransportPort, len(exposedPorts)) + copy(eps, exposedPorts) + // Store endpoint label and in generic because driver needs it + ep.exposedPorts = eps + ep.generic[options.ExposedPorts] = eps + } +} + +// CreateOptionPortMapping function returns an option setter for the mapping // ports option to be passed to network.CreateEndpoint() method. func CreateOptionPortMapping(portBindings []netutils.PortBinding) EndpointOption { return func(ep *endpoint) { - // Extract and store exposed ports as this is the only concern of libnetwork endpoint // Store a copy of the bindings as generic data to pass to the driver - pbs := make([]netutils.PortBinding, 0, len(portBindings)) - exp := make([]netutils.TransportPort, 0, len(portBindings)) - - for _, b := range portBindings { - pbs = append(pbs, b.GetCopy()) - exp = append(exp, netutils.TransportPort{Proto: b.Proto, Port: b.Port}) - } - + pbs := make([]netutils.PortBinding, len(portBindings)) + copy(pbs, portBindings) ep.generic[options.PortMap] = pbs - ep.exposedPorts = exp } } diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index fc51f49c97..6e74952f5d 100644 --- a/libnetwork/libnetwork_test.go +++ b/libnetwork/libnetwork_test.go @@ -179,7 +179,7 @@ func TestBridge(t *testing.T) { t.Fatalf("Unexpected format for port mapping in endpoint operational data") } if len(pm) != 3 { - t.Fatalf("Incomplete data for port mapping in endpoint operational data") + t.Fatalf("Incomplete data for port mapping in endpoint operational data: %d", len(pm)) } if err := ep.Delete(); err != nil { diff --git a/libnetwork/netutils/utils.go b/libnetwork/netutils/utils.go index 05ce372f22..158b2998cf 100644 --- a/libnetwork/netutils/utils.go +++ b/libnetwork/netutils/utils.go @@ -39,6 +39,11 @@ type TransportPort struct { 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 diff --git a/libnetwork/pkg/options/options.go b/libnetwork/pkg/options/options.go index 846d1ea5a1..673da5632d 100644 --- a/libnetwork/pkg/options/options.go +++ b/libnetwork/pkg/options/options.go @@ -14,6 +14,8 @@ const ( PortMap = "io.docker.network.endpoint.portmap" // MacAddress constant represents Mac Address config of a Container MacAddress = "io.docker.network.endpoint.macaddress" + // ExposedPorts constant represents exposedports of a Container + ExposedPorts = "io.docker.network.endpoint.exposedports" ) // NoSuchFieldError is the error returned when the generic parameters hold a