Sfoglia il codice sorgente

Support to provide external key to sandbox

Signed-off-by: Madhu Venugopal <madhu@docker.com>
Madhu Venugopal 9 anni fa
parent
commit
f59502e1bd

+ 3 - 0
libnetwork/api/api.go

@@ -245,6 +245,9 @@ func (sc *sandboxCreate) parseOptions() []libnetwork.SandboxOption {
 	if sc.UseDefaultSandbox {
 		setFctList = append(setFctList, libnetwork.OptionUseDefaultSandbox())
 	}
+	if sc.UseExternalKey {
+		setFctList = append(setFctList, libnetwork.OptionUseExternalKey())
+	}
 	if sc.DNS != nil {
 		for _, d := range sc.DNS {
 			setFctList = append(setFctList, libnetwork.OptionDNS(d))

+ 1 - 0
libnetwork/api/types.go

@@ -57,6 +57,7 @@ type sandboxCreate struct {
 	DNS               []string    `json:"dns"`
 	ExtraHosts        []extraHost `json:"extra_hosts"`
 	UseDefaultSandbox bool        `json:"use_default_sandbox"`
+	UseExternalKey    bool        `json:"use_external_key"`
 }
 
 // endpointJoin represents the expected body of the "join endpoint" or "leave endpoint" http request messages

+ 1 - 1
libnetwork/controller.go

@@ -414,7 +414,7 @@ func (c *controller) NewSandbox(containerID string, options ...SandboxOption) (S
 		return nil, err
 	}
 
-	if sb.osSbox == nil {
+	if sb.osSbox == nil && !sb.config.useExternalKey {
 		if sb.osSbox, err = osl.NewSandbox(sb.Key(), !sb.config.useDefaultSandBox); err != nil {
 			return nil, fmt.Errorf("failed to create new osl sandbox: %v", err)
 		}

+ 17 - 1
libnetwork/endpoint.go

@@ -1,6 +1,7 @@
 package libnetwork
 
 import (
+	"container/heap"
 	"encoding/json"
 	"fmt"
 	"net"
@@ -289,10 +290,25 @@ func (ep *endpoint) Join(sbox Sandbox, options ...EndpointOption) error {
 		return err
 	}
 
+	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
+				}
+			}
+		}
+	}()
+
 	if err = sb.populateNetworkResources(ep); err != nil {
 		return err
 	}
-
 	return nil
 }
 

+ 111 - 0
libnetwork/libnetwork_test.go

@@ -1188,6 +1188,117 @@ func (f *fakeSandbox) Delete() error {
 	return nil
 }
 
+func (f *fakeSandbox) SetKey(key string) error {
+	return nil
+}
+
+func TestExternalKey(t *testing.T) {
+	if !netutils.IsRunningInContainer() {
+		defer osl.SetupTestOSContext(t)()
+	}
+
+	n, err := createTestNetwork(bridgeNetType, "testnetwork", options.Generic{
+		netlabel.GenericData: options.Generic{
+			"BridgeName":            "testnetwork",
+			"AllowNonDefaultBridge": true,
+		},
+	})
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		if err := n.Delete(); err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	ep, err := n.CreateEndpoint("ep1")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		err = ep.Delete()
+		if err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	ep2, err := n.CreateEndpoint("ep2")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		err = ep2.Delete()
+		if err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	cnt, err := controller.NewSandbox(containerID,
+		libnetwork.OptionHostname("test"),
+		libnetwork.OptionDomainname("docker.io"),
+		libnetwork.OptionUseExternalKey(),
+		libnetwork.OptionExtraHost("web", "192.168.0.1"))
+	defer func() {
+		if err := cnt.Delete(); err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	// Join endpoint to sandbox before SetKey
+	err = ep.Join(cnt)
+	runtime.LockOSThread()
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer func() {
+		err = ep.Leave(cnt)
+		runtime.LockOSThread()
+		if err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	sbox := ep.Info().Sandbox()
+	if sbox == nil {
+		t.Fatalf("Expected to have a valid Sandbox")
+	}
+
+	// Setting an non-existing key (namespace) must fail
+	if err := sbox.SetKey("this-must-fail"); err == nil {
+		t.Fatalf("Setkey must fail if the corresponding namespace is not created")
+	}
+
+	// Create a new OS sandbox using the osl API before using it in SetKey
+	if _, err := osl.NewSandbox("ValidKey", true); err != nil {
+		t.Fatalf("Failed to create new osl sandbox")
+	}
+
+	if err := sbox.SetKey("ValidKey"); err != nil {
+		t.Fatalf("Setkey failed with %v", err)
+	}
+
+	// Join endpoint to sandbox after SetKey
+	err = ep2.Join(sbox)
+	if err != nil {
+		t.Fatal(err)
+	}
+	runtime.LockOSThread()
+	defer func() {
+		err = ep2.Leave(sbox)
+		runtime.LockOSThread()
+		if err != nil {
+			t.Fatal(err)
+		}
+	}()
+
+	if ep.Info().Sandbox().Key() != ep2.Info().Sandbox().Key() {
+		t.Fatalf("ep1 and ep2 returned different container sandbox key")
+	}
+
+	checkSandbox(t, ep.Info())
+}
+
 func TestEndpointDeleteWithActiveContainer(t *testing.T) {
 	if !netutils.IsRunningInContainer() {
 		defer osl.SetupTestOSContext(t)()

+ 28 - 6
libnetwork/osl/namespace_linux.go

@@ -157,16 +157,38 @@ func (n *networkNamespace) NeighborOptions() NeighborOptionSetter {
 	return n
 }
 
-func reexecCreateNamespace() {
-	if len(os.Args) < 2 {
-		log.Fatal("no namespace path provided")
+func mountNetworkNamespace(basePath string, lnPath string) error {
+	if err := syscall.Mount(basePath, lnPath, "bind", syscall.MS_BIND, ""); err != nil {
+		return err
 	}
 
-	if err := syscall.Mount("/proc/self/ns/net", os.Args[1], "bind", syscall.MS_BIND, ""); err != nil {
-		log.Fatal(err)
+	if err := loopbackUp(); err != nil {
+		return err
 	}
+	return nil
+}
 
-	if err := loopbackUp(); err != nil {
+// GetSandboxForExternalKey returns sandbox object for the supplied path
+func GetSandboxForExternalKey(basePath string, key string) (Sandbox, error) {
+	var err error
+	if err = createNamespaceFile(key); err != nil {
+		return nil, err
+	}
+	n := &networkNamespace{path: basePath}
+	n.InvokeFunc(func() {
+		err = mountNetworkNamespace(basePath, key)
+	})
+	if err != nil {
+		return nil, err
+	}
+	return &networkNamespace{path: key}, nil
+}
+
+func reexecCreateNamespace() {
+	if len(os.Args) < 2 {
+		log.Fatal("no namespace path provided")
+	}
+	if err := mountNetworkNamespace("/proc/self/ns/net", os.Args[1]); err != nil {
 		log.Fatal(err)
 	}
 }

+ 4 - 0
libnetwork/osl/namespace_unsupported.go

@@ -6,3 +6,7 @@ package osl
 // and waits for it.
 func GC() {
 }
+
+func GetSandboxForExternalKey(path string, key string) (Sandbox, error) {
+	return nil, nil
+}

+ 4 - 0
libnetwork/osl/namespace_windows.go

@@ -19,6 +19,10 @@ func NewSandbox(key string, osCreate bool) (Sandbox, error) {
 	return nil, nil
 }
 
+func GetSandboxForExternalKey(path string, key string) (Sandbox, error) {
+	return nil, nil
+}
+
 // GC triggers garbage collection of namespace path right away
 // and waits for it.
 func GC() {

+ 81 - 19
libnetwork/sandbox.go

@@ -32,6 +32,8 @@ type Sandbox interface {
 	// Refresh leaves all the endpoints, resets and re-apply the options,
 	// re-joins all the endpoints without destroying the osl sandbox
 	Refresh(options ...SandboxOption) error
+	// SetKey updates the Sandbox Key
+	SetKey(key string) error
 	// Delete destroys this container after detaching it from all connected endpoints.
 	Delete() error
 }
@@ -102,6 +104,7 @@ type containerConfig struct {
 	resolvConfPathConfig
 	generic           map[string]interface{}
 	useDefaultSandBox bool
+	useExternalKey    bool
 	prio              int // higher the value, more the priority
 }
 
@@ -241,8 +244,14 @@ func (sb *sandbox) getConnectedEndpoints() []*endpoint {
 }
 
 func (sb *sandbox) updateGateway(ep *endpoint) error {
-	sb.osSbox.UnsetGateway()
-	sb.osSbox.UnsetGatewayIPv6()
+	sb.Lock()
+	osSbox := sb.osSbox
+	sb.Unlock()
+	if osSbox == nil {
+		return nil
+	}
+	osSbox.UnsetGateway()
+	osSbox.UnsetGatewayIPv6()
 
 	if ep == nil {
 		return nil
@@ -252,18 +261,60 @@ func (sb *sandbox) updateGateway(ep *endpoint) error {
 	joinInfo := ep.joinInfo
 	ep.Unlock()
 
-	if err := sb.osSbox.SetGateway(joinInfo.gw); err != nil {
+	if err := osSbox.SetGateway(joinInfo.gw); err != nil {
 		return fmt.Errorf("failed to set gateway while updating gateway: %v", err)
 	}
 
-	if err := sb.osSbox.SetGatewayIPv6(joinInfo.gw6); err != nil {
+	if err := osSbox.SetGatewayIPv6(joinInfo.gw6); err != nil {
 		return fmt.Errorf("failed to set IPv6 gateway while updating gateway: %v", err)
 	}
 
 	return nil
 }
 
+func (sb *sandbox) SetKey(basePath string) error {
+	var err error
+	if basePath == "" {
+		return types.BadRequestErrorf("invalid sandbox key")
+	}
+
+	sb.Lock()
+	if sb.osSbox != nil {
+		sb.Unlock()
+		return types.ForbiddenErrorf("failed to set sandbox key : already assigned")
+	}
+	sb.Unlock()
+	osSbox, err := osl.GetSandboxForExternalKey(basePath, sb.Key())
+	if err != nil {
+		return err
+	}
+	sb.Lock()
+	sb.osSbox = osSbox
+	sb.Unlock()
+	defer func() {
+		if err != nil {
+			sb.Lock()
+			sb.osSbox = nil
+			sb.Unlock()
+		}
+	}()
+
+	for _, ep := range sb.getConnectedEndpoints() {
+		if err = sb.populateNetworkResources(ep); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
 func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
+	sb.Lock()
+	if sb.osSbox == nil {
+		sb.Unlock()
+		return nil
+	}
+	sb.Unlock()
+
 	ep.Lock()
 	joinInfo := ep.joinInfo
 	ifaces := ep.iFaces
@@ -292,7 +343,6 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
 	}
 
 	sb.Lock()
-	heap.Push(&sb.endpoints, ep)
 	highEp := sb.endpoints[0]
 	sb.Unlock()
 	if ep == highEp {
@@ -305,24 +355,28 @@ func (sb *sandbox) populateNetworkResources(ep *endpoint) error {
 }
 
 func (sb *sandbox) clearNetworkResources(ep *endpoint) error {
-
-	for _, i := range sb.osSbox.Info().Interfaces() {
-		// Only remove the interfaces owned by this endpoint from the sandbox.
-		if ep.hasInterface(i.SrcName()) {
-			if err := i.Remove(); err != nil {
-				log.Debugf("Remove interface failed: %v", err)
+	sb.Lock()
+	osSbox := sb.osSbox
+	sb.Unlock()
+	if osSbox != nil {
+		for _, i := range osSbox.Info().Interfaces() {
+			// Only remove the interfaces owned by this endpoint from the sandbox.
+			if ep.hasInterface(i.SrcName()) {
+				if err := i.Remove(); err != nil {
+					log.Debugf("Remove interface failed: %v", err)
+				}
 			}
 		}
-	}
 
-	ep.Lock()
-	joinInfo := ep.joinInfo
-	ep.Unlock()
+		ep.Lock()
+		joinInfo := ep.joinInfo
+		ep.Unlock()
 
-	// Remove non-interface routes.
-	for _, r := range joinInfo.StaticRoutes {
-		if err := sb.osSbox.RemoveStaticRoute(r); err != nil {
-			log.Debugf("Remove route failed: %v", err)
+		// Remove non-interface routes.
+		for _, r := range joinInfo.StaticRoutes {
+			if err := osSbox.RemoveStaticRoute(r); err != nil {
+				log.Debugf("Remove route failed: %v", err)
+			}
 		}
 	}
 
@@ -670,6 +724,14 @@ func OptionUseDefaultSandbox() SandboxOption {
 	}
 }
 
+// OptionUseExternalKey function returns an option setter for using provided namespace
+// instead of creating one.
+func OptionUseExternalKey() SandboxOption {
+	return func(sb *sandbox) {
+		sb.config.useExternalKey = true
+	}
+}
+
 // OptionGeneric function returns an option setter for Generic configuration
 // that is not managed by libNetwork but can be used by the Drivers during the call to
 // net container creation method. Container Labels are a good example.