Browse code

Merge pull request #21904 from yongtang/21848-stats-connected-containers

Docker stats is not working when a container is using another container's network.

Alexander Morozov authored on 2016/04/13 02:28:05
Showing 2 changed files
... ...
@@ -1422,8 +1422,27 @@ func (daemon *Daemon) GetContainerStats(container *container.Container) (*types.
1422 1422
 	return stats, nil
1423 1423
 }
1424 1424
 
1425
+// Resolve Network SandboxID in case the container reuse another container's network stack
1426
+func (daemon *Daemon) getNetworkSandboxID(c *container.Container) (string, error) {
1427
+	curr := c
1428
+	for curr.HostConfig.NetworkMode.IsContainer() {
1429
+		containerID := curr.HostConfig.NetworkMode.ConnectedContainer()
1430
+		connected, err := daemon.GetContainer(containerID)
1431
+		if err != nil {
1432
+			return "", fmt.Errorf("Could not get container for %s", containerID)
1433
+		}
1434
+		curr = connected
1435
+	}
1436
+	return curr.NetworkSettings.SandboxID, nil
1437
+}
1438
+
1425 1439
 func (daemon *Daemon) getNetworkStats(c *container.Container) (map[string]types.NetworkStats, error) {
1426
-	sb, err := daemon.netController.SandboxByID(c.NetworkSettings.SandboxID)
1440
+	sandboxID, err := daemon.getNetworkSandboxID(c)
1441
+	if err != nil {
1442
+		return nil, err
1443
+	}
1444
+
1445
+	sb, err := daemon.netController.SandboxByID(sandboxID)
1427 1446
 	if err != nil {
1428 1447
 		return nil, err
1429 1448
 	}
... ...
@@ -255,3 +255,42 @@ func (s *DockerSuite) TestApiStatsContainerGetMemoryLimit(c *check.C) {
255 255
 	body.Close()
256 256
 	c.Assert(fmt.Sprintf("%d", v.MemoryStats.Limit), checker.Equals, fmt.Sprintf("%d", info.MemTotal))
257 257
 }
258
+
259
+func (s *DockerSuite) TestApiStatsNoStreamConnectedContainers(c *check.C) {
260
+	testRequires(c, DaemonIsLinux)
261
+
262
+	out1, _ := runSleepingContainer(c)
263
+	id1 := strings.TrimSpace(out1)
264
+	c.Assert(waitRun(id1), checker.IsNil)
265
+
266
+	out2, _ := runSleepingContainer(c, "--net", "container:"+id1)
267
+	id2 := strings.TrimSpace(out2)
268
+	c.Assert(waitRun(id2), checker.IsNil)
269
+
270
+	ch := make(chan error)
271
+	go func() {
272
+		resp, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats?stream=false", id2), nil, "")
273
+		defer body.Close()
274
+		if err != nil {
275
+			ch <- err
276
+		}
277
+		if resp.StatusCode != http.StatusOK {
278
+			ch <- fmt.Errorf("Invalid StatusCode %v", resp.StatusCode)
279
+		}
280
+		if resp.Header.Get("Content-Type") != "application/json" {
281
+			ch <- fmt.Errorf("Invalid 'Content-Type' %v", resp.Header.Get("Content-Type"))
282
+		}
283
+		var v *types.Stats
284
+		if err := json.NewDecoder(body).Decode(&v); err != nil {
285
+			ch <- err
286
+		}
287
+		ch <- nil
288
+	}()
289
+
290
+	select {
291
+	case err := <-ch:
292
+		c.Assert(err, checker.IsNil, check.Commentf("Error in stats remote API: %v", err))
293
+	case <-time.After(15 * time.Second):
294
+		c.Fatalf("Stats did not return after timeout")
295
+	}
296
+}