102a8f0c9d
Also update gometalinter to use the official version. The update found some new gosimple errors, which are fixed. Also update the filewatcher script for the latest version of filewatcher. Signed-off-by: Daniel Nephin <dnephin@docker.com>
219 lines
5.8 KiB
Go
219 lines
5.8 KiB
Go
package formatter
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
units "github.com/docker/go-units"
|
|
)
|
|
|
|
const (
|
|
winOSType = "windows"
|
|
defaultStatsTableFormat = "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}\t{{.PIDs}}"
|
|
winDefaultStatsTableFormat = "table {{.Container}}\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
|
|
}
|
|
|
|
// ContainerStats represents an entity to store containers statistics synchronously
|
|
type ContainerStats struct {
|
|
mutex sync.Mutex
|
|
StatsEntry
|
|
err error
|
|
}
|
|
|
|
// GetError returns the container statistics error.
|
|
// This is used to determine whether the statistics are valid or not
|
|
func (cs *ContainerStats) GetError() error {
|
|
cs.mutex.Lock()
|
|
defer cs.mutex.Unlock()
|
|
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 *ContainerStats) 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 *ContainerStats) 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 *ContainerStats) 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 *ContainerStats) GetStatistics() StatsEntry {
|
|
cs.mutex.Lock()
|
|
defer cs.mutex.Unlock()
|
|
return cs.StatsEntry
|
|
}
|
|
|
|
// NewStatsFormat returns a format for rendering an CStatsContext
|
|
func NewStatsFormat(source, osType string) Format {
|
|
if source == TableFormatKey {
|
|
if osType == winOSType {
|
|
return Format(winDefaultStatsTableFormat)
|
|
}
|
|
return Format(defaultStatsTableFormat)
|
|
}
|
|
return Format(source)
|
|
}
|
|
|
|
// NewContainerStats returns a new ContainerStats entity and sets in it the given name
|
|
func NewContainerStats(container string) *ContainerStats {
|
|
return &ContainerStats{StatsEntry: StatsEntry{Container: container}}
|
|
}
|
|
|
|
// ContainerStatsWrite renders the context for a list of containers statistics
|
|
func ContainerStatsWrite(ctx Context, containerStats []StatsEntry, osType string) error {
|
|
render := func(format func(subContext subContext) error) error {
|
|
for _, cstats := range containerStats {
|
|
containerStatsCtx := &containerStatsContext{
|
|
s: cstats,
|
|
os: osType,
|
|
}
|
|
if err := format(containerStatsCtx); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
memUsage := memUseHeader
|
|
if osType == winOSType {
|
|
memUsage = winMemUseHeader
|
|
}
|
|
containerStatsCtx := containerStatsContext{}
|
|
containerStatsCtx.header = map[string]string{
|
|
"Container": containerHeader,
|
|
"Name": nameHeader,
|
|
"ID": containerIDHeader,
|
|
"CPUPerc": cpuPercHeader,
|
|
"MemUsage": memUsage,
|
|
"MemPerc": memPercHeader,
|
|
"NetIO": netIOHeader,
|
|
"BlockIO": blockIOHeader,
|
|
"PIDs": pidsHeader,
|
|
}
|
|
containerStatsCtx.os = osType
|
|
return ctx.Write(&containerStatsCtx, render)
|
|
}
|
|
|
|
type containerStatsContext struct {
|
|
HeaderContext
|
|
s StatsEntry
|
|
os string
|
|
}
|
|
|
|
func (c *containerStatsContext) MarshalJSON() ([]byte, error) {
|
|
return marshalJSON(c)
|
|
}
|
|
|
|
func (c *containerStatsContext) Container() string {
|
|
return c.s.Container
|
|
}
|
|
|
|
func (c *containerStatsContext) Name() string {
|
|
if len(c.s.Name) > 1 {
|
|
return c.s.Name[1:]
|
|
}
|
|
return "--"
|
|
}
|
|
|
|
func (c *containerStatsContext) ID() string {
|
|
return c.s.ID
|
|
}
|
|
|
|
func (c *containerStatsContext) CPUPerc() string {
|
|
if c.s.IsInvalid {
|
|
return fmt.Sprintf("--")
|
|
}
|
|
return fmt.Sprintf("%.2f%%", c.s.CPUPercentage)
|
|
}
|
|
|
|
func (c *containerStatsContext) MemUsage() string {
|
|
if c.s.IsInvalid {
|
|
return fmt.Sprintf("-- / --")
|
|
}
|
|
if c.os == winOSType {
|
|
return units.BytesSize(c.s.Memory)
|
|
}
|
|
return fmt.Sprintf("%s / %s", units.BytesSize(c.s.Memory), units.BytesSize(c.s.MemoryLimit))
|
|
}
|
|
|
|
func (c *containerStatsContext) MemPerc() string {
|
|
if c.s.IsInvalid || c.os == winOSType {
|
|
return fmt.Sprintf("--")
|
|
}
|
|
return fmt.Sprintf("%.2f%%", c.s.MemoryPercentage)
|
|
}
|
|
|
|
func (c *containerStatsContext) NetIO() string {
|
|
if c.s.IsInvalid {
|
|
return fmt.Sprintf("--")
|
|
}
|
|
return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.NetworkRx, 3), units.HumanSizeWithPrecision(c.s.NetworkTx, 3))
|
|
}
|
|
|
|
func (c *containerStatsContext) BlockIO() string {
|
|
if c.s.IsInvalid {
|
|
return fmt.Sprintf("--")
|
|
}
|
|
return fmt.Sprintf("%s / %s", units.HumanSizeWithPrecision(c.s.BlockRead, 3), units.HumanSizeWithPrecision(c.s.BlockWrite, 3))
|
|
}
|
|
|
|
func (c *containerStatsContext) PIDs() string {
|
|
if c.s.IsInvalid || c.os == winOSType {
|
|
return fmt.Sprintf("--")
|
|
}
|
|
return fmt.Sprintf("%d", c.s.PidsCurrent)
|
|
}
|