mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Docker stats is not working when a container is using another container's network.
This fix tries to fix the issue in #21848 where `docker stats` will not correctly display the container stats in case the container reuse another container's network stack. The issue is that when `stats` is performed, the daemon will check for container network setting's `SandboxID`. Unfortunately, for containers that reuse another container's network stack (`NetworkMode.IsConnected()`), SandboxID is not assigned. Therefore, the daemon thinks the id is invalid and remote API will never return. This fix tries to resolve the SandboxID by iterating through connected containers and identify the appropriate SandboxID. A test case for `stats` remote API has been added to check if `stats` will return within the timeout. This fix fixes #21848. Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
This commit is contained in:
parent
4ac59a1282
commit
faf2b6f7aa
2 changed files with 59 additions and 1 deletions
|
@ -1421,8 +1421,27 @@ func (daemon *Daemon) GetContainerStats(container *container.Container) (*types.
|
||||||
return stats, nil
|
return stats, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve Network SandboxID in case the container reuse another container's network stack
|
||||||
|
func (daemon *Daemon) getNetworkSandboxID(c *container.Container) (string, error) {
|
||||||
|
curr := c
|
||||||
|
for curr.HostConfig.NetworkMode.IsContainer() {
|
||||||
|
containerID := curr.HostConfig.NetworkMode.ConnectedContainer()
|
||||||
|
connected, err := daemon.GetContainer(containerID)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Could not get container for %s", containerID)
|
||||||
|
}
|
||||||
|
curr = connected
|
||||||
|
}
|
||||||
|
return curr.NetworkSettings.SandboxID, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (daemon *Daemon) getNetworkStats(c *container.Container) (map[string]types.NetworkStats, error) {
|
func (daemon *Daemon) getNetworkStats(c *container.Container) (map[string]types.NetworkStats, error) {
|
||||||
sb, err := daemon.netController.SandboxByID(c.NetworkSettings.SandboxID)
|
sandboxID, err := daemon.getNetworkSandboxID(c)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sb, err := daemon.netController.SandboxByID(sandboxID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,3 +255,42 @@ func (s *DockerSuite) TestApiStatsContainerGetMemoryLimit(c *check.C) {
|
||||||
body.Close()
|
body.Close()
|
||||||
c.Assert(fmt.Sprintf("%d", v.MemoryStats.Limit), checker.Equals, fmt.Sprintf("%d", info.MemTotal))
|
c.Assert(fmt.Sprintf("%d", v.MemoryStats.Limit), checker.Equals, fmt.Sprintf("%d", info.MemTotal))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *DockerSuite) TestApiStatsNoStreamConnectedContainers(c *check.C) {
|
||||||
|
testRequires(c, DaemonIsLinux)
|
||||||
|
|
||||||
|
out1, _ := runSleepingContainer(c)
|
||||||
|
id1 := strings.TrimSpace(out1)
|
||||||
|
c.Assert(waitRun(id1), checker.IsNil)
|
||||||
|
|
||||||
|
out2, _ := runSleepingContainer(c, "--net", "container:"+id1)
|
||||||
|
id2 := strings.TrimSpace(out2)
|
||||||
|
c.Assert(waitRun(id2), checker.IsNil)
|
||||||
|
|
||||||
|
ch := make(chan error)
|
||||||
|
go func() {
|
||||||
|
resp, body, err := sockRequestRaw("GET", fmt.Sprintf("/containers/%s/stats?stream=false", id2), nil, "")
|
||||||
|
defer body.Close()
|
||||||
|
if err != nil {
|
||||||
|
ch <- err
|
||||||
|
}
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
ch <- fmt.Errorf("Invalid StatusCode %v", resp.StatusCode)
|
||||||
|
}
|
||||||
|
if resp.Header.Get("Content-Type") != "application/json" {
|
||||||
|
ch <- fmt.Errorf("Invalid 'Content-Type' %v", resp.Header.Get("Content-Type"))
|
||||||
|
}
|
||||||
|
var v *types.Stats
|
||||||
|
if err := json.NewDecoder(body).Decode(&v); err != nil {
|
||||||
|
ch <- err
|
||||||
|
}
|
||||||
|
ch <- nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err := <-ch:
|
||||||
|
c.Assert(err, checker.IsNil, check.Commentf("Error in stats remote API: %v", err))
|
||||||
|
case <-time.After(15 * time.Second):
|
||||||
|
c.Fatalf("Stats did not return after timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue