Skip to content

Commit

Permalink
feature: add container stats api client side
Browse files Browse the repository at this point in the history
Signed-off-by: zhangyue <zy675793960@yeah.net>
  • Loading branch information
zhangyue committed Dec 28, 2018
1 parent daeca08 commit ac9fa5d
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 6 deletions.
21 changes: 21 additions & 0 deletions client/container_stats.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package client

import (
"context"
"fmt"
"io"
"net/url"
)

// ContainerStats return the stats related a container in an io.ReadCloser.
func (client *APIClient) ContainerStats(ctx context.Context, name string, stream bool) (io.ReadCloser, error) {
query := url.Values{}
if stream {
query.Set("stream", "1")
}
resp, err := client.get(ctx, fmt.Sprintf("/containers/%s/stats", name), query, nil)
if err != nil {
return nil, err
}
return resp.Body, nil
}
1 change: 1 addition & 0 deletions client/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type ContainerAPIClient interface {
ContainerCheckpointList(ctx context.Context, name string, options types.CheckpointListOptions) ([]string, error)
ContainerCheckpointDelete(ctx context.Context, name string, options types.CheckpointDeleteOptions) error
ContainerCommit(ctx context.Context, name string, options types.ContainerCommitOptions) (*types.ContainerCommitResp, error)
ContainerStats(ctx context.Context, name string, stream bool) (io.ReadCloser, error)
}

// ImageAPIClient defines methods of Image client.
Expand Down
14 changes: 8 additions & 6 deletions daemon/mgr/container_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@ func (mgr *ContainerManager) StreamStats(ctx context.Context, name string, confi
}

outStream := config.OutStream
if (!c.IsRunning() || c.IsRestarting()) && !config.Stream {
if (!c.IsRunningOrPaused() || c.IsRestarting()) && !config.Stream {
return json.NewEncoder(outStream).Encode(&types.ContainerStats{
Name: c.Name,
ID: c.ID,
})
}

if c.IsRunning() && !config.Stream {
if c.IsRunningOrPaused() && !config.Stream {
metrics, stats, err := mgr.Stats(ctx, name)
if err != nil {
return err
}
containerStat := toContainerStats(metrics.Timestamp, stats)
containerStat := toContainerStats(metrics.Timestamp, c, stats)
return json.NewEncoder(outStream).Encode(containerStat)
}

Expand Down Expand Up @@ -63,7 +63,7 @@ func (mgr *ContainerManager) StreamStats(ctx context.Context, name string, confi
// metrics may be nil if the container is not running,
// so just ignore it and try get the metrics next time.
if metrics != nil {
containerStat := toContainerStats(metrics.Timestamp, stats)
containerStat := toContainerStats(metrics.Timestamp, c, stats)

if err := enc.Encode(containerStat); err != nil {
return err
Expand All @@ -87,7 +87,7 @@ func (mgr *ContainerManager) Stats(ctx context.Context, name string) (*container
c.Unlock()

// only get metrics when the container is running
if !(c.IsRunning() || c.IsPaused()) {
if !c.IsRunningOrPaused() {
return nil, nil, nil
}

Expand All @@ -104,9 +104,11 @@ func (mgr *ContainerManager) Stats(ctx context.Context, name string) (*container
return metric, v.(*cgroups.Metrics), nil
}

func toContainerStats(time time.Time, metric *cgroups.Metrics) *types.ContainerStats {
func toContainerStats(time time.Time, container *Container, metric *cgroups.Metrics) *types.ContainerStats {
return &types.ContainerStats{
Read: strfmt.DateTime(time),
ID: container.ID,
Name: container.Name,
PidsStats: &types.PidsStats{
Current: metric.Pids.Current,
},
Expand Down
58 changes: 58 additions & 0 deletions test/api_container_stats_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package main

import (
"fmt"
"net/http"

"github.com/alibaba/pouch/apis/types"
"github.com/alibaba/pouch/test/command"
"github.com/alibaba/pouch/test/environment"
"github.com/alibaba/pouch/test/request"

"github.com/go-check/check"
"github.com/gotestyourself/gotestyourself/icmd"
)

// APIContainerStatsSuite is the test suite for container stats API.
type APIContainerStatsSuite struct{}

func init() {
check.Suite(&APIContainerStatsSuite{})
}

// SetUpTest does common setup in the beginning of each test.
func (suite *APIContainerStatsSuite) SetUpTest(c *check.C) {
SkipIfFalse(c, environment.IsLinux)
PullImage(c, busyboxImage125)
}

// TestNoSuchContainer tests a container that doesn't exits return error.
func (suite *APIContainerStatsSuite) TestNoSuchContainer(c *check.C) {
resp, err := request.Get("/containers/nosuchcontainer/stats")
c.Assert(err, check.IsNil)
CheckRespStatus(c, resp, http.StatusNotFound)
}

// TestNoStream tests stats api without stream
func (suite *APIContainerStatsSuite) TestNoStream(c *check.C) {
name := "test_no_stream"
command.PouchRun("run", "-d", "--name", name, busyboxImage, "sh", "-c", "while true; do sleep 1; done").Assert(c, icmd.Success)
defer DelContainerForceMultyTime(c, name)

resp, err := request.Get(fmt.Sprintf("/containers/%s/stats", name))
c.Assert(err, check.IsNil)
CheckRespStatus(c, resp, http.StatusOK)

out := types.ContainerStats{}
err = request.DecodeBody(&out, resp.Body)
c.Assert(err, check.IsNil)

c.Assert(out.Name, check.Equals, name)
c.Assert(out.ID, check.NotNil)
c.Assert(out.Read, check.NotNil)
c.Assert(out.CPUStats, check.NotNil)
c.Assert(out.MemoryStats, check.NotNil)
c.Assert(out.BlkioStats, check.NotNil)
c.Assert(out.PidsStats, check.NotNil)
c.Assert(out.PrecpuStats, check.NotNil)
}

0 comments on commit ac9fa5d

Please sign in to comment.