mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-06-29 16:57:18 +00:00
cli: implement events command
Events cli display container events such as cpu, memory, and IO usage statistics. By now OOM notifications and intel RDT are not fully supproted. Fixes: #186 Signed-off-by: Haomin <caihaomin@huawei.com>
This commit is contained in:
parent
f1f534c6ae
commit
1205e347f2
270
cli/events.go
Normal file
270
cli/events.go
Normal file
@ -0,0 +1,270 @@
|
|||||||
|
// Copyright (c) 2014,2015,2016,2017 Docker, Inc.
|
||||||
|
// Copyright (c) 2018 Huawei Corporation.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
vc "github.com/kata-containers/runtime/virtcontainers"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
type event struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
Data interface{} `json:"data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// stats is the runc specific stats structure for stability when encoding and decoding stats.
|
||||||
|
type stats struct {
|
||||||
|
CPU cpu `json:"cpu"`
|
||||||
|
Memory memory `json:"memory"`
|
||||||
|
Pids pids `json:"pids"`
|
||||||
|
Blkio blkio `json:"blkio"`
|
||||||
|
Hugetlb map[string]hugetlb `json:"hugetlb"`
|
||||||
|
IntelRdt intelRdt `json:"intel_rdt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type hugetlb struct {
|
||||||
|
Usage uint64 `json:"usage,omitempty"`
|
||||||
|
Max uint64 `json:"max,omitempty"`
|
||||||
|
Failcnt uint64 `json:"failcnt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type blkioEntry struct {
|
||||||
|
Major uint64 `json:"major,omitempty"`
|
||||||
|
Minor uint64 `json:"minor,omitempty"`
|
||||||
|
Op string `json:"op,omitempty"`
|
||||||
|
Value uint64 `json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type blkio struct {
|
||||||
|
IoServiceBytesRecursive []blkioEntry `json:"ioServiceBytesRecursive,omitempty"`
|
||||||
|
IoServicedRecursive []blkioEntry `json:"ioServicedRecursive,omitempty"`
|
||||||
|
IoQueuedRecursive []blkioEntry `json:"ioQueueRecursive,omitempty"`
|
||||||
|
IoServiceTimeRecursive []blkioEntry `json:"ioServiceTimeRecursive,omitempty"`
|
||||||
|
IoWaitTimeRecursive []blkioEntry `json:"ioWaitTimeRecursive,omitempty"`
|
||||||
|
IoMergedRecursive []blkioEntry `json:"ioMergedRecursive,omitempty"`
|
||||||
|
IoTimeRecursive []blkioEntry `json:"ioTimeRecursive,omitempty"`
|
||||||
|
SectorsRecursive []blkioEntry `json:"sectorsRecursive,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type pids struct {
|
||||||
|
Current uint64 `json:"current,omitempty"`
|
||||||
|
Limit uint64 `json:"limit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type throttling struct {
|
||||||
|
Periods uint64 `json:"periods,omitempty"`
|
||||||
|
ThrottledPeriods uint64 `json:"throttledPeriods,omitempty"`
|
||||||
|
ThrottledTime uint64 `json:"throttledTime,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type cpuUsage struct {
|
||||||
|
// Units: nanoseconds.
|
||||||
|
Total uint64 `json:"total,omitempty"`
|
||||||
|
Percpu []uint64 `json:"percpu,omitempty"`
|
||||||
|
Kernel uint64 `json:"kernel"`
|
||||||
|
User uint64 `json:"user"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type cpu struct {
|
||||||
|
Usage cpuUsage `json:"usage,omitempty"`
|
||||||
|
Throttling throttling `json:"throttling,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type memoryEntry struct {
|
||||||
|
Limit uint64 `json:"limit"`
|
||||||
|
Usage uint64 `json:"usage,omitempty"`
|
||||||
|
Max uint64 `json:"max,omitempty"`
|
||||||
|
Failcnt uint64 `json:"failcnt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type memory struct {
|
||||||
|
Cache uint64 `json:"cache,omitempty"`
|
||||||
|
Usage memoryEntry `json:"usage,omitempty"`
|
||||||
|
Swap memoryEntry `json:"swap,omitempty"`
|
||||||
|
Kernel memoryEntry `json:"kernel,omitempty"`
|
||||||
|
KernelTCP memoryEntry `json:"kernelTCP,omitempty"`
|
||||||
|
Raw map[string]uint64 `json:"raw,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type l3CacheInfo struct {
|
||||||
|
CbmMask string `json:"cbm_mask,omitempty"`
|
||||||
|
MinCbmBits uint64 `json:"min_cbm_bits,omitempty"`
|
||||||
|
NumClosids uint64 `json:"num_closids,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type intelRdt struct {
|
||||||
|
// The read-only L3 cache information
|
||||||
|
L3CacheInfo *l3CacheInfo `json:"l3_cache_info,omitempty"`
|
||||||
|
|
||||||
|
// The read-only L3 cache schema in root
|
||||||
|
L3CacheSchemaRoot string `json:"l3_cache_schema_root,omitempty"`
|
||||||
|
|
||||||
|
// The L3 cache schema in 'container_id' group
|
||||||
|
L3CacheSchema string `json:"l3_cache_schema,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventsCLICommand = cli.Command{
|
||||||
|
Name: "events",
|
||||||
|
Usage: "display container events such as OOM notifications, cpu, memory, and IO usage statistics",
|
||||||
|
ArgsUsage: `<container-id>
|
||||||
|
|
||||||
|
Where "<container-id>" is the name for the instance of the container.`,
|
||||||
|
Description: `The events command displays information about the container. By default the
|
||||||
|
information is displayed once every 5 seconds.`,
|
||||||
|
Flags: []cli.Flag{
|
||||||
|
cli.DurationFlag{
|
||||||
|
Name: "interval",
|
||||||
|
Value: 5 * time.Second,
|
||||||
|
Usage: "set the stats collection interval",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "stats",
|
||||||
|
Usage: "display the container's stats then exit",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Action: func(context *cli.Context) error {
|
||||||
|
containerID := context.Args().First()
|
||||||
|
if containerID == "" {
|
||||||
|
return fmt.Errorf("container id cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
duration := context.Duration("interval")
|
||||||
|
if duration <= 0 {
|
||||||
|
return fmt.Errorf("duration interval must be greater than 0")
|
||||||
|
}
|
||||||
|
|
||||||
|
status, sandboxID, err := getExistingContainerInfo(containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if status.State.State == vc.StateStopped {
|
||||||
|
return fmt.Errorf("container with id %s is not running", status.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
events = make(chan *event, 1024)
|
||||||
|
group = &sync.WaitGroup{}
|
||||||
|
)
|
||||||
|
group.Add(1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer group.Done()
|
||||||
|
enc := json.NewEncoder(os.Stdout)
|
||||||
|
for e := range events {
|
||||||
|
if err := enc.Encode(e); err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
if context.Bool("stats") {
|
||||||
|
s, err := vci.StatsContainer(sandboxID, containerID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
events <- &event{Type: "stats", ID: status.ID, Data: convertVirtcontainerStats(&s)}
|
||||||
|
close(events)
|
||||||
|
group.Wait()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for range time.Tick(context.Duration("interval")) {
|
||||||
|
s, err := vci.StatsContainer(sandboxID, containerID)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
events <- &event{Type: "stats", ID: status.ID, Data: convertVirtcontainerStats(&s)}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
group.Wait()
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertVirtcontainerStats(containerStats *vc.ContainerStats) *stats {
|
||||||
|
cg := containerStats.CgroupStats
|
||||||
|
if cg == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var s stats
|
||||||
|
s.Pids.Current = cg.PidsStats.Current
|
||||||
|
s.Pids.Limit = cg.PidsStats.Limit
|
||||||
|
|
||||||
|
s.CPU.Usage.Kernel = cg.CPUStats.CPUUsage.UsageInKernelmode
|
||||||
|
s.CPU.Usage.User = cg.CPUStats.CPUUsage.UsageInUsermode
|
||||||
|
s.CPU.Usage.Total = cg.CPUStats.CPUUsage.TotalUsage
|
||||||
|
s.CPU.Usage.Percpu = cg.CPUStats.CPUUsage.PercpuUsage
|
||||||
|
s.CPU.Throttling.Periods = cg.CPUStats.ThrottlingData.Periods
|
||||||
|
s.CPU.Throttling.ThrottledPeriods = cg.CPUStats.ThrottlingData.ThrottledPeriods
|
||||||
|
s.CPU.Throttling.ThrottledTime = cg.CPUStats.ThrottlingData.ThrottledTime
|
||||||
|
|
||||||
|
s.Memory.Cache = cg.MemoryStats.Cache
|
||||||
|
s.Memory.Kernel = convertMemoryEntry(cg.MemoryStats.KernelUsage)
|
||||||
|
s.Memory.KernelTCP = convertMemoryEntry(cg.MemoryStats.KernelTCPUsage)
|
||||||
|
s.Memory.Swap = convertMemoryEntry(cg.MemoryStats.SwapUsage)
|
||||||
|
s.Memory.Usage = convertMemoryEntry(cg.MemoryStats.Usage)
|
||||||
|
s.Memory.Raw = cg.MemoryStats.Stats
|
||||||
|
|
||||||
|
s.Blkio.IoServiceBytesRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceBytesRecursive)
|
||||||
|
s.Blkio.IoServicedRecursive = convertBlkioEntry(cg.BlkioStats.IoServicedRecursive)
|
||||||
|
s.Blkio.IoQueuedRecursive = convertBlkioEntry(cg.BlkioStats.IoQueuedRecursive)
|
||||||
|
s.Blkio.IoServiceTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoServiceTimeRecursive)
|
||||||
|
s.Blkio.IoWaitTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoWaitTimeRecursive)
|
||||||
|
s.Blkio.IoMergedRecursive = convertBlkioEntry(cg.BlkioStats.IoMergedRecursive)
|
||||||
|
s.Blkio.IoTimeRecursive = convertBlkioEntry(cg.BlkioStats.IoTimeRecursive)
|
||||||
|
s.Blkio.SectorsRecursive = convertBlkioEntry(cg.BlkioStats.SectorsRecursive)
|
||||||
|
|
||||||
|
s.Hugetlb = make(map[string]hugetlb)
|
||||||
|
for k, v := range cg.HugetlbStats {
|
||||||
|
s.Hugetlb[k] = convertHugtlb(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertHugtlb(c vc.HugetlbStats) hugetlb {
|
||||||
|
return hugetlb{
|
||||||
|
Usage: c.Usage,
|
||||||
|
Max: c.MaxUsage,
|
||||||
|
Failcnt: c.Failcnt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertMemoryEntry(c vc.MemoryData) memoryEntry {
|
||||||
|
return memoryEntry{
|
||||||
|
Limit: c.Limit,
|
||||||
|
Usage: c.Usage,
|
||||||
|
Max: c.MaxUsage,
|
||||||
|
Failcnt: c.Failcnt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertBlkioEntry(c []vc.BlkioStatEntry) []blkioEntry {
|
||||||
|
var out []blkioEntry
|
||||||
|
for _, e := range c {
|
||||||
|
out = append(out, blkioEntry{
|
||||||
|
Major: e.Major,
|
||||||
|
Minor: e.Minor,
|
||||||
|
Op: e.Op,
|
||||||
|
Value: e.Value,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
141
cli/events_test.go
Normal file
141
cli/events_test.go
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// Copyright (c) 2018 Huawei Corporation.
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
//
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
vc "github.com/kata-containers/runtime/virtcontainers"
|
||||||
|
vcAnnotations "github.com/kata-containers/runtime/virtcontainers/pkg/annotations"
|
||||||
|
"github.com/kata-containers/runtime/virtcontainers/pkg/vcmock"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/urfave/cli"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEventsCliAction(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
// get Action function
|
||||||
|
actionFunc, ok := eventsCLICommand.Action.(func(ctx *cli.Context) error)
|
||||||
|
flagSet := flag.NewFlagSet("events", flag.ContinueOnError)
|
||||||
|
|
||||||
|
// create a new fake context
|
||||||
|
ctx := cli.NewContext(&cli.App{}, flagSet, nil)
|
||||||
|
assert.True(ok)
|
||||||
|
|
||||||
|
err := actionFunc(ctx)
|
||||||
|
assert.Error(err, "Missing container ID")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEventsCLIFailure(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
flagSet := flag.NewFlagSet("events", flag.ContinueOnError)
|
||||||
|
ctx := cli.NewContext(&cli.App{}, flagSet, nil)
|
||||||
|
|
||||||
|
actionFunc, ok := eventsCLICommand.Action.(func(ctx *cli.Context) error)
|
||||||
|
assert.True(ok)
|
||||||
|
|
||||||
|
// missing container ID
|
||||||
|
err := actionFunc(ctx)
|
||||||
|
assert.Error(err)
|
||||||
|
|
||||||
|
// interval is negative
|
||||||
|
flagSet.Parse([]string{testContainerID})
|
||||||
|
flagSet.Duration("interval", (-1)*time.Second, "")
|
||||||
|
ctx = cli.NewContext(&cli.App{}, flagSet, nil)
|
||||||
|
err = actionFunc(ctx)
|
||||||
|
assert.Error(err)
|
||||||
|
|
||||||
|
// interval is zero
|
||||||
|
flagSet = flag.NewFlagSet("events", flag.ContinueOnError)
|
||||||
|
flagSet.Parse([]string{testContainerID})
|
||||||
|
flagSet.Duration("interval", 0*time.Second, "")
|
||||||
|
ctx = cli.NewContext(&cli.App{}, flagSet, nil)
|
||||||
|
err = actionFunc(ctx)
|
||||||
|
assert.Error(err)
|
||||||
|
|
||||||
|
// not running
|
||||||
|
sandbox := &vcmock.Sandbox{
|
||||||
|
MockID: testContainerID,
|
||||||
|
}
|
||||||
|
|
||||||
|
sandbox.MockContainers = []*vcmock.Container{
|
||||||
|
{
|
||||||
|
MockID: sandbox.ID(),
|
||||||
|
MockSandbox: sandbox,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testingImpl.StatusContainerFunc = func(sandboxID, containerID string) (vc.ContainerStatus, error) {
|
||||||
|
return vc.ContainerStatus{
|
||||||
|
ID: sandbox.ID(),
|
||||||
|
Annotations: map[string]string{
|
||||||
|
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
testingImpl.StatusContainerFunc = nil
|
||||||
|
}()
|
||||||
|
err = actionFunc(ctx)
|
||||||
|
assert.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEventsCLISuccessful(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
sandbox := &vcmock.Sandbox{
|
||||||
|
MockID: testContainerID,
|
||||||
|
}
|
||||||
|
|
||||||
|
sandbox.MockContainers = []*vcmock.Container{
|
||||||
|
{
|
||||||
|
MockID: sandbox.ID(),
|
||||||
|
MockSandbox: sandbox,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
testingImpl.StatusContainerFunc = func(sandboxID, containerID string) (vc.ContainerStatus, error) {
|
||||||
|
return vc.ContainerStatus{
|
||||||
|
ID: sandbox.ID(),
|
||||||
|
Annotations: map[string]string{
|
||||||
|
vcAnnotations.ContainerTypeKey: string(vc.PodContainer),
|
||||||
|
},
|
||||||
|
State: vc.State{
|
||||||
|
State: vc.StateRunning,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
testingImpl.StatsContainerFunc = func(sandboxID, containerID string) (vc.ContainerStats, error) {
|
||||||
|
return vc.ContainerStats{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
testingImpl.StatusContainerFunc = nil
|
||||||
|
testingImpl.StatsContainerFunc = nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
path, err := createTempContainerIDMapping(sandbox.ID(), sandbox.ID())
|
||||||
|
assert.NoError(err)
|
||||||
|
defer os.RemoveAll(path)
|
||||||
|
|
||||||
|
actionFunc, ok := eventsCLICommand.Action.(func(ctx *cli.Context) error)
|
||||||
|
assert.True(ok)
|
||||||
|
|
||||||
|
flagSet := flag.NewFlagSet("events", flag.ContinueOnError)
|
||||||
|
flagSet.Parse([]string{testContainerID})
|
||||||
|
flagSet.Duration("interval", 5*time.Second, "")
|
||||||
|
flagSet.Bool("stats", true, "")
|
||||||
|
ctx := cli.NewContext(&cli.App{}, flagSet, nil)
|
||||||
|
err = actionFunc(ctx)
|
||||||
|
assert.NoError(err)
|
||||||
|
}
|
@ -117,6 +117,7 @@ var runtimeCommands = []cli.Command{
|
|||||||
startCLICommand,
|
startCLICommand,
|
||||||
stateCLICommand,
|
stateCLICommand,
|
||||||
updateCLICommand,
|
updateCLICommand,
|
||||||
|
eventsCLICommand,
|
||||||
versionCLICommand,
|
versionCLICommand,
|
||||||
|
|
||||||
// Kata Containers specific extensions
|
// Kata Containers specific extensions
|
||||||
|
@ -194,4 +194,7 @@ type agent interface {
|
|||||||
// This function should be called after hot adding vCPUs or Memory.
|
// This function should be called after hot adding vCPUs or Memory.
|
||||||
// cpus specifies the number of CPUs that were added and the agent should online
|
// cpus specifies the number of CPUs that were added and the agent should online
|
||||||
onlineCPUMem(cpus uint32) error
|
onlineCPUMem(cpus uint32) error
|
||||||
|
|
||||||
|
// statsContainer will tell the agent to get stats from a container related to a Sandbox
|
||||||
|
statsContainer(sandbox *Sandbox, c Container) (*ContainerStats, error)
|
||||||
}
|
}
|
||||||
|
@ -628,3 +628,28 @@ func UpdateContainer(sandboxID, containerID string, resources specs.LinuxResourc
|
|||||||
|
|
||||||
return s.UpdateContainer(containerID, resources)
|
return s.UpdateContainer(containerID, resources)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatsContainer is the virtcontainers container stats entry point.
|
||||||
|
// StatsContainer returns a detailed container stats.
|
||||||
|
func StatsContainer(sandboxID, containerID string) (ContainerStats, error) {
|
||||||
|
if sandboxID == "" {
|
||||||
|
return ContainerStats{}, errNeedSandboxID
|
||||||
|
}
|
||||||
|
|
||||||
|
if containerID == "" {
|
||||||
|
return ContainerStats{}, errNeedContainerID
|
||||||
|
}
|
||||||
|
lockFile, err := rLockSandbox(sandboxID)
|
||||||
|
if err != nil {
|
||||||
|
return ContainerStats{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer unlockSandbox(lockFile)
|
||||||
|
|
||||||
|
s, err := fetchSandbox(sandboxID)
|
||||||
|
if err != nil {
|
||||||
|
return ContainerStats{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.StatsContainer(containerID)
|
||||||
|
}
|
||||||
|
@ -1934,6 +1934,74 @@ func TestStatusContainerFailing(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStatsContainerFailing(t *testing.T) {
|
||||||
|
cleanUp()
|
||||||
|
|
||||||
|
contID := "100"
|
||||||
|
config := newTestSandboxConfigNoop()
|
||||||
|
|
||||||
|
p, err := CreateSandbox(config)
|
||||||
|
if p == nil || err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pImpl, ok := p.(*Sandbox)
|
||||||
|
assert.True(t, ok)
|
||||||
|
|
||||||
|
os.RemoveAll(pImpl.configPath)
|
||||||
|
globalSandboxList.removeSandbox(p.ID())
|
||||||
|
|
||||||
|
_, err = StatsContainer(p.ID(), contID)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStatsContainer(t *testing.T) {
|
||||||
|
cleanUp()
|
||||||
|
|
||||||
|
assert := assert.New(t)
|
||||||
|
contID := "100"
|
||||||
|
|
||||||
|
_, err := StatsContainer("", "")
|
||||||
|
assert.Error(err)
|
||||||
|
|
||||||
|
_, err = StatsContainer("abc", "")
|
||||||
|
assert.Error(err)
|
||||||
|
|
||||||
|
_, err = StatsContainer("abc", "abc")
|
||||||
|
assert.Error(err)
|
||||||
|
|
||||||
|
config := newTestSandboxConfigNoop()
|
||||||
|
p, err := CreateSandbox(config)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.NotNil(p)
|
||||||
|
|
||||||
|
p, err = StartSandbox(p.ID())
|
||||||
|
if p == nil || err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
pImpl, ok := p.(*Sandbox)
|
||||||
|
assert.True(ok)
|
||||||
|
defer os.RemoveAll(pImpl.configPath)
|
||||||
|
|
||||||
|
contConfig := newTestContainerConfigNoop(contID)
|
||||||
|
_, c, err := CreateContainer(p.ID(), contConfig)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.NotNil(c)
|
||||||
|
|
||||||
|
_, err = StatsContainer(pImpl.id, "xyz")
|
||||||
|
assert.Error(err)
|
||||||
|
|
||||||
|
_, err = StatsContainer("xyz", contID)
|
||||||
|
assert.Error(err)
|
||||||
|
|
||||||
|
stats, err := StatsContainer(pImpl.id, contID)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(stats, ContainerStats{})
|
||||||
|
}
|
||||||
|
|
||||||
func TestProcessListContainer(t *testing.T) {
|
func TestProcessListContainer(t *testing.T) {
|
||||||
cleanUp()
|
cleanUp()
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
// +build linux
|
||||||
// Copyright (c) 2016 Intel Corporation
|
// Copyright (c) 2016 Intel Corporation
|
||||||
//
|
// Copyright (c) 2014,2015,2016,2017 Docker, Inc.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
@ -55,6 +56,119 @@ type ContainerStatus struct {
|
|||||||
Annotations map[string]string
|
Annotations map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ThrottlingData gather the date related to container cpu throttling.
|
||||||
|
type ThrottlingData struct {
|
||||||
|
// Number of periods with throttling active
|
||||||
|
Periods uint64 `json:"periods,omitempty"`
|
||||||
|
// Number of periods when the container hit its throttling limit.
|
||||||
|
ThrottledPeriods uint64 `json:"throttled_periods,omitempty"`
|
||||||
|
// Aggregate time the container was throttled for in nanoseconds.
|
||||||
|
ThrottledTime uint64 `json:"throttled_time,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPUUsage denotes the usage of a CPU.
|
||||||
|
// All CPU stats are aggregate since container inception.
|
||||||
|
type CPUUsage struct {
|
||||||
|
// Total CPU time consumed.
|
||||||
|
// Units: nanoseconds.
|
||||||
|
TotalUsage uint64 `json:"total_usage,omitempty"`
|
||||||
|
// Total CPU time consumed per core.
|
||||||
|
// Units: nanoseconds.
|
||||||
|
PercpuUsage []uint64 `json:"percpu_usage,omitempty"`
|
||||||
|
// Time spent by tasks of the cgroup in kernel mode.
|
||||||
|
// Units: nanoseconds.
|
||||||
|
UsageInKernelmode uint64 `json:"usage_in_kernelmode"`
|
||||||
|
// Time spent by tasks of the cgroup in user mode.
|
||||||
|
// Units: nanoseconds.
|
||||||
|
UsageInUsermode uint64 `json:"usage_in_usermode"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPUStats describes the cpu stats
|
||||||
|
type CPUStats struct {
|
||||||
|
CPUUsage CPUUsage `json:"cpu_usage,omitempty"`
|
||||||
|
ThrottlingData ThrottlingData `json:"throttling_data,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemoryData gather the data related to memory
|
||||||
|
type MemoryData struct {
|
||||||
|
Usage uint64 `json:"usage,omitempty"`
|
||||||
|
MaxUsage uint64 `json:"max_usage,omitempty"`
|
||||||
|
Failcnt uint64 `json:"failcnt"`
|
||||||
|
Limit uint64 `json:"limit"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemoryStats describes the memory stats
|
||||||
|
type MemoryStats struct {
|
||||||
|
// memory used for cache
|
||||||
|
Cache uint64 `json:"cache,omitempty"`
|
||||||
|
// usage of memory
|
||||||
|
Usage MemoryData `json:"usage,omitempty"`
|
||||||
|
// usage of memory swap
|
||||||
|
SwapUsage MemoryData `json:"swap_usage,omitempty"`
|
||||||
|
// usage of kernel memory
|
||||||
|
KernelUsage MemoryData `json:"kernel_usage,omitempty"`
|
||||||
|
// usage of kernel TCP memory
|
||||||
|
KernelTCPUsage MemoryData `json:"kernel_tcp_usage,omitempty"`
|
||||||
|
// if true, memory usage is accounted for throughout a hierarchy of cgroups.
|
||||||
|
UseHierarchy bool `json:"use_hierarchy"`
|
||||||
|
|
||||||
|
Stats map[string]uint64 `json:"stats,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// PidsStats describes the pids stats
|
||||||
|
type PidsStats struct {
|
||||||
|
// number of pids in the cgroup
|
||||||
|
Current uint64 `json:"current,omitempty"`
|
||||||
|
// active pids hard limit
|
||||||
|
Limit uint64 `json:"limit,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlkioStatEntry gather date related to a block device
|
||||||
|
type BlkioStatEntry struct {
|
||||||
|
Major uint64 `json:"major,omitempty"`
|
||||||
|
Minor uint64 `json:"minor,omitempty"`
|
||||||
|
Op string `json:"op,omitempty"`
|
||||||
|
Value uint64 `json:"value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BlkioStats describes block io stats
|
||||||
|
type BlkioStats struct {
|
||||||
|
// number of bytes tranferred to and from the block device
|
||||||
|
IoServiceBytesRecursive []BlkioStatEntry `json:"io_service_bytes_recursive,omitempty"`
|
||||||
|
IoServicedRecursive []BlkioStatEntry `json:"io_serviced_recursive,omitempty"`
|
||||||
|
IoQueuedRecursive []BlkioStatEntry `json:"io_queue_recursive,omitempty"`
|
||||||
|
IoServiceTimeRecursive []BlkioStatEntry `json:"io_service_time_recursive,omitempty"`
|
||||||
|
IoWaitTimeRecursive []BlkioStatEntry `json:"io_wait_time_recursive,omitempty"`
|
||||||
|
IoMergedRecursive []BlkioStatEntry `json:"io_merged_recursive,omitempty"`
|
||||||
|
IoTimeRecursive []BlkioStatEntry `json:"io_time_recursive,omitempty"`
|
||||||
|
SectorsRecursive []BlkioStatEntry `json:"sectors_recursive,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HugetlbStats describes hugetable memory stats
|
||||||
|
type HugetlbStats struct {
|
||||||
|
// current res_counter usage for hugetlb
|
||||||
|
Usage uint64 `json:"usage,omitempty"`
|
||||||
|
// maximum usage ever recorded.
|
||||||
|
MaxUsage uint64 `json:"max_usage,omitempty"`
|
||||||
|
// number of times hugetlb usage allocation failure.
|
||||||
|
Failcnt uint64 `json:"failcnt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CgroupStats describes all cgroup subsystem stats
|
||||||
|
type CgroupStats struct {
|
||||||
|
CPUStats CPUStats `json:"cpu_stats,omitempty"`
|
||||||
|
MemoryStats MemoryStats `json:"memory_stats,omitempty"`
|
||||||
|
PidsStats PidsStats `json:"pids_stats,omitempty"`
|
||||||
|
BlkioStats BlkioStats `json:"blkio_stats,omitempty"`
|
||||||
|
// the map is in the format "size of hugepage: stats of the hugepage"
|
||||||
|
HugetlbStats map[string]HugetlbStats `json:"hugetlb_stats,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContainerStats describes a container stats.
|
||||||
|
type ContainerStats struct {
|
||||||
|
CgroupStats *CgroupStats
|
||||||
|
}
|
||||||
|
|
||||||
// ContainerResources describes container resources
|
// ContainerResources describes container resources
|
||||||
type ContainerResources struct {
|
type ContainerResources struct {
|
||||||
// CPUQuota specifies the total amount of time in microseconds
|
// CPUQuota specifies the total amount of time in microseconds
|
||||||
@ -788,6 +902,13 @@ func (c *Container) processList(options ProcessListOptions) (ProcessList, error)
|
|||||||
return c.sandbox.agent.processListContainer(c.sandbox, *c, options)
|
return c.sandbox.agent.processListContainer(c.sandbox, *c, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Container) stats() (*ContainerStats, error) {
|
||||||
|
if err := c.checkSandboxRunning("stats"); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.sandbox.agent.statsContainer(c.sandbox, *c)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Container) update(resources specs.LinuxResources) error {
|
func (c *Container) update(resources specs.LinuxResources) error {
|
||||||
if err := c.checkSandboxRunning("update"); err != nil {
|
if err := c.checkSandboxRunning("update"); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -609,6 +609,11 @@ func (h *hyper) processListContainer(sandbox *Sandbox, c Container, options Proc
|
|||||||
return h.processListOneContainer(sandbox.id, c.id, options)
|
return h.processListOneContainer(sandbox.id, c.id, options)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// statsContainer is the hyperstart agent Container stats implementation. It does nothing.
|
||||||
|
func (h *hyper) statsContainer(sandbox *Sandbox, c Container) (*ContainerStats, error) {
|
||||||
|
return &ContainerStats{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (h *hyper) updateContainer(sandbox *Sandbox, c Container, resources specs.LinuxResources) error {
|
func (h *hyper) updateContainer(sandbox *Sandbox, c Container, resources specs.LinuxResources) error {
|
||||||
// hyperstart-agent does not support update
|
// hyperstart-agent does not support update
|
||||||
return nil
|
return nil
|
||||||
|
@ -106,6 +106,11 @@ func (impl *VCImpl) StatusContainer(sandboxID, containerID string) (ContainerSta
|
|||||||
return StatusContainer(sandboxID, containerID)
|
return StatusContainer(sandboxID, containerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatsContainer implements the VC function of the same name.
|
||||||
|
func (impl *VCImpl) StatsContainer(sandboxID, containerID string) (ContainerStats, error) {
|
||||||
|
return StatsContainer(sandboxID, containerID)
|
||||||
|
}
|
||||||
|
|
||||||
// KillContainer implements the VC function of the same name.
|
// KillContainer implements the VC function of the same name.
|
||||||
func (impl *VCImpl) KillContainer(sandboxID, containerID string, signal syscall.Signal, all bool) error {
|
func (impl *VCImpl) KillContainer(sandboxID, containerID string, signal syscall.Signal, all bool) error {
|
||||||
return KillContainer(sandboxID, containerID, signal, all)
|
return KillContainer(sandboxID, containerID, signal, all)
|
||||||
|
@ -34,6 +34,7 @@ type VC interface {
|
|||||||
KillContainer(sandboxID, containerID string, signal syscall.Signal, all bool) error
|
KillContainer(sandboxID, containerID string, signal syscall.Signal, all bool) error
|
||||||
StartContainer(sandboxID, containerID string) (VCContainer, error)
|
StartContainer(sandboxID, containerID string) (VCContainer, error)
|
||||||
StatusContainer(sandboxID, containerID string) (ContainerStatus, error)
|
StatusContainer(sandboxID, containerID string) (ContainerStatus, error)
|
||||||
|
StatsContainer(sandboxID, containerID string) (ContainerStats, error)
|
||||||
StopContainer(sandboxID, containerID string) (VCContainer, error)
|
StopContainer(sandboxID, containerID string) (VCContainer, error)
|
||||||
ProcessListContainer(sandboxID, containerID string, options ProcessListOptions) (ProcessList, error)
|
ProcessListContainer(sandboxID, containerID string, options ProcessListOptions) (ProcessList, error)
|
||||||
UpdateContainer(sandboxID, containerID string, resources specs.LinuxResources) error
|
UpdateContainer(sandboxID, containerID string, resources specs.LinuxResources) error
|
||||||
@ -59,6 +60,7 @@ type VCSandbox interface {
|
|||||||
DeleteContainer(contID string) (VCContainer, error)
|
DeleteContainer(contID string) (VCContainer, error)
|
||||||
StartContainer(containerID string) (VCContainer, error)
|
StartContainer(containerID string) (VCContainer, error)
|
||||||
StatusContainer(containerID string) (ContainerStatus, error)
|
StatusContainer(containerID string) (ContainerStatus, error)
|
||||||
|
StatsContainer(containerID string) (ContainerStats, error)
|
||||||
EnterContainer(containerID string, cmd Cmd) (VCContainer, *Process, error)
|
EnterContainer(containerID string, cmd Cmd) (VCContainer, *Process, error)
|
||||||
UpdateContainer(containerID string, resources specs.LinuxResources) error
|
UpdateContainer(containerID string, resources specs.LinuxResources) error
|
||||||
WaitProcess(containerID, processID string) (int32, error)
|
WaitProcess(containerID, processID string) (int32, error)
|
||||||
|
@ -941,6 +941,38 @@ func (k *kataAgent) onlineCPUMem(cpus uint32) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (k *kataAgent) statsContainer(sandbox *Sandbox, c Container) (*ContainerStats, error) {
|
||||||
|
req := &grpc.StatsContainerRequest{
|
||||||
|
ContainerId: c.id,
|
||||||
|
}
|
||||||
|
|
||||||
|
returnStats, err := k.sendReq(req)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stats, ok := returnStats.(*grpc.StatsContainerResponse)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("irregular response container stats")
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.Marshal(stats.CgroupStats)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var cgroupStats CgroupStats
|
||||||
|
err = json.Unmarshal(data, &cgroupStats)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
containerStats := &ContainerStats{
|
||||||
|
CgroupStats: &cgroupStats,
|
||||||
|
}
|
||||||
|
return containerStats, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (k *kataAgent) connect() error {
|
func (k *kataAgent) connect() error {
|
||||||
if k.client != nil {
|
if k.client != nil {
|
||||||
return nil
|
return nil
|
||||||
@ -1069,6 +1101,9 @@ func (k *kataAgent) installReqFunc(c *kataclient.AgentClient) {
|
|||||||
k.reqHandlers["grpc.CloseStdinRequest"] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) {
|
k.reqHandlers["grpc.CloseStdinRequest"] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) {
|
||||||
return k.client.CloseStdin(ctx, req.(*grpc.CloseStdinRequest), opts...)
|
return k.client.CloseStdin(ctx, req.(*grpc.CloseStdinRequest), opts...)
|
||||||
}
|
}
|
||||||
|
k.reqHandlers["grpc.StatsContainerRequest"] = func(ctx context.Context, req interface{}, opts ...golangGrpc.CallOption) (interface{}, error) {
|
||||||
|
return k.client.StatsContainer(ctx, req.(*grpc.StatsContainerRequest), opts...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (k *kataAgent) sendReq(request interface{}) (interface{}, error) {
|
func (k *kataAgent) sendReq(request interface{}) (interface{}, error) {
|
||||||
|
@ -199,6 +199,10 @@ func (p *gRPCProxy) OnlineCPUMem(ctx context.Context, req *pb.OnlineCPUMemReques
|
|||||||
return emptyResp, nil
|
return emptyResp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *gRPCProxy) StatsContainer(ctx context.Context, req *pb.StatsContainerRequest) (*pb.StatsContainerResponse, error) {
|
||||||
|
return &pb.StatsContainerResponse{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (p *gRPCProxy) Check(ctx context.Context, req *pb.CheckRequest) (*pb.HealthCheckResponse, error) {
|
func (p *gRPCProxy) Check(ctx context.Context, req *pb.CheckRequest) (*pb.HealthCheckResponse, error) {
|
||||||
return &pb.HealthCheckResponse{}, nil
|
return &pb.HealthCheckResponse{}, nil
|
||||||
}
|
}
|
||||||
@ -226,6 +230,7 @@ var reqList = []interface{}{
|
|||||||
&pb.SignalProcessRequest{},
|
&pb.SignalProcessRequest{},
|
||||||
&pb.CheckRequest{},
|
&pb.CheckRequest{},
|
||||||
&pb.WaitProcessRequest{},
|
&pb.WaitProcessRequest{},
|
||||||
|
&pb.StatsContainerRequest{},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestKataAgentSendReq(t *testing.T) {
|
func TestKataAgentSendReq(t *testing.T) {
|
||||||
|
@ -91,6 +91,11 @@ func (n *noopAgent) check() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// statsContainer is the Noop agent Container stats implementation. It does nothing.
|
||||||
|
func (n *noopAgent) statsContainer(sandbox *Sandbox, c Container) (*ContainerStats, error) {
|
||||||
|
return &ContainerStats{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// waitProcess is the Noop agent process waiter. It does nothing.
|
// waitProcess is the Noop agent process waiter. It does nothing.
|
||||||
func (n *noopAgent) waitProcess(c *Container, processID string) (int32, error) {
|
func (n *noopAgent) waitProcess(c *Container, processID string) (int32, error) {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
|
@ -117,3 +117,16 @@ func TestNoopAgentStopContainer(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNoopAgentStatsContainer(t *testing.T) {
|
||||||
|
n := &noopAgent{}
|
||||||
|
sandbox, container, err := testCreateNoopContainer()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer cleanUp()
|
||||||
|
_, err = n.statsContainer(sandbox, *container)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -179,6 +179,15 @@ func (m *VCMock) StatusContainer(sandboxID, containerID string) (vc.ContainerSta
|
|||||||
return vc.ContainerStatus{}, fmt.Errorf("%s: %s (%+v): sandboxID: %v, containerID: %v", mockErrorPrefix, getSelf(), m, sandboxID, containerID)
|
return vc.ContainerStatus{}, fmt.Errorf("%s: %s (%+v): sandboxID: %v, containerID: %v", mockErrorPrefix, getSelf(), m, sandboxID, containerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatsContainer implements the VC function of the same name.
|
||||||
|
func (m *VCMock) StatsContainer(sandboxID, containerID string) (vc.ContainerStats, error) {
|
||||||
|
if m.StatsContainerFunc != nil {
|
||||||
|
return m.StatsContainerFunc(sandboxID, containerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return vc.ContainerStats{}, fmt.Errorf("%s: %s (%+v): sandboxID: %v, containerID: %v", mockErrorPrefix, getSelf(), m, sandboxID, containerID)
|
||||||
|
}
|
||||||
|
|
||||||
// KillContainer implements the VC function of the same name.
|
// KillContainer implements the VC function of the same name.
|
||||||
func (m *VCMock) KillContainer(sandboxID, containerID string, signal syscall.Signal, all bool) error {
|
func (m *VCMock) KillContainer(sandboxID, containerID string, signal syscall.Signal, all bool) error {
|
||||||
if m.KillContainerFunc != nil {
|
if m.KillContainerFunc != nil {
|
||||||
|
@ -510,6 +510,33 @@ func TestVCMockStatusContainer(t *testing.T) {
|
|||||||
assert.True(IsMockError(err))
|
assert.True(IsMockError(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVCMockStatsContainer(t *testing.T) {
|
||||||
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
m := &VCMock{}
|
||||||
|
assert.Nil(m.StatsContainerFunc)
|
||||||
|
|
||||||
|
_, err := m.StatsContainer(testSandboxID, testContainerID)
|
||||||
|
|
||||||
|
assert.Error(err)
|
||||||
|
assert.True(IsMockError(err))
|
||||||
|
|
||||||
|
m.StatsContainerFunc = func(sandboxID, containerID string) (vc.ContainerStats, error) {
|
||||||
|
return vc.ContainerStats{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
stats, err := m.StatsContainer(testSandboxID, testContainerID)
|
||||||
|
assert.NoError(err)
|
||||||
|
assert.Equal(stats, vc.ContainerStats{})
|
||||||
|
|
||||||
|
// reset
|
||||||
|
m.StatsContainerFunc = nil
|
||||||
|
|
||||||
|
_, err = m.StatsContainer(testSandboxID, testContainerID)
|
||||||
|
assert.Error(err)
|
||||||
|
assert.True(IsMockError(err))
|
||||||
|
}
|
||||||
|
|
||||||
func TestVCMockStopContainer(t *testing.T) {
|
func TestVCMockStopContainer(t *testing.T) {
|
||||||
assert := assert.New(t)
|
assert := assert.New(t)
|
||||||
|
|
||||||
|
@ -94,6 +94,11 @@ func (p *Sandbox) StatusContainer(contID string) (vc.ContainerStatus, error) {
|
|||||||
return vc.ContainerStatus{}, nil
|
return vc.ContainerStatus{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatsContainer implements the VCSandbox function of the same name.
|
||||||
|
func (p *Sandbox) StatsContainer(contID string) (vc.ContainerStats, error) {
|
||||||
|
return vc.ContainerStats{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Status implements the VCSandbox function of the same name.
|
// Status implements the VCSandbox function of the same name.
|
||||||
func (p *Sandbox) Status() vc.SandboxStatus {
|
func (p *Sandbox) Status() vc.SandboxStatus {
|
||||||
return vc.SandboxStatus{}
|
return vc.SandboxStatus{}
|
||||||
|
@ -37,16 +37,17 @@ type Container struct {
|
|||||||
type VCMock struct {
|
type VCMock struct {
|
||||||
SetLoggerFunc func(logger logrus.FieldLogger)
|
SetLoggerFunc func(logger logrus.FieldLogger)
|
||||||
|
|
||||||
CreateSandboxFunc func(sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error)
|
CreateSandboxFunc func(sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error)
|
||||||
DeleteSandboxFunc func(sandboxID string) (vc.VCSandbox, error)
|
DeleteSandboxFunc func(sandboxID string) (vc.VCSandbox, error)
|
||||||
ListSandboxFunc func() ([]vc.SandboxStatus, error)
|
ListSandboxFunc func() ([]vc.SandboxStatus, error)
|
||||||
FetchSandboxFunc func(sandboxID string) (vc.VCSandbox, error)
|
FetchSandboxFunc func(sandboxID string) (vc.VCSandbox, error)
|
||||||
PauseSandboxFunc func(sandboxID string) (vc.VCSandbox, error)
|
PauseSandboxFunc func(sandboxID string) (vc.VCSandbox, error)
|
||||||
ResumeSandboxFunc func(sandboxID string) (vc.VCSandbox, error)
|
ResumeSandboxFunc func(sandboxID string) (vc.VCSandbox, error)
|
||||||
RunSandboxFunc func(sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error)
|
RunSandboxFunc func(sandboxConfig vc.SandboxConfig) (vc.VCSandbox, error)
|
||||||
StartSandboxFunc func(sandboxID string) (vc.VCSandbox, error)
|
StartSandboxFunc func(sandboxID string) (vc.VCSandbox, error)
|
||||||
StatusSandboxFunc func(sandboxID string) (vc.SandboxStatus, error)
|
StatusSandboxFunc func(sandboxID string) (vc.SandboxStatus, error)
|
||||||
StopSandboxFunc func(sandboxID string) (vc.VCSandbox, error)
|
StatsContainerFunc func(sandboxID, containerID string) (vc.ContainerStats, error)
|
||||||
|
StopSandboxFunc func(sandboxID string) (vc.VCSandbox, error)
|
||||||
|
|
||||||
CreateContainerFunc func(sandboxID string, containerConfig vc.ContainerConfig) (vc.VCSandbox, vc.VCContainer, error)
|
CreateContainerFunc func(sandboxID string, containerConfig vc.ContainerConfig) (vc.VCSandbox, vc.VCContainer, error)
|
||||||
DeleteContainerFunc func(sandboxID, containerID string) (vc.VCContainer, error)
|
DeleteContainerFunc func(sandboxID, containerID string) (vc.VCContainer, error)
|
||||||
|
@ -1101,6 +1101,21 @@ func (s *Sandbox) UpdateContainer(containerID string, resources specs.LinuxResou
|
|||||||
return c.update(resources)
|
return c.update(resources)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StatsContainer return the stats of a running container
|
||||||
|
func (s *Sandbox) StatsContainer(containerID string) (ContainerStats, error) {
|
||||||
|
// Fetch the container.
|
||||||
|
c, err := s.findContainer(containerID)
|
||||||
|
if err != nil {
|
||||||
|
return ContainerStats{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
stats, err := c.stats()
|
||||||
|
if err != nil {
|
||||||
|
return ContainerStats{}, err
|
||||||
|
}
|
||||||
|
return *stats, nil
|
||||||
|
}
|
||||||
|
|
||||||
// createContainers registers all containers to the proxy, create the
|
// createContainers registers all containers to the proxy, create the
|
||||||
// containers in the guest and starts one shim per container.
|
// containers in the guest and starts one shim per container.
|
||||||
func (s *Sandbox) createContainers() error {
|
func (s *Sandbox) createContainers() error {
|
||||||
|
Loading…
Reference in New Issue
Block a user