Kaynağa Gözat

Add support to trigger immediate garbage collection

Right now the namespace paths are cleaned up every
garbage collection period. But if the daemon is restarted
before all the namespace paths of removed containers are
garbage collected they will remain there forever. The fix
is to provide a GC() api so that garbage collection can be
triggered immediately.

Signed-off-by: Jana Radhakrishnan <mrjana@docker.com>
Jana Radhakrishnan 10 yıl önce
ebeveyn
işleme
15ddc3717a

+ 8 - 0
libnetwork/controller.go

@@ -59,6 +59,7 @@ import (
 	"github.com/docker/libnetwork/datastore"
 	"github.com/docker/libnetwork/datastore"
 	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/driverapi"
 	"github.com/docker/libnetwork/hostdiscovery"
 	"github.com/docker/libnetwork/hostdiscovery"
+	"github.com/docker/libnetwork/sandbox"
 	"github.com/docker/libnetwork/types"
 	"github.com/docker/libnetwork/types"
 	"github.com/docker/swarm/pkg/store"
 	"github.com/docker/swarm/pkg/store"
 )
 )
@@ -84,6 +85,9 @@ type NetworkController interface {
 
 
 	// NetworkByID returns the Network which has the passed id. If not found, the error ErrNoSuchNetwork is returned.
 	// NetworkByID returns the Network which has the passed id. If not found, the error ErrNoSuchNetwork is returned.
 	NetworkByID(id string) (Network, error)
 	NetworkByID(id string) (Network, error)
+
+	// GC triggers immediate garbage collection of resources which are garbage collected.
+	GC()
 }
 }
 
 
 // NetworkWalker is a client provided function which will be used to walk the Networks.
 // NetworkWalker is a client provided function which will be used to walk the Networks.
@@ -394,3 +398,7 @@ func (c *controller) loadDriver(networkType string) (driverapi.Driver, error) {
 	}
 	}
 	return d, nil
 	return d, nil
 }
 }
+
+func (c *controller) GC() {
+	sandbox.GC()
+}

+ 28 - 1
libnetwork/sandbox/namespace_linux.go

@@ -25,6 +25,7 @@ var (
 	gpmLock          sync.Mutex
 	gpmLock          sync.Mutex
 	gpmWg            sync.WaitGroup
 	gpmWg            sync.WaitGroup
 	gpmCleanupPeriod = 60 * time.Second
 	gpmCleanupPeriod = 60 * time.Second
+	gpmChan          = make(chan chan struct{})
 )
 )
 
 
 // The networkNamespace type is the linux implementation of the Sandbox
 // The networkNamespace type is the linux implementation of the Sandbox
@@ -56,7 +57,18 @@ func removeUnusedPaths() {
 	period := gpmCleanupPeriod
 	period := gpmCleanupPeriod
 	gpmLock.Unlock()
 	gpmLock.Unlock()
 
 
-	for range time.Tick(period) {
+	ticker := time.NewTicker(period)
+	for {
+		var (
+			gc   chan struct{}
+			gcOk bool
+		)
+
+		select {
+		case <-ticker.C:
+		case gc, gcOk = <-gpmChan:
+		}
+
 		gpmLock.Lock()
 		gpmLock.Lock()
 		pathList := make([]string, 0, len(garbagePathMap))
 		pathList := make([]string, 0, len(garbagePathMap))
 		for path := range garbagePathMap {
 		for path := range garbagePathMap {
@@ -71,6 +83,9 @@ func removeUnusedPaths() {
 		}
 		}
 
 
 		gpmWg.Done()
 		gpmWg.Done()
+		if gcOk {
+			close(gc)
+		}
 	}
 	}
 }
 }
 
 
@@ -86,6 +101,18 @@ func removeFromGarbagePaths(path string) {
 	gpmLock.Unlock()
 	gpmLock.Unlock()
 }
 }
 
 
+// GC triggers garbage collection of namespace path right away
+// and waits for it.
+func GC() {
+	waitGC := make(chan struct{})
+
+	// Trigger GC now
+	gpmChan <- waitGC
+
+	// wait for gc to complete
+	<-waitGC
+}
+
 // GenerateKey generates a sandbox key based on the passed
 // GenerateKey generates a sandbox key based on the passed
 // container id.
 // container id.
 func GenerateKey(containerID string) string {
 func GenerateKey(containerID string) string {

+ 10 - 3
libnetwork/sandbox/sandbox_linux_test.go

@@ -153,9 +153,16 @@ func verifySandbox(t *testing.T, s Sandbox) {
 	}
 	}
 }
 }
 
 
-func verifyCleanup(t *testing.T, s Sandbox) {
-	time.Sleep(time.Duration(gpmCleanupPeriod * 2))
+func verifyCleanup(t *testing.T, s Sandbox, wait bool) {
+	if wait {
+		time.Sleep(time.Duration(gpmCleanupPeriod * 2))
+	}
+
 	if _, err := os.Stat(s.Key()); err == nil {
 	if _, err := os.Stat(s.Key()); err == nil {
-		t.Fatalf("The sandbox path %s is not getting cleanup event after twice the cleanup period", s.Key())
+		if wait {
+			t.Fatalf("The sandbox path %s is not getting cleaned up even after twice the cleanup period", s.Key())
+		} else {
+			t.Fatalf("The sandbox path %s is not cleaned up after running gc", s.Key())
+		}
 	}
 	}
 }
 }

+ 18 - 1
libnetwork/sandbox/sandbox_test.go

@@ -54,7 +54,7 @@ func TestSandboxCreate(t *testing.T) {
 
 
 	verifySandbox(t, s)
 	verifySandbox(t, s)
 	s.Destroy()
 	s.Destroy()
-	verifyCleanup(t, s)
+	verifyCleanup(t, s, true)
 }
 }
 
 
 func TestSandboxCreateTwice(t *testing.T) {
 func TestSandboxCreateTwice(t *testing.T) {
@@ -77,6 +77,23 @@ func TestSandboxCreateTwice(t *testing.T) {
 	s.Destroy()
 	s.Destroy()
 }
 }
 
 
+func TestSandboxGC(t *testing.T) {
+	key, err := newKey(t)
+	if err != nil {
+		t.Fatalf("Failed to obtain a key: %v", err)
+	}
+
+	s, err := NewSandbox(key, true)
+	if err != nil {
+		t.Fatalf("Failed to create a new sandbox: %v", err)
+	}
+
+	s.Destroy()
+
+	GC()
+	verifyCleanup(t, s, false)
+}
+
 func TestAddRemoveInterface(t *testing.T) {
 func TestAddRemoveInterface(t *testing.T) {
 	key, err := newKey(t)
 	key, err := newKey(t)
 	if err != nil {
 	if err != nil {