Browse code

Fix issue in `docker stats` with `NetworkDisabled=true`

This fix tries to address the issue in 25000 where `docker stats`
will not show network stats with `NetworkDisabled=true`.

The `NetworkDisabled=true` could be either invoked through
remote API, or through `docker daemon -b none`.

The issue was that when `NetworkDisabled=true` either by API or
by daemon config, there is no SandboxKey for container so an error
will be returned.

This fix fixes this issue by skipping obtaining SandboxKey if
`NetworkDisabled=true`.

Additional test has bee added to cover the changes.

This fix fixes 25000.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>

Yong Tang authored on 2016/08/21 07:43:15
Showing 2 changed files
... ...
@@ -138,8 +138,10 @@ func (daemon *Daemon) GetContainerStats(container *container.Container) (*types.
138 138
 		return nil, err
139 139
 	}
140 140
 
141
-	if stats.Networks, err = daemon.getNetworkStats(container); err != nil {
142
-		return nil, err
141
+	if !container.Config.NetworkDisabled {
142
+		if stats.Networks, err = daemon.getNetworkStats(container); err != nil {
143
+			return nil, err
144
+		}
143 145
 	}
144 146
 
145 147
 	return stats, nil
... ...
@@ -1432,3 +1432,55 @@ func (s *DockerSuite) TestContainerApiDeleteWithEmptyName(c *check.C) {
1432 1432
 	c.Assert(status, checker.Equals, http.StatusBadRequest)
1433 1433
 	c.Assert(string(out), checker.Contains, "No container name or ID supplied")
1434 1434
 }
1435
+
1436
+func (s *DockerSuite) TestContainerApiStatsWithNetworkDisabled(c *check.C) {
1437
+	// Problematic on Windows as Windows does not support stats
1438
+	testRequires(c, DaemonIsLinux)
1439
+
1440
+	name := "testing-network-disabled"
1441
+	config := map[string]interface{}{
1442
+		"Image":           "busybox",
1443
+		"Cmd":             []string{"top"},
1444
+		"NetworkDisabled": true,
1445
+	}
1446
+
1447
+	status, _, err := sockRequest("POST", "/containers/create?name="+name, config)
1448
+	c.Assert(err, checker.IsNil)
1449
+	c.Assert(status, checker.Equals, http.StatusCreated)
1450
+
1451
+	status, _, err = sockRequest("POST", "/containers/"+name+"/start", nil)
1452
+	c.Assert(err, checker.IsNil)
1453
+	c.Assert(status, checker.Equals, http.StatusNoContent)
1454
+
1455
+	c.Assert(waitRun(name), check.IsNil)
1456
+
1457
+	type b struct {
1458
+		status int
1459
+		body   []byte
1460
+		err    error
1461
+	}
1462
+	bc := make(chan b, 1)
1463
+	go func() {
1464
+		status, body, err := sockRequest("GET", "/containers/"+name+"/stats", nil)
1465
+		bc <- b{status, body, err}
1466
+	}()
1467
+
1468
+	// allow some time to stream the stats from the container
1469
+	time.Sleep(4 * time.Second)
1470
+	dockerCmd(c, "rm", "-f", name)
1471
+
1472
+	// collect the results from the stats stream or timeout and fail
1473
+	// if the stream was not disconnected.
1474
+	select {
1475
+	case <-time.After(2 * time.Second):
1476
+		c.Fatal("stream was not closed after container was removed")
1477
+	case sr := <-bc:
1478
+		c.Assert(sr.err, checker.IsNil)
1479
+		c.Assert(sr.status, checker.Equals, http.StatusOK)
1480
+
1481
+		// decode only one object from the stream
1482
+		var s *types.Stats
1483
+		dec := json.NewDecoder(bytes.NewBuffer(sr.body))
1484
+		c.Assert(dec.Decode(&s), checker.IsNil)
1485
+	}
1486
+}