Browse code

Merge pull request #13499 from cpuguy83/fix_stats_unsubscribe

Fix unregister stats on when rm running container

Phil Estes authored on 2015/05/28 00:10:36
Showing 3 changed files
... ...
@@ -58,10 +58,6 @@ func (daemon *Daemon) ContainerRm(name string, config *ContainerRmConfig) error
58 58
 
59 59
 // Destroy unregisters a container from the daemon and cleanly removes its contents from the filesystem.
60 60
 func (daemon *Daemon) rm(container *Container, forceRemove bool) (err error) {
61
-	// stop collection of stats for the container regardless
62
-	// if stats are currently getting collected.
63
-	daemon.statsCollector.stopCollection(container)
64
-
65 61
 	if container.IsRunning() {
66 62
 		if !forceRemove {
67 63
 			return fmt.Errorf("Conflict, You cannot remove a running container. Stop the container before attempting removal or use -f")
... ...
@@ -71,6 +67,10 @@ func (daemon *Daemon) rm(container *Container, forceRemove bool) (err error) {
71 71
 		}
72 72
 	}
73 73
 
74
+	// stop collection of stats for the container regardless
75
+	// if stats are currently getting collected.
76
+	daemon.statsCollector.stopCollection(container)
77
+
74 78
 	element := daemon.containers.Get(container.ID)
75 79
 	if element == nil {
76 80
 		return fmt.Errorf("Container %v not found - maybe it was already destroyed?", container.ID)
... ...
@@ -254,6 +254,42 @@ func (s *DockerSuite) TestGetContainerStats(c *check.C) {
254 254
 	}
255 255
 }
256 256
 
257
+func (s *DockerSuite) TestContainerStatsRmRunning(c *check.C) {
258
+	out, _ := dockerCmd(c, "run", "-d", "busybox", "top")
259
+	id := strings.TrimSpace(out)
260
+
261
+	buf := &channelBuffer{make(chan []byte, 1)}
262
+	defer buf.Close()
263
+	chErr := make(chan error)
264
+	go func() {
265
+		_, body, err := sockRequestRaw("GET", "/containers/"+id+"/stats?stream=1", nil, "application/json")
266
+		if err != nil {
267
+			chErr <- err
268
+		}
269
+		defer body.Close()
270
+		_, err = io.Copy(buf, body)
271
+		chErr <- err
272
+	}()
273
+	defer func() {
274
+		c.Assert(<-chErr, check.IsNil)
275
+	}()
276
+
277
+	b := make([]byte, 32)
278
+	// make sure we've got some stats
279
+	_, err := buf.ReadTimeout(b, 2*time.Second)
280
+	c.Assert(err, check.IsNil)
281
+
282
+	// Now remove without `-f` and make sure we are still pulling stats
283
+	_, err = runCommand(exec.Command(dockerBinary, "rm", id))
284
+	c.Assert(err, check.Not(check.IsNil), check.Commentf("rm should have failed but didn't"))
285
+	_, err = buf.ReadTimeout(b, 2*time.Second)
286
+	c.Assert(err, check.IsNil)
287
+	dockerCmd(c, "rm", "-f", id)
288
+
289
+	_, err = buf.ReadTimeout(b, 2*time.Second)
290
+	c.Assert(err, check.Not(check.IsNil))
291
+}
292
+
257 293
 func (s *DockerSuite) TestGetStoppedContainerStats(c *check.C) {
258 294
 	// TODO: this test does nothing because we are c.Assert'ing in goroutine
259 295
 	var (
... ...
@@ -316,3 +316,26 @@ func parseCgroupPaths(procCgroupData string) map[string]string {
316 316
 	}
317 317
 	return cgroupPaths
318 318
 }
319
+
320
+type channelBuffer struct {
321
+	c chan []byte
322
+}
323
+
324
+func (c *channelBuffer) Write(b []byte) (int, error) {
325
+	c.c <- b
326
+	return len(b), nil
327
+}
328
+
329
+func (c *channelBuffer) Close() error {
330
+	close(c.c)
331
+	return nil
332
+}
333
+
334
+func (c *channelBuffer) ReadTimeout(p []byte, n time.Duration) (int, error) {
335
+	select {
336
+	case b := <-c.c:
337
+		return copy(p[0:], b), nil
338
+	case <-time.After(n):
339
+		return -1, fmt.Errorf("timeout reading from channel")
340
+	}
341
+}