Browse code

Shutdown plugins during daemon shutdown.

Signed-off-by: Anusha Ragunathan <anusha@docker.com>
(cherry picked from commit 863ab9ab134d0baef3c7e5d745eded891e87e734)
Signed-off-by: Tibor Vass <tibor@docker.com>

Anusha Ragunathan authored on 2016/07/02 03:36:11
Showing 7 changed files
... ...
@@ -685,6 +685,8 @@ func (daemon *Daemon) Shutdown() error {
685 685
 		}
686 686
 	}
687 687
 
688
+	pluginShutdown()
689
+
688 690
 	if err := daemon.cleanupMounts(); err != nil {
689 691
 		return err
690 692
 	}
... ...
@@ -2,8 +2,15 @@
2 2
 
3 3
 package daemon
4 4
 
5
-import "github.com/docker/engine-api/types/container"
5
+import (
6
+	"github.com/docker/docker/plugin"
7
+	"github.com/docker/engine-api/types/container"
8
+)
6 9
 
7 10
 func (daemon *Daemon) verifyExperimentalContainerSettings(hostConfig *container.HostConfig, config *container.Config) ([]string, error) {
8 11
 	return nil, nil
9 12
 }
13
+
14
+func pluginShutdown() {
15
+	plugin.GetManager().Shutdown()
16
+}
... ...
@@ -7,3 +7,6 @@ import "github.com/docker/engine-api/types/container"
7 7
 func (daemon *Daemon) verifyExperimentalContainerSettings(hostConfig *container.HostConfig, config *container.Config) ([]string, error) {
8 8
 	return nil, nil
9 9
 }
10
+
11
+func pluginShutdown() {
12
+}
... ...
@@ -5,6 +5,9 @@ package main
5 5
 import (
6 6
 	"github.com/docker/docker/pkg/integration/checker"
7 7
 	"github.com/go-check/check"
8
+	"os"
9
+	"os/exec"
10
+	"time"
8 11
 )
9 12
 
10 13
 var pluginName = "tiborvass/no-remove"
... ...
@@ -67,3 +70,66 @@ func (s *DockerDaemonSuite) TestDaemonRestartWithPluginDisabled(c *check.C) {
67 67
 	c.Assert(out, checker.Contains, pluginName)
68 68
 	c.Assert(out, checker.Contains, "false")
69 69
 }
70
+
71
+// TestDaemonShutdownLiveRestoreWithPlugins leaves plugin running.
72
+func (s *DockerDaemonSuite) TestDaemonShutdownLiveRestoreWithPlugins(c *check.C) {
73
+	if err := s.d.Start("--live-restore"); err != nil {
74
+		c.Fatalf("Could not start daemon: %v", err)
75
+	}
76
+	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pluginName); err != nil {
77
+		c.Fatalf("Could not install plugin: %v %s", err, out)
78
+	}
79
+	defer func() {
80
+		if err := s.d.Restart("--live-restore"); err != nil {
81
+			c.Fatalf("Could not restart daemon: %v", err)
82
+		}
83
+		if out, err := s.d.Cmd("plugin", "disable", pluginName); err != nil {
84
+			c.Fatalf("Could not disable plugin: %v %s", err, out)
85
+		}
86
+		if out, err := s.d.Cmd("plugin", "remove", pluginName); err != nil {
87
+			c.Fatalf("Could not remove plugin: %v %s", err, out)
88
+		}
89
+	}()
90
+
91
+	if err := s.d.Kill(); err != nil {
92
+		c.Fatalf("Could not kill daemon: %v", err)
93
+	}
94
+
95
+	cmd := exec.Command("pgrep", "-f", "plugin-no-remove")
96
+	if out, ec, err := runCommandWithOutput(cmd); ec != 0 {
97
+		c.Fatalf("Expected exit code '0', got %d err: %v output: %s ", ec, err, out)
98
+	}
99
+}
100
+
101
+// TestDaemonShutdownWithPlugins shuts down running plugins.
102
+func (s *DockerDaemonSuite) TestDaemonShutdownWithPlugins(c *check.C) {
103
+	if err := s.d.Start(); err != nil {
104
+		c.Fatalf("Could not start daemon: %v", err)
105
+	}
106
+	if out, err := s.d.Cmd("plugin", "install", "--grant-all-permissions", pluginName); err != nil {
107
+		c.Fatalf("Could not install plugin: %v %s", err, out)
108
+	}
109
+
110
+	defer func() {
111
+		if err := s.d.Restart(); err != nil {
112
+			c.Fatalf("Could not restart daemon: %v", err)
113
+		}
114
+		if out, err := s.d.Cmd("plugin", "disable", pluginName); err != nil {
115
+			c.Fatalf("Could not disable plugin: %v %s", err, out)
116
+		}
117
+		if out, err := s.d.Cmd("plugin", "remove", pluginName); err != nil {
118
+			c.Fatalf("Could not remove plugin: %v %s", err, out)
119
+		}
120
+	}()
121
+
122
+	if err := s.d.cmd.Process.Signal(os.Interrupt); err != nil {
123
+		c.Fatalf("Could not kill daemon: %v", err)
124
+	}
125
+
126
+	time.Sleep(5 * time.Second)
127
+
128
+	cmd := exec.Command("pgrep", "-f", "plugin-no-remove")
129
+	if out, ec, err := runCommandWithOutput(cmd); ec != 1 {
130
+		c.Fatalf("Expected exit code '1', got %d err: %v output: %s ", ec, err, out)
131
+	}
132
+}
... ...
@@ -47,6 +47,7 @@ type plugin struct {
47 47
 	client            *plugins.Client
48 48
 	restartManager    restartmanager.RestartManager
49 49
 	runtimeSourcePath string
50
+	exitChan          chan bool
50 51
 }
