Merge pull request #40478 from cpuguy83/dont-prime-the-stats
Add stats options to not prime the stats
This commit is contained in:
commit
54d88a7cd3
8 changed files with 51 additions and 2 deletions
|
@ -105,9 +105,14 @@ func (s *containerRouter) getContainersStats(ctx context.Context, w http.Respons
|
|||
if !stream {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
}
|
||||
var oneShot bool
|
||||
if versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.41") {
|
||||
oneShot = httputils.BoolValueOrDefault(r, "one-shot", false)
|
||||
}
|
||||
|
||||
config := &backend.ContainerStatsConfig{
|
||||
Stream: stream,
|
||||
OneShot: oneShot,
|
||||
OutStream: w,
|
||||
Version: httputils.VersionFromContext(ctx),
|
||||
}
|
||||
|
|
|
@ -5691,6 +5691,11 @@ paths:
|
|||
description: "Stream the output. If false, the stats will be output once and then it will disconnect."
|
||||
type: "boolean"
|
||||
default: true
|
||||
- name: "one-shot"
|
||||
in: "query"
|
||||
description: "Only get a single stat instead of waiting for 2 cycles. Must be used with stream=false"
|
||||
type: "boolean"
|
||||
default: false
|
||||
tags: ["Container"]
|
||||
/containers/{id}/resize:
|
||||
post:
|
||||
|
|
|
@ -73,6 +73,7 @@ type LogSelector struct {
|
|||
// behavior of a backend.ContainerStats() call.
|
||||
type ContainerStatsConfig struct {
|
||||
Stream bool
|
||||
OneShot bool
|
||||
OutStream io.Writer
|
||||
Version string
|
||||
}
|
||||
|
|
|
@ -24,3 +24,19 @@ func (cli *Client) ContainerStats(ctx context.Context, containerID string, strea
|
|||
osType := getDockerOS(resp.header.Get("Server"))
|
||||
return types.ContainerStats{Body: resp.body, OSType: osType}, err
|
||||
}
|
||||
|
||||
// ContainerStatsOneShot gets a single stat entry from a container.
|
||||
// It differs from `ContainerStats` in that the API should not wait to prime the stats
|
||||
func (cli *Client) ContainerStatsOneShot(ctx context.Context, containerID string) (types.ContainerStats, error) {
|
||||
query := url.Values{}
|
||||
query.Set("stream", "0")
|
||||
query.Set("one-shot", "1")
|
||||
|
||||
resp, err := cli.get(ctx, "/containers/"+containerID+"/stats", query, nil)
|
||||
if err != nil {
|
||||
return types.ContainerStats{}, err
|
||||
}
|
||||
|
||||
osType := getDockerOS(resp.header.Get("Server"))
|
||||
return types.ContainerStats{Body: resp.body, OSType: osType}, err
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ type ContainerAPIClient interface {
|
|||
ContainerRestart(ctx context.Context, container string, timeout *time.Duration) error
|
||||
ContainerStatPath(ctx context.Context, container, path string) (types.ContainerPathStat, error)
|
||||
ContainerStats(ctx context.Context, container string, stream bool) (types.ContainerStats, error)
|
||||
ContainerStatsOneShot(ctx context.Context, container string) (types.ContainerStats, error)
|
||||
ContainerStart(ctx context.Context, container string, options types.ContainerStartOptions) error
|
||||
ContainerStop(ctx context.Context, container string, timeout *time.Duration) error
|
||||
ContainerTop(ctx context.Context, container string, arguments []string) (containertypes.ContainerTopOKBody, error)
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/api/types/versions/v1p20"
|
||||
"github.com/docker/docker/container"
|
||||
"github.com/docker/docker/errdefs"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
)
|
||||
|
||||
|
@ -30,6 +31,10 @@ func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, c
|
|||
return err
|
||||
}
|
||||
|
||||
if config.Stream && config.OneShot {
|
||||
return errdefs.InvalidParameter(errors.New("cannot have stream=true and one-shot=true"))
|
||||
}
|
||||
|
||||
// If the container is either not running or restarting and requires no stream, return an empty stats.
|
||||
if (!ctr.IsRunning() || ctr.IsRestarting()) && !config.Stream {
|
||||
return json.NewEncoder(config.OutStream).Encode(&types.StatsJSON{
|
||||
|
@ -64,7 +69,7 @@ func (daemon *Daemon) ContainerStats(ctx context.Context, prefixOrName string, c
|
|||
updates := daemon.subscribeToContainerStats(ctr)
|
||||
defer daemon.unsubscribeToContainerStats(ctr, updates)
|
||||
|
||||
noStreamFirstFrame := true
|
||||
noStreamFirstFrame := !config.OneShot
|
||||
for {
|
||||
select {
|
||||
case v, ok := <-updates:
|
||||
|
|
|
@ -61,6 +61,8 @@ keywords: "API, Docker, rcli, REST, documentation"
|
|||
service.
|
||||
* `GET /tasks/{id}` now includes `JobIteration` on the task if spawned from a
|
||||
job-mode service.
|
||||
* `GET /containers/{id}/stats` now accepts a query param (`one-shot`) which, when used with `stream=false` fetches a
|
||||
single set of stats instead of waiting for two collection cycles to have 2 CPU stats over a 1 second period.
|
||||
|
||||
## v1.40 API changes
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -34,10 +35,23 @@ func TestStats(t *testing.T) {
|
|||
assert.NilError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
var v *types.Stats
|
||||
var v types.Stats
|
||||
err = json.NewDecoder(resp.Body).Decode(&v)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(int64(v.MemoryStats.Limit), info.MemTotal))
|
||||
assert.Check(t, !reflect.DeepEqual(v.PreCPUStats, types.CPUStats{}))
|
||||
err = json.NewDecoder(resp.Body).Decode(&v)
|
||||
assert.Assert(t, is.ErrorContains(err, ""), io.EOF)
|
||||
|
||||
resp, err = client.ContainerStatsOneShot(ctx, cID)
|
||||
assert.NilError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
v = types.Stats{}
|
||||
err = json.NewDecoder(resp.Body).Decode(&v)
|
||||
assert.NilError(t, err)
|
||||
assert.Check(t, is.Equal(int64(v.MemoryStats.Limit), info.MemTotal))
|
||||
assert.Check(t, is.DeepEqual(v.PreCPUStats, types.CPUStats{}))
|
||||
err = json.NewDecoder(resp.Body).Decode(&v)
|
||||
assert.Assert(t, is.ErrorContains(err, ""), io.EOF)
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue