From 1638fbdf279b1646ee0d50387b3ed291bbc3ac45 Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Mon, 7 Dec 2015 14:45:51 -0800 Subject: [PATCH] Move exposed ports from Endpoint to Sandbox Signed-off-by: Alessandro Boch --- libnetwork/api/api.go | 20 +- libnetwork/api/api_test.go | 20 +- libnetwork/api/types.go | 34 ++- libnetwork/client/types.go | 26 +-- libnetwork/default_gateway.go | 44 ++-- libnetwork/driverapi/driverapi.go | 8 + libnetwork/drivers/bridge/bridge.go | 200 ++++++++++++------ libnetwork/drivers/bridge/bridge_test.go | 78 +++++-- libnetwork/drivers/bridge/labels.go | 6 + libnetwork/drivers/bridge/port_mapping.go | 6 +- .../drivers/bridge/port_mapping_test.go | 24 ++- libnetwork/drivers/host/host.go | 8 + libnetwork/drivers/null/null.go | 8 + libnetwork/drivers/overlay/ov_network.go | 8 + libnetwork/drivers/remote/api/api.go | 23 ++ libnetwork/drivers/remote/driver.go | 34 +++ libnetwork/drivers/windows/windows.go | 8 + libnetwork/endpoint.go | 120 +++++++---- libnetwork/libnetwork_internal_test.go | 6 + libnetwork/libnetwork_test.go | 30 ++- libnetwork/sandbox.go | 77 ++++++- 21 files changed, 552 insertions(+), 236 deletions(-) diff --git a/libnetwork/api/api.go b/libnetwork/api/api.go index f860ab1f5f..d3f070d220 100644 --- a/libnetwork/api/api.go +++ b/libnetwork/api/api.go @@ -251,6 +251,12 @@ func (sc *sandboxCreate) parseOptions() []libnetwork.SandboxOption { setFctList = append(setFctList, libnetwork.OptionExtraHost(e.Name, e.Address)) } } + if sc.ExposedPorts != nil { + setFctList = append(setFctList, libnetwork.OptionExposedPorts(sc.ExposedPorts)) + } + if sc.PortMapping != nil { + setFctList = append(setFctList, libnetwork.OptionPortMapping(sc.PortMapping)) + } return setFctList } @@ -384,13 +390,6 @@ func procCreateEndpoint(c libnetwork.NetworkController, vars map[string]string, } var setFctList []libnetwork.EndpointOption - if ec.ExposedPorts != nil { - setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(ec.ExposedPorts)) - } - if ec.PortMapping != nil { - setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(ec.PortMapping)) - } - for _, str := range ec.MyAliases { setFctList = append(setFctList, libnetwork.CreateOptionMyAlias(str)) } @@ -633,13 +632,6 @@ func procPublishService(c libnetwork.NetworkController, vars map[string]string, } var setFctList []libnetwork.EndpointOption - if sp.ExposedPorts != nil { - setFctList = append(setFctList, libnetwork.CreateOptionExposedPorts(sp.ExposedPorts)) - } - if sp.PortMapping != nil { - setFctList = append(setFctList, libnetwork.CreateOptionPortMapping(sp.PortMapping)) - } - for _, str := range sp.MyAliases { setFctList = append(setFctList, libnetwork.CreateOptionMyAlias(str)) } diff --git a/libnetwork/api/api_test.go b/libnetwork/api/api_test.go index a7842c8516..5e563c3c5b 100644 --- a/libnetwork/api/api_test.go +++ b/libnetwork/api/api_test.go @@ -293,9 +293,7 @@ func TestGetNetworksAndEndpoints(t *testing.T) { } ec1 := endpointCreate{ - Name: "ep1", - ExposedPorts: getExposedPorts(), - PortMapping: getPortMapping(), + Name: "ep1", } b1, err := json.Marshal(ec1) if err != nil { @@ -823,10 +821,8 @@ func TestProcPublishUnpublishService(t *testing.T) { } sp := servicePublish{ - Name: "web", - Network: "network", - ExposedPorts: getExposedPorts(), - PortMapping: getPortMapping(), + Name: "web", + Network: "network", } b, err = json.Marshal(sp) if err != nil { @@ -2087,7 +2083,10 @@ func TestEndToEnd(t *testing.T) { cpid1 := string(chars[0 : len(chars)/2]) // Create sandboxes - sb1, err := json.Marshal(sandboxCreate{ContainerID: cid1}) + sb1, err := json.Marshal(sandboxCreate{ + ContainerID: cid1, + PortMapping: getPortMapping(), + }) if err != nil { t.Fatal(err) } @@ -2111,7 +2110,10 @@ func TestEndToEnd(t *testing.T) { t.Fatal(err) } - sb2, err := json.Marshal(sandboxCreate{ContainerID: cid2}) + sb2, err := json.Marshal(sandboxCreate{ + ContainerID: cid2, + ExposedPorts: getExposedPorts(), + }) if err != nil { t.Fatal(err) } diff --git a/libnetwork/api/types.go b/libnetwork/api/types.go index cb027654a9..e13144be73 100644 --- a/libnetwork/api/types.go +++ b/libnetwork/api/types.go @@ -42,23 +42,23 @@ type networkCreate struct { // endpointCreate represents the body of the "create endpoint" http request message type endpointCreate struct { - Name string `json:"name"` - MyAliases []string `json:"my_aliases"` - ExposedPorts []types.TransportPort `json:"exposed_ports"` - PortMapping []types.PortBinding `json:"port_mapping"` + Name string `json:"name"` + MyAliases []string `json:"my_aliases"` } // sandboxCreate is the expected body of the "create sandbox" http request message type sandboxCreate struct { - ContainerID string `json:"container_id"` - HostName string `json:"host_name"` - DomainName string `json:"domain_name"` - HostsPath string `json:"hosts_path"` - ResolvConfPath string `json:"resolv_conf_path"` - DNS []string `json:"dns"` - ExtraHosts []extraHost `json:"extra_hosts"` - UseDefaultSandbox bool `json:"use_default_sandbox"` - UseExternalKey bool `json:"use_external_key"` + ContainerID string `json:"container_id"` + HostName string `json:"host_name"` + DomainName string `json:"domain_name"` + HostsPath string `json:"hosts_path"` + ResolvConfPath string `json:"resolv_conf_path"` + DNS []string `json:"dns"` + ExtraHosts []extraHost `json:"extra_hosts"` + UseDefaultSandbox bool `json:"use_default_sandbox"` + UseExternalKey bool `json:"use_external_key"` + ExposedPorts []types.TransportPort `json:"exposed_ports"` + PortMapping []types.PortBinding `json:"port_mapping"` } // endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages @@ -69,11 +69,9 @@ type endpointJoin struct { // servicePublish represents the body of the "publish service" http request message type servicePublish struct { - Name string `json:"name"` - MyAliases []string `json:"my_aliases"` - Network string `json:"network_name"` - ExposedPorts []types.TransportPort `json:"exposed_ports"` - PortMapping []types.PortBinding `json:"port_mapping"` + Name string `json:"name"` + MyAliases []string `json:"my_aliases"` + Network string `json:"network_name"` } // serviceDelete represents the body of the "unpublish service" http request message diff --git a/libnetwork/client/types.go b/libnetwork/client/types.go index 64e52a7e1a..18cd288253 100644 --- a/libnetwork/client/types.go +++ b/libnetwork/client/types.go @@ -42,11 +42,9 @@ type networkCreate struct { // serviceCreate represents the body of the "publish service" http request message type serviceCreate struct { - Name string `json:"name"` - MyAliases []string `json:"my_aliases"` - Network string `json:"network_name"` - ExposedPorts []types.TransportPort `json:"exposed_ports"` - PortMapping []types.PortBinding `json:"port_mapping"` + Name string `json:"name"` + MyAliases []string `json:"my_aliases"` + Network string `json:"network_name"` } // serviceDelete represents the body of the "unpublish service" http request message @@ -63,14 +61,16 @@ type serviceAttach struct { // SandboxCreate is the body of the "post /sandboxes" http request message type SandboxCreate struct { - ContainerID string `json:"container_id"` - HostName string `json:"host_name"` - DomainName string `json:"domain_name"` - HostsPath string `json:"hosts_path"` - ResolvConfPath string `json:"resolv_conf_path"` - DNS []string `json:"dns"` - ExtraHosts []extraHost `json:"extra_hosts"` - UseDefaultSandbox bool `json:"use_default_sandbox"` + ContainerID string `json:"container_id"` + HostName string `json:"host_name"` + DomainName string `json:"domain_name"` + HostsPath string `json:"hosts_path"` + ResolvConfPath string `json:"resolv_conf_path"` + DNS []string `json:"dns"` + ExtraHosts []extraHost `json:"extra_hosts"` + UseDefaultSandbox bool `json:"use_default_sandbox"` + ExposedPorts []types.TransportPort `json:"exposed_ports"` + PortMapping []types.PortBinding `json:"port_mapping"` } // extraHost represents the extra host object diff --git a/libnetwork/default_gateway.go b/libnetwork/default_gateway.go index 2df047a348..d8eb732701 100644 --- a/libnetwork/default_gateway.go +++ b/libnetwork/default_gateway.go @@ -3,7 +3,6 @@ package libnetwork import ( "fmt" - "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/types" ) @@ -28,15 +27,15 @@ var procGwNetwork = make(chan (bool), 1) - its deleted when an endpoint with GW joins the container */ -func (sb *sandbox) setupDefaultGW(srcEp *endpoint) error { - var createOptions []EndpointOption - c := srcEp.getNetwork().getController() +func (sb *sandbox) setupDefaultGW() error { // check if the conitainer already has a GW endpoint if ep := sb.getEndpointInGWNetwork(); ep != nil { return nil } + c := sb.controller + // Look for default gw network. In case of error (includes not found), // retry and create it if needed in a serialized execution. n, err := c.NetworkByName(libnGWNetwork) @@ -46,19 +45,7 @@ func (sb *sandbox) setupDefaultGW(srcEp *endpoint) error { } } - if opt, ok := srcEp.generic[netlabel.PortMap]; ok { - if pb, ok := opt.([]types.PortBinding); ok { - createOptions = append(createOptions, CreateOptionPortMapping(pb)) - } - } - - if opt, ok := srcEp.generic[netlabel.ExposedPorts]; ok { - if exp, ok := opt.([]types.TransportPort); ok { - createOptions = append(createOptions, CreateOptionExposedPorts(exp)) - } - } - - createOptions = append(createOptions, CreateOptionAnonymous()) + createOptions := []EndpointOption{CreateOptionAnonymous()} eplen := gwEPlen if len(sb.containerID) < gwEPlen { @@ -74,9 +61,13 @@ func (sb *sandbox) setupDefaultGW(srcEp *endpoint) error { if err := epLocal.sbJoin(sb); err != nil { return fmt.Errorf("container %s: endpoint join on GW Network failed: %v", sb.containerID, err) } + return nil } +// If present, removes the endpoint connecting the sandbox to the default gw network. +// Unless it is the endpoint designated to provide the external connectivity. +// If the sandbox is being deleted, removes the endpoint unconditionally. func (sb *sandbox) clearDefaultGW() error { var ep *endpoint @@ -84,6 +75,10 @@ func (sb *sandbox) clearDefaultGW() error { return nil } + if ep == sb.getGatewayEndpoint() && !sb.inDelete { + return nil + } + if err := ep.sbLeave(sb, false); err != nil { return fmt.Errorf("container %s: endpoint leaving GW Network failed: %v", sb.containerID, err) } @@ -98,7 +93,7 @@ func (sb *sandbox) needDefaultGW() bool { for _, ep := range sb.getConnectedEndpoints() { if ep.endpointInGWNetwork() { - continue + return false } if ep.getNetwork().Type() == "null" || ep.getNetwork().Type() == "host" { continue @@ -165,3 +160,16 @@ func (c *controller) defaultGwNetwork() (Network, error) { } return n, err } + +// Returns the endpoint which is providing external connectivity to the sandbox +func (sb *sandbox) getGatewayEndpoint() *endpoint { + for _, ep := range sb.getConnectedEndpoints() { + if ep.getNetwork().Type() == "null" || ep.getNetwork().Type() == "host" { + continue + } + if len(ep.Gateway()) != 0 { + return ep + } + } + return nil +} diff --git a/libnetwork/driverapi/driverapi.go b/libnetwork/driverapi/driverapi.go index 3d1ff2fb9e..4ea5e11278 100644 --- a/libnetwork/driverapi/driverapi.go +++ b/libnetwork/driverapi/driverapi.go @@ -42,6 +42,14 @@ type Driver interface { // Leave method is invoked when a Sandbox detaches from an endpoint. Leave(nid, eid string) error + // ProgramExternalConnectivity invokes the driver method which does the necessary + // programming to allow the external connectivity dictated by the passed options + ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error + + // RevokeExternalConnectivity aks the driver to remove any external connectivity + // programming that was done so far + RevokeExternalConnectivity(nid, eid string) error + // Type returns the the type of this driver, the network type this driver manages Type() string } diff --git a/libnetwork/drivers/bridge/bridge.go b/libnetwork/drivers/bridge/bridge.go index eb61d206a5..9c8996a70d 100644 --- a/libnetwork/drivers/bridge/bridge.go +++ b/libnetwork/drivers/bridge/bridge.go @@ -74,9 +74,7 @@ type networkConfiguration struct { // endpointConfiguration represents the user specified configuration for the sandbox endpoint type endpointConfiguration struct { - MacAddress net.HardwareAddr - PortBindings []types.PortBinding - ExposedPorts []types.TransportPort + MacAddress net.HardwareAddr } // containerConfiguration represents the user specified configuration for a container @@ -85,6 +83,12 @@ type containerConfiguration struct { ChildEndpoints []string } +// cnnectivityConfiguration represents the user specified configuration regarding the external connectivity +type connectivityConfiguration struct { + PortBindings []types.PortBinding + ExposedPorts []types.TransportPort +} + type bridgeEndpoint struct { id string srcName string @@ -93,6 +97,7 @@ type bridgeEndpoint struct { macAddress net.HardwareAddr config *endpointConfiguration // User specified parameters containerConfig *containerConfiguration + extConnConfig *connectivityConfiguration portMapping []types.PortBinding // Operation port bindings } @@ -1004,12 +1009,6 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, } } - // Program any required port mapping and store them in the endpoint - endpoint.portMapping, err = n.allocatePorts(epConfig, endpoint, config.DefaultBindingIP, d.config.EnableUserlandProxy) - if err != nil { - return err - } - return nil } @@ -1064,9 +1063,6 @@ func (d *driver) DeleteEndpoint(nid, eid string) error { } }() - // Remove port mappings. Do not stop endpoint delete on unmap failure - n.releasePorts(ep) - // Try removal of link. Discard error: it is a best effort. // Also make sure defer does not see this error either. if link, err := netlink.LinkByName(ep.srcName); err == nil { @@ -1107,10 +1103,10 @@ func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, erro m := make(map[string]interface{}) - if ep.config.ExposedPorts != nil { + if ep.extConnConfig != nil && ep.extConnConfig.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 := make([]types.TransportPort, 0, len(ep.extConnConfig.ExposedPorts)) + for _, tp := range ep.extConnConfig.ExposedPorts { epc = append(epc, tp.GetCopy()) } m[netlabel.ExposedPorts] = epc @@ -1150,6 +1146,11 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, return EndpointNotFoundError(eid) } + endpoint.containerConfig, err = parseContainerOptions(options) + if err != nil { + return err + } + iNames := jinfo.InterfaceName() err = iNames.SetNames(endpoint.srcName, containerVethPrefix) if err != nil { @@ -1166,10 +1167,6 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, return err } - if !network.config.EnableICC { - return d.link(network, endpoint, options, true) - } - return nil } @@ -1192,32 +1189,87 @@ func (d *driver) Leave(nid, eid string) error { } if !network.config.EnableICC { - return d.link(network, endpoint, nil, false) + if err = d.link(network, endpoint, false); err != nil { + return err + } } return nil } -func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options map[string]interface{}, enable bool) error { - var ( - cc *containerConfiguration - err error - ) +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + defer osl.InitOSContext()() - if enable { - cc, err = parseContainerOptions(options) - if err != nil { - return err - } - } else { - cc = endpoint.containerConfig + network, err := d.getNetwork(nid) + if err != nil { + return err } + endpoint, err := network.getEndpoint(eid) + if err != nil { + return err + } + + if endpoint == nil { + return EndpointNotFoundError(eid) + } + + endpoint.extConnConfig, err = parseConnectivityOptions(options) + if err != nil { + return err + } + + // Program any required port mapping and store them in the endpoint + endpoint.portMapping, err = network.allocatePorts(endpoint, network.config.DefaultBindingIP, d.config.EnableUserlandProxy) + if err != nil { + return err + } + + if !network.config.EnableICC { + return d.link(network, endpoint, true) + } + + return nil +} + +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + defer osl.InitOSContext()() + + network, err := d.getNetwork(nid) + if err != nil { + return err + } + + endpoint, err := network.getEndpoint(eid) + if err != nil { + return err + } + + if endpoint == nil { + return EndpointNotFoundError(eid) + } + + err = network.releasePorts(endpoint) + if err != nil { + logrus.Warn(err) + } + + return nil +} + +func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, enable bool) error { + var err error + + cc := endpoint.containerConfig if cc == nil { return nil } + ec := endpoint.extConnConfig + if ec == nil { + return nil + } - if endpoint.config != nil && endpoint.config.ExposedPorts != nil { + if ec.ExposedPorts != nil { for _, p := range cc.ParentEndpoints { var parentEndpoint *bridgeEndpoint parentEndpoint, err = network.getEndpoint(p) @@ -1231,7 +1283,7 @@ func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options l := newLink(parentEndpoint.addr.IP.String(), endpoint.addr.IP.String(), - endpoint.config.ExposedPorts, network.config.BridgeName) + ec.ExposedPorts, network.config.BridgeName) if enable { err = l.Enable() if err != nil { @@ -1258,13 +1310,13 @@ func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options err = InvalidEndpointIDError(c) return err } - if childEndpoint.config == nil || childEndpoint.config.ExposedPorts == nil { + if childEndpoint.extConnConfig == nil || childEndpoint.extConnConfig.ExposedPorts == nil { continue } l := newLink(endpoint.addr.IP.String(), childEndpoint.addr.IP.String(), - childEndpoint.config.ExposedPorts, network.config.BridgeName) + childEndpoint.extConnConfig.ExposedPorts, network.config.BridgeName) if enable { err = l.Enable() if err != nil { @@ -1280,10 +1332,6 @@ func (d *driver) link(network *bridgeNetwork, endpoint *bridgeEndpoint, options } } - if enable { - endpoint.containerConfig = cc - } - return nil } @@ -1316,22 +1364,6 @@ func parseEndpointOptions(epOptions map[string]interface{}) (*endpointConfigurat } } - if opt, ok := epOptions[netlabel.PortMap]; ok { - if bs, ok := opt.([]types.PortBinding); ok { - ec.PortBindings = bs - } else { - return nil, &ErrInvalidEndpointConfig{} - } - } - - if opt, ok := epOptions[netlabel.ExposedPorts]; ok { - if ports, ok := opt.([]types.TransportPort); ok { - ec.ExposedPorts = ports - } else { - return nil, &ErrInvalidEndpointConfig{} - } - } - return ec, nil } @@ -1339,22 +1371,52 @@ func parseContainerOptions(cOptions map[string]interface{}) (*containerConfigura if cOptions == nil { return nil, nil } - genericData := cOptions[netlabel.GenericData] - if genericData == nil { - return nil, nil - } - switch opt := genericData.(type) { - case options.Generic: - opaqueConfig, err := options.GenerateFromModel(opt, &containerConfiguration{}) - if err != nil { - return nil, err + + cc := &containerConfiguration{} + + if opt, ok := cOptions[ParentEndpoints]; ok { + if pe, ok := opt.([]string); ok { + cc.ParentEndpoints = pe + } else { + return nil, types.BadRequestErrorf("Invalid parent endpoints data in sandbox configuration: %v", opt) } - return opaqueConfig.(*containerConfiguration), nil - case *containerConfiguration: - return opt, nil - default: + } + + if opt, ok := cOptions[ChildEndpoints]; ok { + if ce, ok := opt.([]string); ok { + cc.ChildEndpoints = ce + } else { + return nil, types.BadRequestErrorf("Invalid child endpoints data in sandbox configuration: %v", opt) + } + } + + return cc, nil +} + +func parseConnectivityOptions(cOptions map[string]interface{}) (*connectivityConfiguration, error) { + if cOptions == nil { return nil, nil } + + cc := &connectivityConfiguration{} + + if opt, ok := cOptions[netlabel.PortMap]; ok { + if pb, ok := opt.([]types.PortBinding); ok { + cc.PortBindings = pb + } else { + return nil, types.BadRequestErrorf("Invalid port mapping data in connectivity configuration: %v", opt) + } + } + + if opt, ok := cOptions[netlabel.ExposedPorts]; ok { + if ports, ok := opt.([]types.TransportPort); ok { + cc.ExposedPorts = ports + } else { + return nil, types.BadRequestErrorf("Invalid exposed ports data in connectivity configuration: %v", opt) + } + } + + return cc, nil } func electMacAddress(epConfig *endpointConfiguration, ip net.IP) net.HardwareAddr { diff --git a/libnetwork/drivers/bridge/bridge_test.go b/libnetwork/drivers/bridge/bridge_test.go index f4d6923c53..3c74eba8ce 100644 --- a/libnetwork/drivers/bridge/bridge_test.go +++ b/libnetwork/drivers/bridge/bridge_test.go @@ -463,16 +463,25 @@ func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) { t.Fatalf("Failed to create bridge: %v", err) } - portMappings := getPortMapping() - epOptions := make(map[string]interface{}) - epOptions[netlabel.PortMap] = portMappings + sbOptions := make(map[string]interface{}) + sbOptions[netlabel.PortMap] = getPortMapping() te := newTestEndpoint(ipdList[0].Pool, 11) - err = d.CreateEndpoint("net1", "ep1", te.Interface(), epOptions) + err = d.CreateEndpoint("net1", "ep1", te.Interface(), nil) if err != nil { t.Fatalf("Failed to create an endpoint : %s", err.Error()) } + err = d.Join("net1", "ep1", "sbox", te, sbOptions) + if err != nil { + t.Fatalf("Failed to join the endpoint: %v", err) + } + + err = d.ProgramExternalConnectivity("net1", "ep1", sbOptions) + if err != nil { + t.Fatalf("Failed to program external connectivity: %v", err) + } + network, ok := d.networks["net1"] if !ok { t.Fatalf("Cannot find network %s inside driver", "net1") @@ -499,10 +508,15 @@ func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) { } } - // Cleanup as host ports are there - err = network.releasePorts(ep) + err = d.RevokeExternalConnectivity("net1", "ep1") if err != nil { - t.Fatalf("Failed to release mapped ports: %v", err) + t.Fatal(err) + } + + // release host mapped ports + err = d.Leave("net1", "ep1") + if err != nil { + t.Fatal(err) } } @@ -594,16 +608,26 @@ func TestLinkContainers(t *testing.T) { t.Fatalf("Failed to create bridge: %v", err) } - exposedPorts := getExposedPorts() - epOptions := make(map[string]interface{}) - epOptions[netlabel.ExposedPorts] = exposedPorts - te1 := newTestEndpoint(ipdList[0].Pool, 11) - err = d.CreateEndpoint("net1", "ep1", te1.Interface(), epOptions) + err = d.CreateEndpoint("net1", "ep1", te1.Interface(), nil) if err != nil { t.Fatalf("Failed to create an endpoint : %s", err.Error()) } + exposedPorts := getExposedPorts() + sbOptions := make(map[string]interface{}) + sbOptions[netlabel.ExposedPorts] = exposedPorts + + err = d.Join("net1", "ep1", "sbox", te1, sbOptions) + if err != nil { + t.Fatalf("Failed to join the endpoint: %v", err) + } + + err = d.ProgramExternalConnectivity("net1", "ep1", sbOptions) + if err != nil { + t.Fatalf("Failed to program external connectivity: %v", err) + } + addr1 := te1.iface.addr if addr1.IP.To4() == nil { t.Fatalf("No Ipv4 address assigned to the endpoint: ep1") @@ -620,16 +644,19 @@ func TestLinkContainers(t *testing.T) { t.Fatalf("No Ipv4 address assigned to the endpoint: ep2") } - ce := []string{"ep1"} - cConfig := &containerConfiguration{ChildEndpoints: ce} - genericOption = make(map[string]interface{}) - genericOption[netlabel.GenericData] = cConfig + sbOptions = make(map[string]interface{}) + sbOptions[ChildEndpoints] = []string{"ep1"} - err = d.Join("net1", "ep2", "", te2, genericOption) + err = d.Join("net1", "ep2", "", te2, sbOptions) if err != nil { t.Fatalf("Failed to link ep1 and ep2") } + err = d.ProgramExternalConnectivity("net1", "ep2", sbOptions) + if err != nil { + t.Fatalf("Failed to program external connectivity: %v", err) + } + out, err := iptables.Raw("-L", DockerChain) for _, pm := range exposedPorts { regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port) @@ -646,6 +673,11 @@ func TestLinkContainers(t *testing.T) { } } + err = d.RevokeExternalConnectivity("net1", "ep2") + if err != nil { + t.Fatalf("Failed to revoke external connectivity: %v", err) + } + err = d.Leave("net1", "ep2") if err != nil { t.Fatalf("Failed to unlink ep1 and ep2") @@ -668,12 +700,14 @@ func TestLinkContainers(t *testing.T) { } // Error condition test with an invalid endpoint-id "ep4" - ce = []string{"ep1", "ep4"} - cConfig = &containerConfiguration{ChildEndpoints: ce} - genericOption = make(map[string]interface{}) - genericOption[netlabel.GenericData] = cConfig + sbOptions = make(map[string]interface{}) + sbOptions[ChildEndpoints] = []string{"ep1", "ep4"} - err = d.Join("net1", "ep2", "", te2, genericOption) + err = d.Join("net1", "ep2", "", te2, sbOptions) + if err != nil { + t.Fatal(err) + } + err = d.ProgramExternalConnectivity("net1", "ep2", sbOptions) if err != nil { out, err = iptables.Raw("-L", DockerChain) for _, pm := range exposedPorts { diff --git a/libnetwork/drivers/bridge/labels.go b/libnetwork/drivers/bridge/labels.go index 7447bd3f93..ebdb20d720 100644 --- a/libnetwork/drivers/bridge/labels.go +++ b/libnetwork/drivers/bridge/labels.go @@ -15,4 +15,10 @@ const ( // DefaultBridge label DefaultBridge = "com.docker.network.bridge.default_bridge" + + // ChildEndpoints for links + ChildEndpoints = "com.docker.network.bridge.child_endpoints" + + // ParentEndpoints for links + ParentEndpoints = "com.docker.network.bridge.parent_endpoints" ) diff --git a/libnetwork/drivers/bridge/port_mapping.go b/libnetwork/drivers/bridge/port_mapping.go index 4dab8a0c89..965cc9a039 100644 --- a/libnetwork/drivers/bridge/port_mapping.go +++ b/libnetwork/drivers/bridge/port_mapping.go @@ -14,8 +14,8 @@ var ( defaultBindingIP = net.IPv4(0, 0, 0, 0) ) -func (n *bridgeNetwork) allocatePorts(epConfig *endpointConfiguration, ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { - if epConfig == nil || epConfig.PortBindings == nil { +func (n *bridgeNetwork) allocatePorts(ep *bridgeEndpoint, reqDefBindIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { + if ep.extConnConfig == nil || ep.extConnConfig.PortBindings == nil { return nil, nil } @@ -24,7 +24,7 @@ func (n *bridgeNetwork) allocatePorts(epConfig *endpointConfiguration, ep *bridg defHostIP = reqDefBindIP } - return n.allocatePortsInternal(epConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled) + return n.allocatePortsInternal(ep.extConnConfig.PortBindings, ep.addr.IP, defHostIP, ulPxyEnabled) } func (n *bridgeNetwork) allocatePortsInternal(bindings []types.PortBinding, containerIP, defHostIP net.IP, ulPxyEnabled bool) ([]types.PortBinding, error) { diff --git a/libnetwork/drivers/bridge/port_mapping_test.go b/libnetwork/drivers/bridge/port_mapping_test.go index 8aa2bc0228..3e76aac59d 100644 --- a/libnetwork/drivers/bridge/port_mapping_test.go +++ b/libnetwork/drivers/bridge/port_mapping_test.go @@ -35,8 +35,8 @@ func TestPortMappingConfig(t *testing.T) { 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 + sbOptions := make(map[string]interface{}) + sbOptions[netlabel.PortMap] = portBindings netConfig := &networkConfiguration{ BridgeName: DefaultBridgeName, @@ -51,11 +51,19 @@ func TestPortMappingConfig(t *testing.T) { } te := newTestEndpoint(ipdList[0].Pool, 11) - err = d.CreateEndpoint("dummy", "ep1", te.Interface(), epOptions) + err = d.CreateEndpoint("dummy", "ep1", te.Interface(), nil) if err != nil { t.Fatalf("Failed to create the endpoint: %s", err.Error()) } + if err = d.Join("dummy", "ep1", "sbox", te, sbOptions); err != nil { + t.Fatalf("Failed to join the endpoint: %v", err) + } + + if err = d.ProgramExternalConnectivity("dummy", "ep1", sbOptions); err != nil { + t.Fatalf("Failed to program external connectivity: %v", err) + } + network, ok := d.networks["dummy"] if !ok { t.Fatalf("Cannot find network %s inside driver", "dummy") @@ -73,8 +81,14 @@ func TestPortMappingConfig(t *testing.T) { t.Fatalf("operational port mapping data not found on bridgeEndpoint") } - err = network.releasePorts(ep) + // release host mapped ports + err = d.Leave("dummy", "ep1") if err != nil { - t.Fatalf("Failed to release mapped ports: %v", err) + t.Fatal(err) + } + + err = d.RevokeExternalConnectivity("dummy", "ep1") + if err != nil { + t.Fatal(err) } } diff --git a/libnetwork/drivers/host/host.go b/libnetwork/drivers/host/host.go index 66fd9ebdb8..bbf59c204c 100644 --- a/libnetwork/drivers/host/host.go +++ b/libnetwork/drivers/host/host.go @@ -63,6 +63,14 @@ func (d *driver) Leave(nid, eid string) error { return nil } +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + return nil +} + +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + return nil +} + func (d *driver) Type() string { return networkType } diff --git a/libnetwork/drivers/null/null.go b/libnetwork/drivers/null/null.go index b64c9e995d..ecc64d2db3 100644 --- a/libnetwork/drivers/null/null.go +++ b/libnetwork/drivers/null/null.go @@ -63,6 +63,14 @@ func (d *driver) Leave(nid, eid string) error { return nil } +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + return nil +} + +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + return nil +} + func (d *driver) Type() string { return networkType } diff --git a/libnetwork/drivers/overlay/ov_network.go b/libnetwork/drivers/overlay/ov_network.go index 306806a8b8..1bf91e3f21 100644 --- a/libnetwork/drivers/overlay/ov_network.go +++ b/libnetwork/drivers/overlay/ov_network.go @@ -114,6 +114,14 @@ func (d *driver) DeleteNetwork(nid string) error { return n.releaseVxlanID() } +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + return nil +} + +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + return nil +} + func (n *network) incEndpointCount() { n.Lock() defer n.Unlock() diff --git a/libnetwork/drivers/remote/api/api.go b/libnetwork/drivers/remote/api/api.go index 7dc877fc66..c40a80bb87 100644 --- a/libnetwork/drivers/remote/api/api.go +++ b/libnetwork/drivers/remote/api/api.go @@ -153,6 +153,29 @@ type LeaveResponse struct { Response } +// ProgramExternalConnectivityRequest describes the API for programming the external connectivity for the given endpoint. +type ProgramExternalConnectivityRequest struct { + NetworkID string + EndpointID string + Options map[string]interface{} +} + +// ProgramExternalConnectivityResponse is the answer to ProgramExternalConnectivityRequest. +type ProgramExternalConnectivityResponse struct { + Response +} + +// RevokeExternalConnectivityRequest describes the API for revoking the external connectivity for the given endpoint. +type RevokeExternalConnectivityRequest struct { + NetworkID string + EndpointID string +} + +// RevokeExternalConnectivityResponse is the answer to RevokeExternalConnectivityRequest. +type RevokeExternalConnectivityResponse struct { + Response +} + // DiscoveryNotification represents a discovery notification type DiscoveryNotification struct { DiscoveryType discoverapi.DiscoveryType diff --git a/libnetwork/drivers/remote/driver.go b/libnetwork/drivers/remote/driver.go index c55915ce97..32533533dd 100644 --- a/libnetwork/drivers/remote/driver.go +++ b/libnetwork/drivers/remote/driver.go @@ -3,6 +3,7 @@ package remote import ( "fmt" "net" + "strings" log "github.com/Sirupsen/logrus" "github.com/docker/docker/pkg/plugins" @@ -13,6 +14,10 @@ import ( "github.com/docker/libnetwork/types" ) +const ( + missingMethod = "404 page not found" +) + type driver struct { endpoint *plugins.Client networkType string @@ -247,6 +252,35 @@ func (d *driver) Leave(nid, eid string) error { return d.call("Leave", leave, &api.LeaveResponse{}) } +// ProgramExternalConnectivity is invoked to program the rules to allow external connectivity for the endpoint. +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + data := &api.ProgramExternalConnectivityRequest{ + NetworkID: nid, + EndpointID: eid, + Options: options, + } + err := d.call("ProgramExternalConnectivity", data, &api.ProgramExternalConnectivityResponse{}) + if err != nil && strings.Contains(err.Error(), missingMethod) { + // It is not mandatory yet to support this method + return nil + } + return err +} + +// RevokeExternalConnectivity method is invoked to remove any external connectivity programming related to the endpoint. +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + data := &api.RevokeExternalConnectivityRequest{ + NetworkID: nid, + EndpointID: eid, + } + err := d.call("RevokeExternalConnectivity", data, &api.RevokeExternalConnectivityResponse{}) + if err != nil && strings.Contains(err.Error(), missingMethod) { + // It is not mandatory yet to support this method + return nil + } + return err +} + func (d *driver) Type() string { return d.networkType } diff --git a/libnetwork/drivers/windows/windows.go b/libnetwork/drivers/windows/windows.go index 3039c79ec6..133047fec4 100644 --- a/libnetwork/drivers/windows/windows.go +++ b/libnetwork/drivers/windows/windows.go @@ -509,6 +509,14 @@ func (d *driver) Leave(nid, eid string) error { return nil } +func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + return nil +} + +func (d *driver) RevokeExternalConnectivity(nid, eid string) error { + return nil +} + func (d *driver) Type() string { return d.name } diff --git a/libnetwork/endpoint.go b/libnetwork/endpoint.go index 38506c82a8..3295a99eb4 100644 --- a/libnetwork/endpoint.go +++ b/libnetwork/endpoint.go @@ -359,22 +359,16 @@ func (ep *endpoint) Join(sbox Sandbox, options ...EndpointOption) error { sb.joinLeaveStart() defer sb.joinLeaveEnd() - return ep.sbJoin(sbox, options...) + return ep.sbJoin(sb, options...) } -func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error { - var err error - sb, ok := sbox.(*sandbox) - if !ok { - return types.BadRequestErrorf("not a valid Sandbox interface") - } - - network, err := ep.getNetworkFromStore() +func (ep *endpoint) sbJoin(sb *sandbox, options ...EndpointOption) error { + n, err := ep.getNetworkFromStore() if err != nil { return fmt.Errorf("failed to get network from store during join: %v", err) } - ep, err = network.getEndpointFromStore(ep.ID()) + ep, err = n.getEndpointFromStore(ep.ID()) if err != nil { return fmt.Errorf("failed to get endpoint from store during join: %v", err) } @@ -384,11 +378,8 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error { ep.Unlock() return types.ForbiddenErrorf("another container is attached to the same network endpoint") } - ep.Unlock() - - ep.Lock() - ep.network = network - ep.sandboxID = sbox.ID() + ep.network = n + ep.sandboxID = sb.ID() ep.joinInfo = &endpointJoinInfo{} epid := ep.id ep.Unlock() @@ -400,32 +391,29 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error { } }() - network.Lock() - nid := network.id - network.Unlock() + nid := n.ID() ep.processOptions(options...) - driver, err := network.driver(true) + d, err := n.driver(true) if err != nil { return fmt.Errorf("failed to join endpoint: %v", err) } - err = driver.Join(nid, epid, sbox.Key(), ep, sbox.Labels()) + err = d.Join(nid, epid, sb.Key(), ep, sb.Labels()) if err != nil { return err } defer func() { if err != nil { - // Do not alter global err variable, it's needed by the previous defer - if err := driver.Leave(nid, epid); err != nil { + if err := d.Leave(nid, epid); err != nil { log.Warnf("driver leave failed while rolling back join: %v", err) } } }() // Watch for service records - network.getController().watchSvcRecord(ep) + n.getController().watchSvcRecord(ep) address := "" if ip := ep.getFirstInterfaceAddress(); ip != nil { @@ -434,27 +422,23 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error { if err = sb.updateHostsFile(address); err != nil { return err } - if err = sb.updateDNS(network.enableIPv6); err != nil { + if err = sb.updateDNS(n.enableIPv6); err != nil { return err } - if err = network.getController().updateToStore(ep); err != nil { + if err = n.getController().updateToStore(ep); err != nil { return err } + // Current endpoint providing external connectivity for the sandbox + extEp := sb.getGatewayEndpoint() + sb.Lock() heap.Push(&sb.endpoints, ep) sb.Unlock() defer func() { if err != nil { - for i, e := range sb.getConnectedEndpoints() { - if e == ep { - sb.Lock() - heap.Remove(&sb.endpoints, i) - sb.Unlock() - return - } - } + sb.removeEndpoint(ep) } }() @@ -463,9 +447,29 @@ func (ep *endpoint) sbJoin(sbox Sandbox, options ...EndpointOption) error { } if sb.needDefaultGW() { - return sb.setupDefaultGW(ep) + return sb.setupDefaultGW() } - return nil + + moveExtConn := sb.getGatewayEndpoint() != extEp + + if moveExtConn { + if extEp != nil { + log.Debugf("Revoking external connectivity on endpoint %s (%s)", extEp.Name(), extEp.ID()) + if err := d.RevokeExternalConnectivity(extEp.network.ID(), extEp.ID()); err != nil { + log.Warnf("driver failed revoking external connectivity on endpoint %s (%s): %v", + extEp.Name(), extEp.ID(), err) + } + } + if !n.internal { + log.Debugf("Programming external connectivity on endpoint %s (%s)", ep.Name(), ep.ID()) + if err := d.ProgramExternalConnectivity(n.ID(), ep.ID(), sb.Labels()); err != nil { + log.Warnf("driver failed programming external connectivity on endpoint %s (%s): %v", + extEp.Name(), extEp.ID(), err) + } + } + } + + return sb.clearDefaultGW() } func (ep *endpoint) rename(name string) error { @@ -533,15 +537,10 @@ func (ep *endpoint) Leave(sbox Sandbox, options ...EndpointOption) error { sb.joinLeaveStart() defer sb.joinLeaveEnd() - return ep.sbLeave(sbox, false, options...) + return ep.sbLeave(sb, false, options...) } -func (ep *endpoint) sbLeave(sbox Sandbox, force bool, options ...EndpointOption) error { - sb, ok := sbox.(*sandbox) - if !ok { - return types.BadRequestErrorf("not a valid Sandbox interface") - } - +func (ep *endpoint) sbLeave(sb *sandbox, force bool, options ...EndpointOption) error { n, err := ep.getNetworkFromStore() if err != nil { return fmt.Errorf("failed to get network from store during leave: %v", err) @@ -559,8 +558,8 @@ func (ep *endpoint) sbLeave(sbox Sandbox, force bool, options ...EndpointOption) if sid == "" { return types.ForbiddenErrorf("cannot leave endpoint with no attached sandbox") } - if sid != sbox.ID() { - return types.ForbiddenErrorf("unexpected sandbox ID in leave request. Expected %s. Got %s", ep.sandboxID, sbox.ID()) + if sid != sb.ID() { + return types.ForbiddenErrorf("unexpected sandbox ID in leave request. Expected %s. Got %s", ep.sandboxID, sb.ID()) } ep.processOptions(options...) @@ -575,7 +574,19 @@ func (ep *endpoint) sbLeave(sbox Sandbox, force bool, options ...EndpointOption) ep.network = n ep.Unlock() + // Current endpoint providing external connectivity to the sandbox + extEp := sb.getGatewayEndpoint() + moveExtConn := extEp != nil && (extEp.ID() == ep.ID()) + if d != nil { + if moveExtConn { + log.Debugf("Revoking external connectivity on endpoint %s (%s)", ep.Name(), ep.ID()) + if err := d.RevokeExternalConnectivity(n.id, ep.id); err != nil { + log.Warnf("driver failed removing external connectivity on endpoint %s (%s): %v", + ep.Name(), ep.ID(), err) + } + } + if err := d.Leave(n.id, ep.id); err != nil { if _, ok := err.(types.MaskableError); !ok { log.Warnf("driver error disconnecting container %s : %v", ep.name, err) @@ -597,7 +608,24 @@ func (ep *endpoint) sbLeave(sbox Sandbox, force bool, options ...EndpointOption) } sb.deleteHostsEntries(n.getSvcRecords(ep)) - return nil + if !sb.inDelete && sb.needDefaultGW() { + if sb.getEPwithoutGateway() == nil { + return fmt.Errorf("endpoint without GW expected, but not found") + } + return sb.setupDefaultGW() + } + + // New endpoint providing external connectivity for the sandbox + extEp = sb.getGatewayEndpoint() + if moveExtConn && extEp != nil { + log.Debugf("Programming external connectivity on endpoint %s (%s)", extEp.Name(), extEp.ID()) + if err := d.ProgramExternalConnectivity(extEp.network.ID(), extEp.ID(), sb.Labels()); err != nil { + log.Warnf("driver failed programming external connectivity on endpoint %s: (%s) %v", + extEp.Name(), extEp.ID(), err) + } + } + + return sb.clearDefaultGW() } func (n *network) validateForceDelete(locator string) error { @@ -643,7 +671,7 @@ func (ep *endpoint) Delete(force bool) error { } if sb != nil { - if e := ep.sbLeave(sb, force); e != nil { + if e := ep.sbLeave(sb.(*sandbox), force); e != nil { log.Warnf("failed to leave sandbox for endpoint %s : %v", name, e) } } diff --git a/libnetwork/libnetwork_internal_test.go b/libnetwork/libnetwork_internal_test.go index b852de5e1b..c2e008ad44 100644 --- a/libnetwork/libnetwork_internal_test.go +++ b/libnetwork/libnetwork_internal_test.go @@ -455,3 +455,9 @@ func (b *badDriver) DiscoverDelete(dType discoverapi.DiscoveryType, data interfa func (b *badDriver) Type() string { return badDriverName } +func (b *badDriver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error { + return nil +} +func (b *badDriver) RevokeExternalConnectivity(nid, eid string) error { + return nil +} diff --git a/libnetwork/libnetwork_test.go b/libnetwork/libnetwork_test.go index 44b636f10d..15a163c6c2 100644 --- a/libnetwork/libnetwork_test.go +++ b/libnetwork/libnetwork_test.go @@ -283,8 +283,28 @@ func TestBridge(t *testing.T) { if err != nil { t.Fatal(err) } + defer func() { + if err := network.Delete(); err != nil { + t.Fatal(err) + } + }() - ep, err := network.CreateEndpoint("testep", libnetwork.CreateOptionPortMapping(getPortMapping())) + ep, err := network.CreateEndpoint("testep") + if err != nil { + t.Fatal(err) + } + + sb, err := controller.NewSandbox(containerID, libnetwork.OptionPortMapping(getPortMapping())) + if err != nil { + t.Fatal(err) + } + defer func() { + if err := sb.Delete(); err != nil { + t.Fatal(err) + } + }() + + err = ep.Join(sb) if err != nil { t.Fatal(err) } @@ -304,14 +324,6 @@ func TestBridge(t *testing.T) { if len(pm) != 5 { t.Fatalf("Incomplete data for port mapping in endpoint operational data: %d", len(pm)) } - - if err := ep.Delete(false); err != nil { - t.Fatal(err) - } - - if err := network.Delete(); err != nil { - t.Fatal(err) - } } // Testing IPV6 from MAC address diff --git a/libnetwork/sandbox.go b/libnetwork/sandbox.go index e0275383cf..35adea2b2f 100644 --- a/libnetwork/sandbox.go +++ b/libnetwork/sandbox.go @@ -10,6 +10,7 @@ import ( log "github.com/Sirupsen/logrus" "github.com/docker/libnetwork/etchosts" + "github.com/docker/libnetwork/netlabel" "github.com/docker/libnetwork/osl" "github.com/docker/libnetwork/types" ) @@ -118,6 +119,7 @@ type containerConfig struct { useDefaultSandBox bool useExternalKey bool prio int // higher the value, more the priority + exposedPorts []types.TransportPort } func (sb *sandbox) ID() string { @@ -136,7 +138,13 @@ func (sb *sandbox) Key() string { } func (sb *sandbox) Labels() map[string]interface{} { - return sb.config.generic + sb.Lock() + sb.Unlock() + opts := make(map[string]interface{}, len(sb.config.generic)) + for k, v := range sb.config.generic { + opts[k] = v + } + return opts } func (sb *sandbox) Statistics() (map[string]*types.InterfaceStatistics, error) { @@ -329,6 +337,18 @@ func (sb *sandbox) getConnectedEndpoints() []*endpoint { return eps } +func (sb *sandbox) removeEndpoint(ep *endpoint) { + sb.Lock() + defer sb.Unlock() + + for i, e := range sb.endpoints { + if e == ep { + heap.Remove(&sb.endpoints, i) + return + } + } +} + func (sb *sandbox) getEndpoint(id string) *endpoint { sb.Lock() defer sb.Unlock() @@ -624,14 +644,9 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error { } } - for _, gwep := range sb.getConnectedEndpoints() { - if len(gwep.Gateway()) > 0 { - if gwep != ep { - break - } - if err := sb.updateGateway(gwep); err != nil { - return err - } + if ep == sb.getGatewayEndpoint() { + if err := sb.updateGateway(ep); err != nil { + return err } } @@ -742,6 +757,13 @@ func (sb *sandbox) joinLeaveEnd() { } } +func (sb *sandbox) hasPortConfigs() bool { + opts := sb.Labels() + _, hasExpPorts := opts[netlabel.ExposedPorts] + _, hasPortMaps := opts[netlabel.PortMap] + return hasExpPorts || hasPortMaps +} + // OptionHostname function returns an option setter for hostname option to // be passed to NewSandbox method. func OptionHostname(name string) SandboxOption { @@ -851,7 +873,42 @@ func OptionUseExternalKey() SandboxOption { // net container creation method. Container Labels are a good example. func OptionGeneric(generic map[string]interface{}) SandboxOption { return func(sb *sandbox) { - sb.config.generic = generic + if sb.config.generic == nil { + sb.config.generic = make(map[string]interface{}, len(generic)) + } + for k, v := range generic { + sb.config.generic[k] = v + } + } +} + +// OptionExposedPorts function returns an option setter for the container exposed +// ports option to be passed to container Create method. +func OptionExposedPorts(exposedPorts []types.TransportPort) SandboxOption { + return func(sb *sandbox) { + if sb.config.generic == nil { + sb.config.generic = make(map[string]interface{}) + } + // Defensive copy + eps := make([]types.TransportPort, len(exposedPorts)) + copy(eps, exposedPorts) + // Store endpoint label and in generic because driver needs it + sb.config.exposedPorts = eps + sb.config.generic[netlabel.ExposedPorts] = eps + } +} + +// OptionPortMapping function returns an option setter for the mapping +// ports option to be passed to container Create method. +func OptionPortMapping(portBindings []types.PortBinding) SandboxOption { + return func(sb *sandbox) { + if sb.config.generic == nil { + sb.config.generic = make(map[string]interface{}) + } + // Store a copy of the bindings as generic data to pass to the driver + pbs := make([]types.PortBinding, len(portBindings)) + copy(pbs, portBindings) + sb.config.generic[netlabel.PortMap] = pbs } }