51 52
 
52 53
 func (p *plugin) Client() *plugins.Client {
... ...
@@ -98,6 +99,7 @@ type Manager struct {
98 98
 	registryService  registry.Service
99 99
 	handleLegacy     bool
100 100
 	liveRestore      bool
101
+	shutdown         bool
101 102
 }
102 103
 
103 104
 // GetManager returns the singleton plugin Manager
... ...
@@ -250,10 +252,23 @@ func LookupWithCapability(name, capability string) (Plugin, error) {
250 250
 	return nil, ErrInadequateCapability{name, capability}
251 251
 }
252 252
 
253
-// StateChanged updates daemon inter...
253
+// StateChanged updates plugin internals using from libcontainerd events.
254 254
 func (pm *Manager) StateChanged(id string, e libcontainerd.StateInfo) error {
255 255
 	logrus.Debugf("plugin state changed %s %#v", id, e)
256 256
 
257
+	switch e.State {
258
+	case libcontainerd.StateExit:
259
+		pm.RLock()
260
+		p, idOk := pm.plugins[id]
261
+		pm.RUnlock()
262
+		if !idOk {
263
+			return ErrNotFound(id)
264
+		}
265
+		if pm.shutdown == true {
266
+			p.exitChan <- true
267
+		}
268
+	}
269
+
257 270
 	return nil
258 271
 }
259 272
 
... ...
@@ -6,6 +6,7 @@ import (
6 6
 	"os"
7 7
 	"path/filepath"
8 8
 	"syscall"
9
+	"time"
9 10
 
10 11
 	"github.com/Sirupsen/logrus"
11 12
 	"github.com/docker/docker/libcontainerd"
... ...
@@ -128,3 +129,39 @@ func (pm *Manager) disable(p *plugin) error {
128 128
 	pm.save()
129 129
 	return nil
130 130
 }
131
+
132
+// Shutdown stops all plugins and called during daemon shutdown.
133
+func (pm *Manager) Shutdown() {
134
+	pm.RLock()
135
+	defer pm.RUnlock()
136
+
137
+	pm.shutdown = true
138
+	for _, p := range pm.plugins {
139
+		if p.restartManager != nil {
140
+			if err := p.restartManager.Cancel(); err != nil {
141
+				logrus.Error(err)
142
+			}
143
+		}
144
+		if pm.containerdClient != nil {
145
+			p.exitChan = make(chan bool)
146
+			err := pm.containerdClient.Signal(p.P.ID, int(syscall.SIGTERM))
147
+			if err != nil {
148
+				logrus.Errorf("Sending SIGTERM to plugin failed with error: %v", err)
149
+			} else {
150
+				select {
151
+				case <-p.exitChan:
152
+					logrus.Debug("Clean shutdown of plugin")
153
+				case <-time.After(time.Second * 10):
154
+					logrus.Debug("Force shutdown plugin")
155
+					if err := pm.containerdClient.Signal(p.P.ID, int(syscall.SIGKILL)); err != nil {
156
+						logrus.Errorf("Sending SIGKILL to plugin failed with error: %v", err)
157
+					}
158
+				}
159
+			}
160
+			close(p.exitChan)
161
+		}
162
+		if err := os.RemoveAll(p.runtimeSourcePath); err != nil {
163
+			logrus.Errorf("Remove plugin runtime failed with error: %v", err)
164
+		}
165
+	}
166
+}
... ...
@@ -23,3 +23,7 @@ func (pm *Manager) disable(p *plugin) error {
23 23
 func (pm *Manager) restore(p *plugin) error {
24 24
 	return fmt.Errorf("Not implemented")
25 25
 }
26
+
27
+// Shutdown plugins
28
+func (pm *Manager) Shutdown() {
29
+}