Initial commit
This commit is contained in:
51
vendor/github.com/hyperhq/hypercli/cli/command/formatter/custom.go
generated
vendored
Normal file
51
vendor/github.com/hyperhq/hypercli/cli/command/formatter/custom.go
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
imageHeader = "IMAGE"
|
||||
createdSinceHeader = "CREATED"
|
||||
createdAtHeader = "CREATED AT"
|
||||
sizeHeader = "SIZE"
|
||||
labelsHeader = "LABELS"
|
||||
nameHeader = "NAME"
|
||||
driverHeader = "DRIVER"
|
||||
scopeHeader = "SCOPE"
|
||||
)
|
||||
|
||||
type subContext interface {
|
||||
FullHeader() string
|
||||
AddHeader(header string)
|
||||
}
|
||||
|
||||
// HeaderContext provides the subContext interface for managing headers
|
||||
type HeaderContext struct {
|
||||
header []string
|
||||
}
|
||||
|
||||
// FullHeader returns the header as a string
|
||||
func (c *HeaderContext) FullHeader() string {
|
||||
if c.header == nil {
|
||||
return ""
|
||||
}
|
||||
return strings.Join(c.header, "\t")
|
||||
}
|
||||
|
||||
// AddHeader adds another column to the header
|
||||
func (c *HeaderContext) AddHeader(header string) {
|
||||
if c.header == nil {
|
||||
c.header = []string{}
|
||||
}
|
||||
c.header = append(c.header, strings.ToUpper(header))
|
||||
}
|
||||
|
||||
func stripNamePrefix(ss []string) []string {
|
||||
sss := make([]string, len(ss))
|
||||
for i, s := range ss {
|
||||
sss[i] = s[1:]
|
||||
}
|
||||
|
||||
return sss
|
||||
}
|
||||
123
vendor/github.com/hyperhq/hypercli/cli/command/formatter/formatter.go
generated
vendored
Normal file
123
vendor/github.com/hyperhq/hypercli/cli/command/formatter/formatter.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"text/tabwriter"
|
||||
"text/template"
|
||||
|
||||
"github.com/hyperhq/hypercli/utils/templates"
|
||||
)
|
||||
|
||||
// Format keys used to specify certain kinds of output formats
|
||||
const (
|
||||
TableFormatKey = "table"
|
||||
RawFormatKey = "raw"
|
||||
PrettyFormatKey = "pretty"
|
||||
|
||||
defaultQuietFormat = "{{.ID}}"
|
||||
)
|
||||
|
||||
// Format is the format string rendered using the Context
|
||||
type Format string
|
||||
|
||||
// IsTable returns true if the format is a table-type format
|
||||
func (f Format) IsTable() bool {
|
||||
return strings.HasPrefix(string(f), TableFormatKey)
|
||||
}
|
||||
|
||||
// Contains returns true if the format contains the substring
|
||||
func (f Format) Contains(sub string) bool {
|
||||
return strings.Contains(string(f), sub)
|
||||
}
|
||||
|
||||
// Context contains information required by the formatter to print the output as desired.
|
||||
type Context struct {
|
||||
// Output is the output stream to which the formatted string is written.
|
||||
Output io.Writer
|
||||
// Format is used to choose raw, table or custom format for the output.
|
||||
Format Format
|
||||
// Trunc when set to true will truncate the output of certain fields such as Container ID.
|
||||
Trunc bool
|
||||
|
||||
// internal element
|
||||
finalFormat string
|
||||
header string
|
||||
buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
func (c *Context) preFormat() {
|
||||
c.finalFormat = string(c.Format)
|
||||
|
||||
// TODO: handle this in the Format type
|
||||
if c.Format.IsTable() {
|
||||
c.finalFormat = c.finalFormat[len(TableFormatKey):]
|
||||
}
|
||||
|
||||
c.finalFormat = strings.Trim(c.finalFormat, " ")
|
||||
r := strings.NewReplacer(`\t`, "\t", `\n`, "\n")
|
||||
c.finalFormat = r.Replace(c.finalFormat)
|
||||
}
|
||||
|
||||
func (c *Context) parseFormat() (*template.Template, error) {
|
||||
tmpl, err := templates.Parse(c.finalFormat)
|
||||
if err != nil {
|
||||
return tmpl, fmt.Errorf("Template parsing error: %v\n", err)
|
||||
}
|
||||
return tmpl, err
|
||||
}
|
||||
|
||||
func (c *Context) postFormat(tmpl *template.Template, subContext subContext) {
|
||||
if c.Format.IsTable() {
|
||||
if len(c.header) == 0 {
|
||||
// if we still don't have a header, we didn't have any containers so we need to fake it to get the right headers from the template
|
||||
tmpl.Execute(bytes.NewBufferString(""), subContext)
|
||||
c.header = subContext.FullHeader()
|
||||
}
|
||||
|
||||
t := tabwriter.NewWriter(c.Output, 20, 1, 3, ' ', 0)
|
||||
t.Write([]byte(c.header))
|
||||
t.Write([]byte("\n"))
|
||||
c.buffer.WriteTo(t)
|
||||
t.Flush()
|
||||
} else {
|
||||
c.buffer.WriteTo(c.Output)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Context) contextFormat(tmpl *template.Template, subContext subContext) error {
|
||||
if err := tmpl.Execute(c.buffer, subContext); err != nil {
|
||||
return fmt.Errorf("Template parsing error: %v\n", err)
|
||||
}
|
||||
if c.Format.IsTable() && len(c.header) == 0 {
|
||||
c.header = subContext.FullHeader()
|
||||
}
|
||||
c.buffer.WriteString("\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
// SubFormat is a function type accepted by Write()
|
||||
type SubFormat func(func(subContext) error) error
|
||||
|
||||
// Write the template to the buffer using this Context
|
||||
func (c *Context) Write(sub subContext, f SubFormat) error {
|
||||
c.buffer = bytes.NewBufferString("")
|
||||
c.preFormat()
|
||||
|
||||
tmpl, err := c.parseFormat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
subFormat := func(subContext subContext) error {
|
||||
return c.contextFormat(tmpl, subContext)
|
||||
}
|
||||
if err := f(subFormat); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.postFormat(tmpl, sub)
|
||||
return nil
|
||||
}
|
||||
177
vendor/github.com/hyperhq/hypercli/cli/command/formatter/stats.go
generated
vendored
Normal file
177
vendor/github.com/hyperhq/hypercli/cli/command/formatter/stats.go
generated
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
package formatter
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
units "github.com/docker/go-units"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultStatsTableFormat = "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.BlockIO}}"
|
||||
|
||||
containerHeader = "CONTAINER"
|
||||
cpuPercHeader = "CPU %"
|
||||
netIOHeader = "NET I/O"
|
||||
blockIOHeader = "BLOCK I/O"
|
||||
memPercHeader = "MEM %"
|
||||
memUseHeader = "MEM USAGE / LIMIT"
|
||||
)
|
||||
|
||||
// StatsEntry represents represents the statistics data collected from a container
|
||||
type StatsEntry struct {
|
||||
Container string
|
||||
Name string
|
||||
ID string
|
||||
CPUPercentage float64
|
||||
Memory float64
|
||||
MemoryLimit float64
|
||||
MemoryPercentage float64
|
||||
NetworkRx float64
|
||||
NetworkTx float64
|
||||
BlockRead float64
|
||||
BlockWrite float64
|
||||
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.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 string) Format {
|
||||
if source == TableFormatKey {
|
||||
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) error {
|
||||
render := func(format func(subContext subContext) error) error {
|
||||
for _, cstats := range containerStats {
|
||||
containerStatsCtx := &containerStatsContext{
|
||||
s: cstats,
|
||||
}
|
||||
if err := format(containerStatsCtx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return ctx.Write(&containerStatsContext{}, render)
|
||||
}
|
||||
|
||||
type containerStatsContext struct {
|
||||
HeaderContext
|
||||
s StatsEntry
|
||||
}
|
||||
|
||||
func (c *containerStatsContext) Container() string {
|
||||
c.AddHeader(containerHeader)
|
||||
return c.s.Container
|
||||
}
|
||||
|
||||
func (c *containerStatsContext) CPUPerc() string {
|
||||
c.AddHeader(cpuPercHeader)
|
||||
if c.s.IsInvalid {
|
||||
return fmt.Sprintf("--")
|
||||
}
|
||||
return fmt.Sprintf("%.2f%%", c.s.CPUPercentage)
|
||||
}
|
||||
|
||||
func (c *containerStatsContext) MemUsage() string {
|
||||
header := memUseHeader
|
||||
|
||||
c.AddHeader(header)
|
||||
if c.s.IsInvalid {
|
||||
return fmt.Sprintf("-- / --")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s / %s", units.HumanSize(c.s.Memory), units.HumanSize(c.s.MemoryLimit))
|
||||
}
|
||||
|
||||
func (c *containerStatsContext) MemPerc() string {
|
||||
header := memPercHeader
|
||||
c.AddHeader(header)
|
||||
if c.s.IsInvalid {
|
||||
return fmt.Sprintf("--")
|
||||
}
|
||||
return fmt.Sprintf("%.2f%%", c.s.MemoryPercentage)
|
||||
}
|
||||
|
||||
func (c *containerStatsContext) NetIO() string {
|
||||
c.AddHeader(netIOHeader)
|
||||
if c.s.IsInvalid {
|
||||
return fmt.Sprintf("--")
|
||||
}
|
||||
return fmt.Sprintf("%s / %s", units.HumanSize(c.s.NetworkRx), units.HumanSize(c.s.NetworkTx))
|
||||
}
|
||||
|
||||
func (c *containerStatsContext) BlockIO() string {
|
||||
c.AddHeader(blockIOHeader)
|
||||
if c.s.IsInvalid {
|
||||
return fmt.Sprintf("--")
|
||||
}
|
||||
return fmt.Sprintf("%s / %s", units.HumanSize(c.s.BlockRead), units.HumanSize(c.s.BlockWrite))
|
||||
}
|
||||
Reference in New Issue
Block a user