Move exposed ports from Endpoint to Sandbox

Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
Alessandro Boch 2015-12-07 14:45:51 -08:00
parent b532754b19
commit 1638fbdf27
21 changed files with 552 additions and 236 deletions

View File

@ -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))
}

View File

@ -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)
}

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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"
)

View File

@ -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) {

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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()

View File

@ -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

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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
}
}