internal/test/builders/config.go:36:15: captLocal: `ID' should not be capitalized (gocritic)
func ConfigID(ID string) func(config *swarm.Config) {
^
internal/test/builders/secret.go:45:15: captLocal: `ID' should not be capitalized (gocritic)
func SecretID(ID string) func(secret *swarm.Secret) {
^
internal/test/builders/service.go:21:16: captLocal: `ID' should not be capitalized (gocritic)
func ServiceID(ID string) func(*swarm.Service) {
^
cli/command/image/formatter_history.go💯15: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(c.h.CreatedBy, "\t", " ", -1)` (gocritic)
createdBy := strings.Replace(c.h.CreatedBy, "\t", " ", -1)
^
e2e/image/push_test.go:246:34: badCall: suspicious Join on 1 argument (gocritic)
assert.NilError(t, os.RemoveAll(filepath.Join(dir.Join("trust"))))
^
e2e/image/push_test.go:313:34: badCall: suspicious Join on 1 argument (gocritic)
assert.NilError(t, os.RemoveAll(filepath.Join(dir.Join("trust"))))
^
cli/config/configfile/file_test.go:185:2: assignOp: replace `c.GetAllCallCount = c.GetAllCallCount + 1` with `c.GetAllCallCount++` (gocritic)
c.GetAllCallCount = c.GetAllCallCount + 1
^
cli/command/context/inspect_test.go:20:58: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(si.MetadataPath, `\`, `\\`, -1)` (gocritic)
expected = strings.Replace(expected, "<METADATA_PATH>", strings.Replace(si.MetadataPath, `\`, `\\`, -1), 1)
^
cli/command/context/inspect_test.go:21:53: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(si.TLSPath, `\`, `\\`, -1)` (gocritic)
expected = strings.Replace(expected, "<TLS_PATH>", strings.Replace(si.TLSPath, `\`, `\\`, -1), 1)
^
cli/command/container/formatter_stats.go:119:46: captLocal: `Stats' should not be capitalized (gocritic)
func statsFormatWrite(ctx formatter.Context, Stats []StatsEntry, osType string, trunc bool) error {
^
cli/command/container/stats_helpers.go:209:4: assignOp: replace `blkRead = blkRead + bioEntry.Value` with `blkRead += bioEntry.Value` (gocritic)
blkRead = blkRead + bioEntry.Value
^
cli/command/container/stats_helpers.go:211:4: assignOp: replace `blkWrite = blkWrite + bioEntry.Value` with `blkWrite += bioEntry.Value` (gocritic)
blkWrite = blkWrite + bioEntry.Value
^
cli/command/registry/formatter_search.go:67:10: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(c.s.Description, "\n", " ", -1)` (gocritic)
desc := strings.Replace(c.s.Description, "\n", " ", -1)
^
cli/command/registry/formatter_search.go:68:9: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(desc, "\r", " ", -1)` (gocritic)
desc = strings.Replace(desc, "\r", " ", -1)
^
cli/command/service/list_test.go:164:5: assignOp: replace `tc.doc = tc.doc + " with quiet"` with `tc.doc += " with quiet"` (gocritic)
tc.doc = tc.doc + " with quiet"
^
cli/command/service/progress/progress.go:274:11: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(errMsg, "\n", " ", -1)` (gocritic)
errMsg = strings.Replace(errMsg, "\n", " ", -1)
^
cli/manifest/store/store.go:153:9: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(fileName, "/", "_", -1)` (gocritic)
return strings.Replace(fileName, "/", "_", -1)
^
cli/manifest/store/store.go:152:14: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(ref, ":", "-", -1)` (gocritic)
fileName := strings.Replace(ref, ":", "-", -1)
^
cli/command/plugin/formatter.go:79:10: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(c.p.Config.Description, "\n", "", -1)` (gocritic)
desc := strings.Replace(c.p.Config.Description, "\n", "", -1)
^
cli/command/plugin/formatter.go:80:9: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(desc, "\r", "", -1)` (gocritic)
desc = strings.Replace(desc, "\r", "", -1)
^
cli/compose/convert/service.go:642:23: captLocal: `DNS' should not be capitalized (gocritic)
func convertDNSConfig(DNS []string, DNSSearch []string) *swarm.DNSConfig {
^
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
230 lines
5.7 KiB
Go
230 lines
5.7 KiB
Go
package container
|
|
|
|
import (
|
|
"strconv"
|
|
"sync"
|
|
|
|
"github.com/docker/cli/cli/command/formatter"
|
|
"github.com/docker/docker/pkg/stringid"
|
|
units "github.com/docker/go-units"
|
|
)
|
|
|
|
const (
|
|
winOSType = "windows"
|
|
defaultStatsTableFormat = "table {{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDs}}"
|
|
winDefaultStatsTableFormat = "table {{.ID}}\t{{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}"
|
|
|
|
containerHeader = "CONTAINER"
|
|
cpuPercHeader = "CPU %"
|
|
netIOHeader = "NET I/O"
|
|
blockIOHeader = "BLOCK I/O"
|
|
memPercHeader = "MEM %" // Used only on Linux
|
|
winMemUseHeader = "PRIV WORKING SET" // Used only on Windows
|
|
memUseHeader = "MEM USAGE / LIMIT" // Used only on Linux
|
|
pidsHeader = "PIDS" // Used only on Linux
|
|
)
|
|
|
|
// StatsEntry represents represents the statistics data collected from a container
|
|
type StatsEntry struct {
|
|
Container string
|
|
Name string
|
|
ID string
|
|
CPUPercentage float64
|
|
Memory float64 // On Windows this is the private working set
|
|
MemoryLimit float64 // Not used on Windows
|
|
MemoryPercentage float64 // Not used on Windows
|
|
NetworkRx float64
|
|
NetworkTx float64
|
|
BlockRead float64
|
|
BlockWrite float64
|
|
PidsCurrent uint64 // Not used on Windows
|
|
IsInvalid bool
|
|
}
|
|
|
|
// Stats represents an entity to store containers statistics synchronously
|
|
type Stats struct {
|
|
mutex sync.RWMutex
|
|
StatsEntry
|
|
err error
|
|
}
|
|
|
|
// GetError returns the container statistics error.
|
|
// This is used to determine whether the statistics are valid or not
|
|
func (cs *Stats) GetError() error {
|
|
cs.mutex.RLock()
|
|
defer cs.mutex.RUnlock()
|
|
return cs.err
|
|
}
|
|
|
|
// SetErrorAndReset zeroes all the container statistics and store the error.
|
|
// It is used when receiving time out error during statistics collecting to reduce lock overhead
|
|
func (cs *Stats) SetErrorAndReset(err error) {
|
|
cs.mutex.Lock()
|
|
defer cs.mutex.Unlock()
|
|
cs.CPUPercentage = 0
|
|
cs.Memory = 0
|
|
cs.MemoryPercentage = 0
|
|
cs.MemoryLimit = 0
|
|
cs.NetworkRx = 0
|
|
cs.NetworkTx = 0
|
|
cs.BlockRead = 0
|
|
cs.BlockWrite = 0
|
|
cs.PidsCurrent = 0
|
|
cs.err = err
|
|
cs.IsInvalid = true
|
|
}
|
|
|
|
// SetError sets container statistics error
|
|
func (cs *Stats) SetError(err error) {
|
|
cs.mutex.Lock()
|
|
defer cs.mutex.Unlock()
|
|
cs.err = err
|
|
if err != nil {
|
|
cs.IsInvalid = true
|
|
}
|
|
}
|
|
|
|
// SetStatistics set the container statistics
|
|
func (cs *Stats) SetStatistics(s StatsEntry) {
|
|
cs.mutex.Lock()
|
|
defer cs.mutex.Unlock()
|
|
s.Container = cs.Container
|
|
cs.StatsEntry = s
|
|
}
|
|
|
|
// GetStatistics returns container statistics with other meta data such as the container name
|
|
func (cs *Stats) GetStatistics() StatsEntry {
|
|
cs.mutex.RLock()
|
|
defer cs.mutex.RUnlock()
|
|
return cs.StatsEntry
|
|
}
|
|
|
|
// NewStatsFormat returns a format for rendering an CStatsContext
|
|
func NewStatsFormat(source, osType string) formatter.Format {
|
|
if source == formatter.TableFormatKey {
|
|
if osType == winOSType {
|
|
return winDefaultStatsTableFormat
|
|
}
|
|
return defaultStatsTableFormat
|
|
}
|
|
return formatter.Format(source)
|
|
}
|
|
|
|
// NewStats returns a new Stats entity and sets in it the given name
|
|
func NewStats(container string) *Stats {
|
|
return &Stats{StatsEntry: StatsEntry{Container: container}}
|
|
}
|
|
|
|
// statsFormatWrite renders the context for a list of containers statistics
|
|
func statsFormatWrite(ctx formatter.Context, stats []StatsEntry, osType string, trunc bool) error {
|
|
render := func(format func(subContext formatter.SubContext) error) error {
|
|
for _, cstats := range stats {
|
|
statsCtx := &statsContext{
|
|
s: cstats,
|
|
os: osType,
|
|
trunc: trunc,
|
|
}
|
|
if err := format(statsCtx); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
memUsage := memUseHeader
|
|
if osType == winOSType {
|
|
memUsage = winMemUseHeader
|
|
}
|
|
statsCtx := statsContext{}
|
|
statsCtx.Header = formatter.SubHeaderContext{
|
|
"Container": containerHeader,
|
|
"Name": formatter.NameHeader,
|
|
"ID": formatter.ContainerIDHeader,
|
|
"CPUPerc": cpuPercHeader,
|
|
"MemUsage": memUsage,
|
|
"MemPerc": memPercHeader,
|
|
"NetIO": netIOHeader,
|
|
"BlockIO": blockIOHeader,
|
|
"PIDs": pidsHeader,
|
|
}
|
|
statsCtx.os = osType
|
|
return ctx.Write(&statsCtx, render)
|
|
}
|
|
|
|
type statsContext struct {
|
|
formatter.HeaderContext
|
|
s StatsEntry
|
|
os string
|
|
trunc bool
|
|
}
|
|
|
|
func (c *statsContext) MarshalJSON() ([]byte, error) {
|
|
return formatter.MarshalJSON(c)
|
|
}
|
|
|
|
func (c *statsContext) Container() string {
|
|
return c.s.Container
|
|
}
|
|
|
|
func (c *statsContext) Name() string {
|
|
if len(c.s.Name) > 1 {
|
|
return c.s.Name[1:]
|
|
}
|
|
return "--"
|
|
}
|
|
|
|
func (c *statsContext) ID() string {
|
|
if c.trunc {
|
|
return stringid.TruncateID(c.s.ID)
|
|
}
|
|
return c.s.ID
|
|
}
|
|
|
|
func (c *statsContext) CPUPerc() string {
|
|
if c.s.IsInvalid {
|
|
return "--"
|
|
}
|
|
return formatPercentage(c.s.CPUPercentage)
|
|
}
|
|
|
|
func (c *statsContext) MemUsage() string {
|
|
if c.s.IsInvalid {
|
|
return "-- / --"
|
|
}
|
|
if c.os == winOSType {
|
|
return units.BytesSize(c.s.Memory)
|
|
}
|
|
return units.BytesSize(c.s.Memory) + " / " + units.BytesSize(c.s.MemoryLimit)
|
|
}
|
|
|
|
func (c *statsContext) MemPerc() string {
|
|
if c.s.IsInvalid || c.os == winOSType {
|
|
return "--"
|
|
}
|
|
return formatPercentage(c.s.MemoryPercentage)
|
|
}
|
|
|
|
func (c *statsContext) NetIO() string {
|
|
if c.s.IsInvalid {
|
|
return "--"
|
|
}
|
|
return units.HumanSizeWithPrecision(c.s.NetworkRx, 3) + " / " + units.HumanSizeWithPrecision(c.s.NetworkTx, 3)
|
|
}
|
|
|
|
func (c *statsContext) BlockIO() string {
|
|
if c.s.IsInvalid {
|
|
return "--"
|
|
}
|
|
return units.HumanSizeWithPrecision(c.s.BlockRead, 3) + " / " + units.HumanSizeWithPrecision(c.s.BlockWrite, 3)
|
|
}
|
|
|
|
func (c *statsContext) PIDs() string {
|
|
if c.s.IsInvalid || c.os == winOSType {
|
|
return "--"
|
|
}
|
|
return strconv.FormatUint(c.s.PidsCurrent, 10)
|
|
}
|
|
|
|
func formatPercentage(val float64) string {
|
|
return strconv.FormatFloat(val, 'f', 2, 64) + "%"
|
|
}
|