mirror of https://github.com/docker/cli.git
Merge pull request #30733 from yongtang/02022017-formatter-header
Allow `--format` to use different delim in `table` format
This commit is contained in:
commit
9fb77af2e6
|
@ -15,7 +15,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultContainerTableFormat = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.RunningFor}} ago\t{{.Status}}\t{{.Ports}}\t{{.Names}}"
|
defaultContainerTableFormat = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.RunningFor}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}"
|
||||||
|
|
||||||
containerIDHeader = "CONTAINER ID"
|
containerIDHeader = "CONTAINER ID"
|
||||||
namesHeader = "NAMES"
|
namesHeader = "NAMES"
|
||||||
|
@ -72,7 +72,17 @@ func ContainerWrite(ctx Context, containers []types.Container) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ctx.Write(&containerContext{}, render)
|
return ctx.Write(newContainerContext(), render)
|
||||||
|
}
|
||||||
|
|
||||||
|
type containerHeaderContext map[string]string
|
||||||
|
|
||||||
|
func (c containerHeaderContext) Label(name string) string {
|
||||||
|
n := strings.Split(name, ".")
|
||||||
|
r := strings.NewReplacer("-", " ", "_", " ")
|
||||||
|
h := r.Replace(n[len(n)-1])
|
||||||
|
|
||||||
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
type containerContext struct {
|
type containerContext struct {
|
||||||
|
@ -81,12 +91,31 @@ type containerContext struct {
|
||||||
c types.Container
|
c types.Container
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newContainerContext() *containerContext {
|
||||||
|
containerCtx := containerContext{}
|
||||||
|
containerCtx.header = containerHeaderContext{
|
||||||
|
"ID": containerIDHeader,
|
||||||
|
"Names": namesHeader,
|
||||||
|
"Image": imageHeader,
|
||||||
|
"Command": commandHeader,
|
||||||
|
"CreatedAt": createdAtHeader,
|
||||||
|
"RunningFor": runningForHeader,
|
||||||
|
"Ports": portsHeader,
|
||||||
|
"Status": statusHeader,
|
||||||
|
"Size": sizeHeader,
|
||||||
|
"Labels": labelsHeader,
|
||||||
|
"Mounts": mountsHeader,
|
||||||
|
"LocalVolumes": localVolumes,
|
||||||
|
"Networks": networksHeader,
|
||||||
|
}
|
||||||
|
return &containerCtx
|
||||||
|
}
|
||||||
|
|
||||||
func (c *containerContext) MarshalJSON() ([]byte, error) {
|
func (c *containerContext) MarshalJSON() ([]byte, error) {
|
||||||
return marshalJSON(c)
|
return marshalJSON(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerContext) ID() string {
|
func (c *containerContext) ID() string {
|
||||||
c.AddHeader(containerIDHeader)
|
|
||||||
if c.trunc {
|
if c.trunc {
|
||||||
return stringid.TruncateID(c.c.ID)
|
return stringid.TruncateID(c.c.ID)
|
||||||
}
|
}
|
||||||
|
@ -94,7 +123,6 @@ func (c *containerContext) ID() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerContext) Names() string {
|
func (c *containerContext) Names() string {
|
||||||
c.AddHeader(namesHeader)
|
|
||||||
names := stripNamePrefix(c.c.Names)
|
names := stripNamePrefix(c.c.Names)
|
||||||
if c.trunc {
|
if c.trunc {
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
|
@ -108,7 +136,6 @@ func (c *containerContext) Names() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerContext) Image() string {
|
func (c *containerContext) Image() string {
|
||||||
c.AddHeader(imageHeader)
|
|
||||||
if c.c.Image == "" {
|
if c.c.Image == "" {
|
||||||
return "<no image>"
|
return "<no image>"
|
||||||
}
|
}
|
||||||
|
@ -136,7 +163,6 @@ func (c *containerContext) Image() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerContext) Command() string {
|
func (c *containerContext) Command() string {
|
||||||
c.AddHeader(commandHeader)
|
|
||||||
command := c.c.Command
|
command := c.c.Command
|
||||||
if c.trunc {
|
if c.trunc {
|
||||||
command = stringutils.Ellipsis(command, 20)
|
command = stringutils.Ellipsis(command, 20)
|
||||||
|
@ -145,28 +171,23 @@ func (c *containerContext) Command() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerContext) CreatedAt() string {
|
func (c *containerContext) CreatedAt() string {
|
||||||
c.AddHeader(createdAtHeader)
|
|
||||||
return time.Unix(int64(c.c.Created), 0).String()
|
return time.Unix(int64(c.c.Created), 0).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerContext) RunningFor() string {
|
func (c *containerContext) RunningFor() string {
|
||||||
c.AddHeader(runningForHeader)
|
|
||||||
createdAt := time.Unix(int64(c.c.Created), 0)
|
createdAt := time.Unix(int64(c.c.Created), 0)
|
||||||
return units.HumanDuration(time.Now().UTC().Sub(createdAt))
|
return units.HumanDuration(time.Now().UTC().Sub(createdAt)) + " ago"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerContext) Ports() string {
|
func (c *containerContext) Ports() string {
|
||||||
c.AddHeader(portsHeader)
|
|
||||||
return api.DisplayablePorts(c.c.Ports)
|
return api.DisplayablePorts(c.c.Ports)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerContext) Status() string {
|
func (c *containerContext) Status() string {
|
||||||
c.AddHeader(statusHeader)
|
|
||||||
return c.c.Status
|
return c.c.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerContext) Size() string {
|
func (c *containerContext) Size() string {
|
||||||
c.AddHeader(sizeHeader)
|
|
||||||
srw := units.HumanSizeWithPrecision(float64(c.c.SizeRw), 3)
|
srw := units.HumanSizeWithPrecision(float64(c.c.SizeRw), 3)
|
||||||
sv := units.HumanSizeWithPrecision(float64(c.c.SizeRootFs), 3)
|
sv := units.HumanSizeWithPrecision(float64(c.c.SizeRootFs), 3)
|
||||||
|
|
||||||
|
@ -178,7 +199,6 @@ func (c *containerContext) Size() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerContext) Labels() string {
|
func (c *containerContext) Labels() string {
|
||||||
c.AddHeader(labelsHeader)
|
|
||||||
if c.c.Labels == nil {
|
if c.c.Labels == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -191,12 +211,6 @@ func (c *containerContext) Labels() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerContext) Label(name string) string {
|
func (c *containerContext) Label(name string) string {
|
||||||
n := strings.Split(name, ".")
|
|
||||||
r := strings.NewReplacer("-", " ", "_", " ")
|
|
||||||
h := r.Replace(n[len(n)-1])
|
|
||||||
|
|
||||||
c.AddHeader(h)
|
|
||||||
|
|
||||||
if c.c.Labels == nil {
|
if c.c.Labels == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -204,8 +218,6 @@ func (c *containerContext) Label(name string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerContext) Mounts() string {
|
func (c *containerContext) Mounts() string {
|
||||||
c.AddHeader(mountsHeader)
|
|
||||||
|
|
||||||
var name string
|
var name string
|
||||||
var mounts []string
|
var mounts []string
|
||||||
for _, m := range c.c.Mounts {
|
for _, m := range c.c.Mounts {
|
||||||
|
@ -223,8 +235,6 @@ func (c *containerContext) Mounts() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerContext) LocalVolumes() string {
|
func (c *containerContext) LocalVolumes() string {
|
||||||
c.AddHeader(localVolumes)
|
|
||||||
|
|
||||||
count := 0
|
count := 0
|
||||||
for _, m := range c.c.Mounts {
|
for _, m := range c.c.Mounts {
|
||||||
if m.Driver == "local" {
|
if m.Driver == "local" {
|
||||||
|
@ -236,8 +246,6 @@ func (c *containerContext) LocalVolumes() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerContext) Networks() string {
|
func (c *containerContext) Networks() string {
|
||||||
c.AddHeader(networksHeader)
|
|
||||||
|
|
||||||
if c.c.NetworkSettings == nil {
|
if c.c.NetworkSettings == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,22 +22,20 @@ func TestContainerPsContext(t *testing.T) {
|
||||||
container types.Container
|
container types.Container
|
||||||
trunc bool
|
trunc bool
|
||||||
expValue string
|
expValue string
|
||||||
expHeader string
|
|
||||||
call func() string
|
call func() string
|
||||||
}{
|
}{
|
||||||
{types.Container{ID: containerID}, true, stringid.TruncateID(containerID), containerIDHeader, ctx.ID},
|
{types.Container{ID: containerID}, true, stringid.TruncateID(containerID), ctx.ID},
|
||||||
{types.Container{ID: containerID}, false, containerID, containerIDHeader, ctx.ID},
|
{types.Container{ID: containerID}, false, containerID, ctx.ID},
|
||||||
{types.Container{Names: []string{"/foobar_baz"}}, true, "foobar_baz", namesHeader, ctx.Names},
|
{types.Container{Names: []string{"/foobar_baz"}}, true, "foobar_baz", ctx.Names},
|
||||||
{types.Container{Image: "ubuntu"}, true, "ubuntu", imageHeader, ctx.Image},
|
{types.Container{Image: "ubuntu"}, true, "ubuntu", ctx.Image},
|
||||||
{types.Container{Image: "verylongimagename"}, true, "verylongimagename", imageHeader, ctx.Image},
|
{types.Container{Image: "verylongimagename"}, true, "verylongimagename", ctx.Image},
|
||||||
{types.Container{Image: "verylongimagename"}, false, "verylongimagename", imageHeader, ctx.Image},
|
{types.Container{Image: "verylongimagename"}, false, "verylongimagename", ctx.Image},
|
||||||
{types.Container{
|
{types.Container{
|
||||||
Image: "a5a665ff33eced1e0803148700880edab4",
|
Image: "a5a665ff33eced1e0803148700880edab4",
|
||||||
ImageID: "a5a665ff33eced1e0803148700880edab4269067ed77e27737a708d0d293fbf5",
|
ImageID: "a5a665ff33eced1e0803148700880edab4269067ed77e27737a708d0d293fbf5",
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
"a5a665ff33ec",
|
"a5a665ff33ec",
|
||||||
imageHeader,
|
|
||||||
ctx.Image,
|
ctx.Image,
|
||||||
},
|
},
|
||||||
{types.Container{
|
{types.Container{
|
||||||
|
@ -46,19 +44,18 @@ func TestContainerPsContext(t *testing.T) {
|
||||||
},
|
},
|
||||||
false,
|
false,
|
||||||
"a5a665ff33eced1e0803148700880edab4",
|
"a5a665ff33eced1e0803148700880edab4",
|
||||||
imageHeader,
|
|
||||||
ctx.Image,
|
ctx.Image,
|
||||||
},
|
},
|
||||||
{types.Container{Image: ""}, true, "<no image>", imageHeader, ctx.Image},
|
{types.Container{Image: ""}, true, "<no image>", ctx.Image},
|
||||||
{types.Container{Command: "sh -c 'ls -la'"}, true, `"sh -c 'ls -la'"`, commandHeader, ctx.Command},
|
{types.Container{Command: "sh -c 'ls -la'"}, true, `"sh -c 'ls -la'"`, ctx.Command},
|
||||||
{types.Container{Created: unix}, true, time.Unix(unix, 0).String(), createdAtHeader, ctx.CreatedAt},
|
{types.Container{Created: unix}, true, time.Unix(unix, 0).String(), ctx.CreatedAt},
|
||||||
{types.Container{Ports: []types.Port{{PrivatePort: 8080, PublicPort: 8080, Type: "tcp"}}}, true, "8080/tcp", portsHeader, ctx.Ports},
|
{types.Container{Ports: []types.Port{{PrivatePort: 8080, PublicPort: 8080, Type: "tcp"}}}, true, "8080/tcp", ctx.Ports},
|
||||||
{types.Container{Status: "RUNNING"}, true, "RUNNING", statusHeader, ctx.Status},
|
{types.Container{Status: "RUNNING"}, true, "RUNNING", ctx.Status},
|
||||||
{types.Container{SizeRw: 10}, true, "10B", sizeHeader, ctx.Size},
|
{types.Container{SizeRw: 10}, true, "10B", ctx.Size},
|
||||||
{types.Container{SizeRw: 10, SizeRootFs: 20}, true, "10B (virtual 20B)", sizeHeader, ctx.Size},
|
{types.Container{SizeRw: 10, SizeRootFs: 20}, true, "10B (virtual 20B)", ctx.Size},
|
||||||
{types.Container{}, true, "", labelsHeader, ctx.Labels},
|
{types.Container{}, true, "", ctx.Labels},
|
||||||
{types.Container{Labels: map[string]string{"cpu": "6", "storage": "ssd"}}, true, "cpu=6,storage=ssd", labelsHeader, ctx.Labels},
|
{types.Container{Labels: map[string]string{"cpu": "6", "storage": "ssd"}}, true, "cpu=6,storage=ssd", ctx.Labels},
|
||||||
{types.Container{Created: unix}, true, "About a minute", runningForHeader, ctx.RunningFor},
|
{types.Container{Created: unix}, true, "About a minute ago", ctx.RunningFor},
|
||||||
{types.Container{
|
{types.Container{
|
||||||
Mounts: []types.MountPoint{
|
Mounts: []types.MountPoint{
|
||||||
{
|
{
|
||||||
|
@ -67,7 +64,7 @@ func TestContainerPsContext(t *testing.T) {
|
||||||
Source: "/a/path",
|
Source: "/a/path",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, true, "this-is-a-lo...", mountsHeader, ctx.Mounts},
|
}, true, "this-is-a-lo...", ctx.Mounts},
|
||||||
{types.Container{
|
{types.Container{
|
||||||
Mounts: []types.MountPoint{
|
Mounts: []types.MountPoint{
|
||||||
{
|
{
|
||||||
|
@ -75,7 +72,7 @@ func TestContainerPsContext(t *testing.T) {
|
||||||
Source: "/a/path",
|
Source: "/a/path",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, false, "/a/path", mountsHeader, ctx.Mounts},
|
}, false, "/a/path", ctx.Mounts},
|
||||||
{types.Container{
|
{types.Container{
|
||||||
Mounts: []types.MountPoint{
|
Mounts: []types.MountPoint{
|
||||||
{
|
{
|
||||||
|
@ -84,7 +81,7 @@ func TestContainerPsContext(t *testing.T) {
|
||||||
Source: "/a/path",
|
Source: "/a/path",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}, false, "733908409c91817de8e92b0096373245f329f19a88e2c849f02460e9b3d1c203", mountsHeader, ctx.Mounts},
|
}, false, "733908409c91817de8e92b0096373245f329f19a88e2c849f02460e9b3d1c203", ctx.Mounts},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
@ -95,11 +92,6 @@ func TestContainerPsContext(t *testing.T) {
|
||||||
} else if v != c.expValue {
|
} else if v != c.expValue {
|
||||||
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
h := ctx.FullHeader()
|
|
||||||
if h != c.expHeader {
|
|
||||||
t.Fatalf("Expected %s, was %s\n", c.expHeader, h)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c1 := types.Container{Labels: map[string]string{"com.docker.swarm.swarm-id": "33", "com.docker.swarm.node_name": "ubuntu"}}
|
c1 := types.Container{Labels: map[string]string{"com.docker.swarm.swarm-id": "33", "com.docker.swarm.node_name": "ubuntu"}}
|
||||||
|
@ -115,12 +107,6 @@ func TestContainerPsContext(t *testing.T) {
|
||||||
t.Fatalf("Expected ubuntu, was %s\n", node)
|
t.Fatalf("Expected ubuntu, was %s\n", node)
|
||||||
}
|
}
|
||||||
|
|
||||||
h := ctx.FullHeader()
|
|
||||||
if h != "SWARM ID\tNODE NAME" {
|
|
||||||
t.Fatalf("Expected %s, was %s\n", "SWARM ID\tNODE NAME", h)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
c2 := types.Container{}
|
c2 := types.Container{}
|
||||||
ctx = containerContext{c: c2, trunc: true}
|
ctx = containerContext{c: c2, trunc: true}
|
||||||
|
|
||||||
|
@ -128,13 +114,6 @@ func TestContainerPsContext(t *testing.T) {
|
||||||
if label != "" {
|
if label != "" {
|
||||||
t.Fatalf("Expected an empty string, was %s", label)
|
t.Fatalf("Expected an empty string, was %s", label)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx = containerContext{c: c2, trunc: true}
|
|
||||||
FullHeader := ctx.FullHeader()
|
|
||||||
if FullHeader != "" {
|
|
||||||
t.Fatalf("Expected FullHeader to be empty, was %s", FullHeader)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestContainerContextWrite(t *testing.T) {
|
func TestContainerContextWrite(t *testing.T) {
|
||||||
|
@ -247,6 +226,14 @@ size: 0B
|
||||||
Context{Format: NewContainerFormat("{{.Image}}", false, true)},
|
Context{Format: NewContainerFormat("{{.Image}}", false, true)},
|
||||||
"ubuntu\nubuntu\n",
|
"ubuntu\nubuntu\n",
|
||||||
},
|
},
|
||||||
|
// Special headers for customerized table format
|
||||||
|
{
|
||||||
|
Context{Format: NewContainerFormat(`table {{truncate .ID 5}}\t{{json .Image}} {{.RunningFor}}/{{title .Status}}/{{pad .Ports 2 2}}.{{upper .Names}} {{lower .Status}}`, false, true)},
|
||||||
|
`CONTAINER ID IMAGE CREATED/STATUS/ PORTS .NAMES STATUS
|
||||||
|
conta "ubuntu" 24 hours ago//.FOOBAR_BAZ
|
||||||
|
conta "ubuntu" 24 hours ago//.FOOBAR_BAR
|
||||||
|
`,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testcase := range cases {
|
for _, testcase := range cases {
|
||||||
|
@ -333,8 +320,8 @@ func TestContainerContextWriteJSON(t *testing.T) {
|
||||||
}
|
}
|
||||||
expectedCreated := time.Unix(unix, 0).String()
|
expectedCreated := time.Unix(unix, 0).String()
|
||||||
expectedJSONs := []map[string]interface{}{
|
expectedJSONs := []map[string]interface{}{
|
||||||
{"Command": "\"\"", "CreatedAt": expectedCreated, "ID": "containerID1", "Image": "ubuntu", "Labels": "", "LocalVolumes": "0", "Mounts": "", "Names": "foobar_baz", "Networks": "", "Ports": "", "RunningFor": "About a minute", "Size": "0B", "Status": ""},
|
{"Command": "\"\"", "CreatedAt": expectedCreated, "ID": "containerID1", "Image": "ubuntu", "Labels": "", "LocalVolumes": "0", "Mounts": "", "Names": "foobar_baz", "Networks": "", "Ports": "", "RunningFor": "About a minute ago", "Size": "0B", "Status": ""},
|
||||||
{"Command": "\"\"", "CreatedAt": expectedCreated, "ID": "containerID2", "Image": "ubuntu", "Labels": "", "LocalVolumes": "0", "Mounts": "", "Names": "foobar_bar", "Networks": "", "Ports": "", "RunningFor": "About a minute", "Size": "0B", "Status": ""},
|
{"Command": "\"\"", "CreatedAt": expectedCreated, "ID": "containerID2", "Image": "ubuntu", "Labels": "", "LocalVolumes": "0", "Mounts": "", "Names": "foobar_bar", "Networks": "", "Ports": "", "RunningFor": "About a minute ago", "Size": "0B", "Status": ""},
|
||||||
}
|
}
|
||||||
out := bytes.NewBufferString("")
|
out := bytes.NewBufferString("")
|
||||||
err := ContainerWrite(Context{Format: "{{json .}}", Output: out}, containers)
|
err := ContainerWrite(Context{Format: "{{json .}}", Output: out}, containers)
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
package formatter
|
package formatter
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
imageHeader = "IMAGE"
|
imageHeader = "IMAGE"
|
||||||
createdSinceHeader = "CREATED"
|
createdSinceHeader = "CREATED"
|
||||||
|
@ -16,29 +12,17 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
type subContext interface {
|
type subContext interface {
|
||||||
FullHeader() string
|
FullHeader() interface{}
|
||||||
AddHeader(header string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// HeaderContext provides the subContext interface for managing headers
|
// HeaderContext provides the subContext interface for managing headers
|
||||||
type HeaderContext struct {
|
type HeaderContext struct {
|
||||||
header []string
|
header interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FullHeader returns the header as a string
|
// FullHeader returns the header as an interface
|
||||||
func (c *HeaderContext) FullHeader() string {
|
func (c *HeaderContext) FullHeader() interface{} {
|
||||||
if c.header == nil {
|
return c.header
|
||||||
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 {
|
func stripNamePrefix(ss []string) []string {
|
||||||
|
|
|
@ -77,7 +77,15 @@ func (ctx *DiskUsageContext) Write() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.postFormat(tmpl, &diskUsageContainersContext{containers: []*types.Container{}})
|
diskUsageContainersCtx := diskUsageContainersContext{containers: []*types.Container{}}
|
||||||
|
diskUsageContainersCtx.header = map[string]string{
|
||||||
|
"Type": typeHeader,
|
||||||
|
"TotalCount": totalHeader,
|
||||||
|
"Active": activeHeader,
|
||||||
|
"Size": sizeHeader,
|
||||||
|
"Reclaimable": reclaimableHeader,
|
||||||
|
}
|
||||||
|
ctx.postFormat(tmpl, &diskUsageContainersCtx)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -114,7 +122,7 @@ func (ctx *DiskUsageContext) Write() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.postFormat(tmpl, &imageContext{})
|
ctx.postFormat(tmpl, newImageContext())
|
||||||
|
|
||||||
// Now containers
|
// Now containers
|
||||||
ctx.Output.Write([]byte("\nContainers space usage:\n\n"))
|
ctx.Output.Write([]byte("\nContainers space usage:\n\n"))
|
||||||
|
@ -133,7 +141,7 @@ func (ctx *DiskUsageContext) Write() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.postFormat(tmpl, &containerContext{})
|
ctx.postFormat(tmpl, newContainerContext())
|
||||||
|
|
||||||
// And volumes
|
// And volumes
|
||||||
ctx.Output.Write([]byte("\nLocal Volumes space usage:\n\n"))
|
ctx.Output.Write([]byte("\nLocal Volumes space usage:\n\n"))
|
||||||
|
@ -149,7 +157,7 @@ func (ctx *DiskUsageContext) Write() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ctx.postFormat(tmpl, &volumeContext{v: types.Volume{}})
|
ctx.postFormat(tmpl, newVolumeContext())
|
||||||
}
|
}
|
||||||
|
|
||||||
type diskUsageImagesContext struct {
|
type diskUsageImagesContext struct {
|
||||||
|
@ -163,17 +171,14 @@ func (c *diskUsageImagesContext) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *diskUsageImagesContext) Type() string {
|
func (c *diskUsageImagesContext) Type() string {
|
||||||
c.AddHeader(typeHeader)
|
|
||||||
return "Images"
|
return "Images"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *diskUsageImagesContext) TotalCount() string {
|
func (c *diskUsageImagesContext) TotalCount() string {
|
||||||
c.AddHeader(totalHeader)
|
|
||||||
return fmt.Sprintf("%d", len(c.images))
|
return fmt.Sprintf("%d", len(c.images))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *diskUsageImagesContext) Active() string {
|
func (c *diskUsageImagesContext) Active() string {
|
||||||
c.AddHeader(activeHeader)
|
|
||||||
used := 0
|
used := 0
|
||||||
for _, i := range c.images {
|
for _, i := range c.images {
|
||||||
if i.Containers > 0 {
|
if i.Containers > 0 {
|
||||||
|
@ -185,7 +190,6 @@ func (c *diskUsageImagesContext) Active() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *diskUsageImagesContext) Size() string {
|
func (c *diskUsageImagesContext) Size() string {
|
||||||
c.AddHeader(sizeHeader)
|
|
||||||
return units.HumanSize(float64(c.totalSize))
|
return units.HumanSize(float64(c.totalSize))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -193,7 +197,6 @@ func (c *diskUsageImagesContext) Size() string {
|
||||||
func (c *diskUsageImagesContext) Reclaimable() string {
|
func (c *diskUsageImagesContext) Reclaimable() string {
|
||||||
var used int64
|
var used int64
|
||||||
|
|
||||||
c.AddHeader(reclaimableHeader)
|
|
||||||
for _, i := range c.images {
|
for _, i := range c.images {
|
||||||
if i.Containers != 0 {
|
if i.Containers != 0 {
|
||||||
if i.VirtualSize == -1 || i.SharedSize == -1 {
|
if i.VirtualSize == -1 || i.SharedSize == -1 {
|
||||||
|
@ -221,12 +224,10 @@ func (c *diskUsageContainersContext) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *diskUsageContainersContext) Type() string {
|
func (c *diskUsageContainersContext) Type() string {
|
||||||
c.AddHeader(typeHeader)
|
|
||||||
return "Containers"
|
return "Containers"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *diskUsageContainersContext) TotalCount() string {
|
func (c *diskUsageContainersContext) TotalCount() string {
|
||||||
c.AddHeader(totalHeader)
|
|
||||||
return fmt.Sprintf("%d", len(c.containers))
|
return fmt.Sprintf("%d", len(c.containers))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,7 +238,6 @@ func (c *diskUsageContainersContext) isActive(container types.Container) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *diskUsageContainersContext) Active() string {
|
func (c *diskUsageContainersContext) Active() string {
|
||||||
c.AddHeader(activeHeader)
|
|
||||||
used := 0
|
used := 0
|
||||||
for _, container := range c.containers {
|
for _, container := range c.containers {
|
||||||
if c.isActive(*container) {
|
if c.isActive(*container) {
|
||||||
|
@ -251,7 +251,6 @@ func (c *diskUsageContainersContext) Active() string {
|
||||||
func (c *diskUsageContainersContext) Size() string {
|
func (c *diskUsageContainersContext) Size() string {
|
||||||
var size int64
|
var size int64
|
||||||
|
|
||||||
c.AddHeader(sizeHeader)
|
|
||||||
for _, container := range c.containers {
|
for _, container := range c.containers {
|
||||||
size += container.SizeRw
|
size += container.SizeRw
|
||||||
}
|
}
|
||||||
|
@ -263,7 +262,6 @@ func (c *diskUsageContainersContext) Reclaimable() string {
|
||||||
var reclaimable int64
|
var reclaimable int64
|
||||||
var totalSize int64
|
var totalSize int64
|
||||||
|
|
||||||
c.AddHeader(reclaimableHeader)
|
|
||||||
for _, container := range c.containers {
|
for _, container := range c.containers {
|
||||||
if !c.isActive(*container) {
|
if !c.isActive(*container) {
|
||||||
reclaimable += container.SizeRw
|
reclaimable += container.SizeRw
|
||||||
|
@ -289,17 +287,14 @@ func (c *diskUsageVolumesContext) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *diskUsageVolumesContext) Type() string {
|
func (c *diskUsageVolumesContext) Type() string {
|
||||||
c.AddHeader(typeHeader)
|
|
||||||
return "Local Volumes"
|
return "Local Volumes"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *diskUsageVolumesContext) TotalCount() string {
|
func (c *diskUsageVolumesContext) TotalCount() string {
|
||||||
c.AddHeader(totalHeader)
|
|
||||||
return fmt.Sprintf("%d", len(c.volumes))
|
return fmt.Sprintf("%d", len(c.volumes))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *diskUsageVolumesContext) Active() string {
|
func (c *diskUsageVolumesContext) Active() string {
|
||||||
c.AddHeader(activeHeader)
|
|
||||||
|
|
||||||
used := 0
|
used := 0
|
||||||
for _, v := range c.volumes {
|
for _, v := range c.volumes {
|
||||||
|
@ -314,7 +309,6 @@ func (c *diskUsageVolumesContext) Active() string {
|
||||||
func (c *diskUsageVolumesContext) Size() string {
|
func (c *diskUsageVolumesContext) Size() string {
|
||||||
var size int64
|
var size int64
|
||||||
|
|
||||||
c.AddHeader(sizeHeader)
|
|
||||||
for _, v := range c.volumes {
|
for _, v := range c.volumes {
|
||||||
if v.UsageData.Size != -1 {
|
if v.UsageData.Size != -1 {
|
||||||
size += v.UsageData.Size
|
size += v.UsageData.Size
|
||||||
|
@ -328,7 +322,6 @@ func (c *diskUsageVolumesContext) Reclaimable() string {
|
||||||
var reclaimable int64
|
var reclaimable int64
|
||||||
var totalSize int64
|
var totalSize int64
|
||||||
|
|
||||||
c.AddHeader(reclaimableHeader)
|
|
||||||
for _, v := range c.volumes {
|
for _, v := range c.volumes {
|
||||||
if v.UsageData.Size != -1 {
|
if v.UsageData.Size != -1 {
|
||||||
if v.UsageData.RefCount == 0 {
|
if v.UsageData.RefCount == 0 {
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
package formatter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/docker/docker/pkg/testutil/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDiskUsageContextFormatWrite(t *testing.T) {
|
||||||
|
// Check default output format (verbose and non-verbose mode) for table headers
|
||||||
|
cases := []struct {
|
||||||
|
context DiskUsageContext
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
DiskUsageContext{Verbose: false},
|
||||||
|
`TYPE TOTAL ACTIVE SIZE RECLAIMABLE
|
||||||
|
Images 0 0 0B 0B
|
||||||
|
Containers 0 0 0B 0B
|
||||||
|
Local Volumes 0 0 0B 0B
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
DiskUsageContext{Verbose: true},
|
||||||
|
`Images space usage:
|
||||||
|
|
||||||
|
REPOSITORY TAG IMAGE ID CREATED ago SIZE SHARED SIZE UNIQUE SiZE CONTAINERS
|
||||||
|
|
||||||
|
Containers space usage:
|
||||||
|
|
||||||
|
CONTAINER ID IMAGE COMMAND LOCAL VOLUMES SIZE CREATED ago STATUS NAMES
|
||||||
|
|
||||||
|
Local Volumes space usage:
|
||||||
|
|
||||||
|
VOLUME NAME LINKS SIZE
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testcase := range cases {
|
||||||
|
out := bytes.NewBufferString("")
|
||||||
|
testcase.context.Output = out
|
||||||
|
testcase.context.Write()
|
||||||
|
assert.Equal(t, out.String(), testcase.expected)
|
||||||
|
}
|
||||||
|
}
|
|
@ -44,7 +44,7 @@ type Context struct {
|
||||||
|
|
||||||
// internal element
|
// internal element
|
||||||
finalFormat string
|
finalFormat string
|
||||||
header string
|
header interface{}
|
||||||
buffer *bytes.Buffer
|
buffer *bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,14 +71,10 @@ func (c *Context) parseFormat() (*template.Template, error) {
|
||||||
|
|
||||||
func (c *Context) postFormat(tmpl *template.Template, subContext subContext) {
|
func (c *Context) postFormat(tmpl *template.Template, subContext subContext) {
|
||||||
if c.Format.IsTable() {
|
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 := tabwriter.NewWriter(c.Output, 20, 1, 3, ' ', 0)
|
||||||
t.Write([]byte(c.header))
|
buffer := bytes.NewBufferString("")
|
||||||
|
tmpl.Funcs(templates.HeaderFunctions).Execute(buffer, subContext.FullHeader())
|
||||||
|
buffer.WriteTo(t)
|
||||||
t.Write([]byte("\n"))
|
t.Write([]byte("\n"))
|
||||||
c.buffer.WriteTo(t)
|
c.buffer.WriteTo(t)
|
||||||
t.Flush()
|
t.Flush()
|
||||||
|
@ -91,7 +87,7 @@ func (c *Context) contextFormat(tmpl *template.Template, subContext subContext)
|
||||||
if err := tmpl.Execute(c.buffer, subContext); err != nil {
|
if err := tmpl.Execute(c.buffer, subContext); err != nil {
|
||||||
return fmt.Errorf("Template parsing error: %v\n", err)
|
return fmt.Errorf("Template parsing error: %v\n", err)
|
||||||
}
|
}
|
||||||
if c.Format.IsTable() && len(c.header) == 0 {
|
if c.Format.IsTable() && c.header != nil {
|
||||||
c.header = subContext.FullHeader()
|
c.header = subContext.FullHeader()
|
||||||
}
|
}
|
||||||
c.buffer.WriteString("\n")
|
c.buffer.WriteString("\n")
|
||||||
|
|
|
@ -11,8 +11,8 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
defaultImageTableFormat = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.Size}}"
|
defaultImageTableFormat = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}}\t{{.Size}}"
|
||||||
defaultImageTableFormatWithDigest = "table {{.Repository}}\t{{.Tag}}\t{{.Digest}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.Size}}"
|
defaultImageTableFormatWithDigest = "table {{.Repository}}\t{{.Tag}}\t{{.Digest}}\t{{.ID}}\t{{.CreatedSince}}\t{{.Size}}"
|
||||||
|
|
||||||
imageIDHeader = "IMAGE ID"
|
imageIDHeader = "IMAGE ID"
|
||||||
repositoryHeader = "REPOSITORY"
|
repositoryHeader = "REPOSITORY"
|
||||||
|
@ -76,7 +76,7 @@ func ImageWrite(ctx ImageContext, images []types.ImageSummary) error {
|
||||||
render := func(format func(subContext subContext) error) error {
|
render := func(format func(subContext subContext) error) error {
|
||||||
return imageFormat(ctx, images, format)
|
return imageFormat(ctx, images, format)
|
||||||
}
|
}
|
||||||
return ctx.Write(&imageContext{}, render)
|
return ctx.Write(newImageContext(), render)
|
||||||
}
|
}
|
||||||
|
|
||||||
func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subContext subContext) error) error {
|
func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subContext subContext) error) error {
|
||||||
|
@ -192,12 +192,29 @@ type imageContext struct {
|
||||||
digest string
|
digest string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newImageContext() *imageContext {
|
||||||
|
imageCtx := imageContext{}
|
||||||
|
imageCtx.header = map[string]string{
|
||||||
|
"ID": imageIDHeader,
|
||||||
|
"Repository": repositoryHeader,
|
||||||
|
"Tag": tagHeader,
|
||||||
|
"Digest": digestHeader,
|
||||||
|
"CreatedSince": createdSinceHeader,
|
||||||
|
"CreatedAt": createdAtHeader,
|
||||||
|
"Size": sizeHeader,
|
||||||
|
"Containers": containersHeader,
|
||||||
|
"VirtualSize": sizeHeader,
|
||||||
|
"SharedSize": sharedSizeHeader,
|
||||||
|
"UniqueSize": uniqueSizeHeader,
|
||||||
|
}
|
||||||
|
return &imageCtx
|
||||||
|
}
|
||||||
|
|
||||||
func (c *imageContext) MarshalJSON() ([]byte, error) {
|
func (c *imageContext) MarshalJSON() ([]byte, error) {
|
||||||
return marshalJSON(c)
|
return marshalJSON(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *imageContext) ID() string {
|
func (c *imageContext) ID() string {
|
||||||
c.AddHeader(imageIDHeader)
|
|
||||||
if c.trunc {
|
if c.trunc {
|
||||||
return stringid.TruncateID(c.i.ID)
|
return stringid.TruncateID(c.i.ID)
|
||||||
}
|
}
|
||||||
|
@ -205,38 +222,31 @@ func (c *imageContext) ID() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *imageContext) Repository() string {
|
func (c *imageContext) Repository() string {
|
||||||
c.AddHeader(repositoryHeader)
|
|
||||||
return c.repo
|
return c.repo
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *imageContext) Tag() string {
|
func (c *imageContext) Tag() string {
|
||||||
c.AddHeader(tagHeader)
|
|
||||||
return c.tag
|
return c.tag
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *imageContext) Digest() string {
|
func (c *imageContext) Digest() string {
|
||||||
c.AddHeader(digestHeader)
|
|
||||||
return c.digest
|
return c.digest
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *imageContext) CreatedSince() string {
|
func (c *imageContext) CreatedSince() string {
|
||||||
c.AddHeader(createdSinceHeader)
|
|
||||||
createdAt := time.Unix(int64(c.i.Created), 0)
|
createdAt := time.Unix(int64(c.i.Created), 0)
|
||||||
return units.HumanDuration(time.Now().UTC().Sub(createdAt))
|
return units.HumanDuration(time.Now().UTC().Sub(createdAt)) + " ago"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *imageContext) CreatedAt() string {
|
func (c *imageContext) CreatedAt() string {
|
||||||
c.AddHeader(createdAtHeader)
|
|
||||||
return time.Unix(int64(c.i.Created), 0).String()
|
return time.Unix(int64(c.i.Created), 0).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *imageContext) Size() string {
|
func (c *imageContext) Size() string {
|
||||||
c.AddHeader(sizeHeader)
|
|
||||||
return units.HumanSizeWithPrecision(float64(c.i.Size), 3)
|
return units.HumanSizeWithPrecision(float64(c.i.Size), 3)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *imageContext) Containers() string {
|
func (c *imageContext) Containers() string {
|
||||||
c.AddHeader(containersHeader)
|
|
||||||
if c.i.Containers == -1 {
|
if c.i.Containers == -1 {
|
||||||
return "N/A"
|
return "N/A"
|
||||||
}
|
}
|
||||||
|
@ -244,12 +254,10 @@ func (c *imageContext) Containers() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *imageContext) VirtualSize() string {
|
func (c *imageContext) VirtualSize() string {
|
||||||
c.AddHeader(sizeHeader)
|
|
||||||
return units.HumanSize(float64(c.i.VirtualSize))
|
return units.HumanSize(float64(c.i.VirtualSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *imageContext) SharedSize() string {
|
func (c *imageContext) SharedSize() string {
|
||||||
c.AddHeader(sharedSizeHeader)
|
|
||||||
if c.i.SharedSize == -1 {
|
if c.i.SharedSize == -1 {
|
||||||
return "N/A"
|
return "N/A"
|
||||||
}
|
}
|
||||||
|
@ -257,7 +265,6 @@ func (c *imageContext) SharedSize() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *imageContext) UniqueSize() string {
|
func (c *imageContext) UniqueSize() string {
|
||||||
c.AddHeader(uniqueSizeHeader)
|
|
||||||
if c.i.VirtualSize == -1 || c.i.SharedSize == -1 {
|
if c.i.VirtualSize == -1 || c.i.SharedSize == -1 {
|
||||||
return "N/A"
|
return "N/A"
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,27 +18,26 @@ func TestImageContext(t *testing.T) {
|
||||||
|
|
||||||
var ctx imageContext
|
var ctx imageContext
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
imageCtx imageContext
|
imageCtx imageContext
|
||||||
expValue string
|
expValue string
|
||||||
expHeader string
|
call func() string
|
||||||
call func() string
|
|
||||||
}{
|
}{
|
||||||
{imageContext{
|
{imageContext{
|
||||||
i: types.ImageSummary{ID: imageID},
|
i: types.ImageSummary{ID: imageID},
|
||||||
trunc: true,
|
trunc: true,
|
||||||
}, stringid.TruncateID(imageID), imageIDHeader, ctx.ID},
|
}, stringid.TruncateID(imageID), ctx.ID},
|
||||||
{imageContext{
|
{imageContext{
|
||||||
i: types.ImageSummary{ID: imageID},
|
i: types.ImageSummary{ID: imageID},
|
||||||
trunc: false,
|
trunc: false,
|
||||||
}, imageID, imageIDHeader, ctx.ID},
|
}, imageID, ctx.ID},
|
||||||
{imageContext{
|
{imageContext{
|
||||||
i: types.ImageSummary{Size: 10, VirtualSize: 10},
|
i: types.ImageSummary{Size: 10, VirtualSize: 10},
|
||||||
trunc: true,
|
trunc: true,
|
||||||
}, "10B", sizeHeader, ctx.Size},
|
}, "10B", ctx.Size},
|
||||||
{imageContext{
|
{imageContext{
|
||||||
i: types.ImageSummary{Created: unix},
|
i: types.ImageSummary{Created: unix},
|
||||||
trunc: true,
|
trunc: true,
|
||||||
}, time.Unix(unix, 0).String(), createdAtHeader, ctx.CreatedAt},
|
}, time.Unix(unix, 0).String(), ctx.CreatedAt},
|
||||||
// FIXME
|
// FIXME
|
||||||
// {imageContext{
|
// {imageContext{
|
||||||
// i: types.ImageSummary{Created: unix},
|
// i: types.ImageSummary{Created: unix},
|
||||||
|
@ -47,15 +46,15 @@ func TestImageContext(t *testing.T) {
|
||||||
{imageContext{
|
{imageContext{
|
||||||
i: types.ImageSummary{},
|
i: types.ImageSummary{},
|
||||||
repo: "busybox",
|
repo: "busybox",
|
||||||
}, "busybox", repositoryHeader, ctx.Repository},
|
}, "busybox", ctx.Repository},
|
||||||
{imageContext{
|
{imageContext{
|
||||||
i: types.ImageSummary{},
|
i: types.ImageSummary{},
|
||||||
tag: "latest",
|
tag: "latest",
|
||||||
}, "latest", tagHeader, ctx.Tag},
|
}, "latest", ctx.Tag},
|
||||||
{imageContext{
|
{imageContext{
|
||||||
i: types.ImageSummary{},
|
i: types.ImageSummary{},
|
||||||
digest: "sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a",
|
digest: "sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a",
|
||||||
}, "sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a", digestHeader, ctx.Digest},
|
}, "sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a", ctx.Digest},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
@ -66,11 +65,6 @@ func TestImageContext(t *testing.T) {
|
||||||
} else if v != c.expValue {
|
} else if v != c.expValue {
|
||||||
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
h := ctx.FullHeader()
|
|
||||||
if h != c.expHeader {
|
|
||||||
t.Fatalf("Expected %s, was %s\n", c.expHeader, h)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,28 @@ func NetworkWrite(ctx Context, networks []types.NetworkResource) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ctx.Write(&networkContext{}, render)
|
networkCtx := networkContext{}
|
||||||
|
networkCtx.header = networkHeaderContext{
|
||||||
|
"ID": networkIDHeader,
|
||||||
|
"Name": nameHeader,
|
||||||
|
"Driver": driverHeader,
|
||||||
|
"Scope": scopeHeader,
|
||||||
|
"IPv6": ipv6Header,
|
||||||
|
"Internal": internalHeader,
|
||||||
|
"Labels": labelsHeader,
|
||||||
|
"CreatedAt": createdAtHeader,
|
||||||
|
}
|
||||||
|
return ctx.Write(&networkCtx, render)
|
||||||
|
}
|
||||||
|
|
||||||
|
type networkHeaderContext map[string]string
|
||||||
|
|
||||||
|
func (c networkHeaderContext) Label(name string) string {
|
||||||
|
n := strings.Split(name, ".")
|
||||||
|
r := strings.NewReplacer("-", " ", "_", " ")
|
||||||
|
h := r.Replace(n[len(n)-1])
|
||||||
|
|
||||||
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
type networkContext struct {
|
type networkContext struct {
|
||||||
|
@ -58,7 +79,6 @@ func (c *networkContext) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *networkContext) ID() string {
|
func (c *networkContext) ID() string {
|
||||||
c.AddHeader(networkIDHeader)
|
|
||||||
if c.trunc {
|
if c.trunc {
|
||||||
return stringid.TruncateID(c.n.ID)
|
return stringid.TruncateID(c.n.ID)
|
||||||
}
|
}
|
||||||
|
@ -66,32 +86,26 @@ func (c *networkContext) ID() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *networkContext) Name() string {
|
func (c *networkContext) Name() string {
|
||||||
c.AddHeader(nameHeader)
|
|
||||||
return c.n.Name
|
return c.n.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *networkContext) Driver() string {
|
func (c *networkContext) Driver() string {
|
||||||
c.AddHeader(driverHeader)
|
|
||||||
return c.n.Driver
|
return c.n.Driver
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *networkContext) Scope() string {
|
func (c *networkContext) Scope() string {
|
||||||
c.AddHeader(scopeHeader)
|
|
||||||
return c.n.Scope
|
return c.n.Scope
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *networkContext) IPv6() string {
|
func (c *networkContext) IPv6() string {
|
||||||
c.AddHeader(ipv6Header)
|
|
||||||
return fmt.Sprintf("%v", c.n.EnableIPv6)
|
return fmt.Sprintf("%v", c.n.EnableIPv6)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *networkContext) Internal() string {
|
func (c *networkContext) Internal() string {
|
||||||
c.AddHeader(internalHeader)
|
|
||||||
return fmt.Sprintf("%v", c.n.Internal)
|
return fmt.Sprintf("%v", c.n.Internal)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *networkContext) Labels() string {
|
func (c *networkContext) Labels() string {
|
||||||
c.AddHeader(labelsHeader)
|
|
||||||
if c.n.Labels == nil {
|
if c.n.Labels == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -104,12 +118,6 @@ func (c *networkContext) Labels() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *networkContext) Label(name string) string {
|
func (c *networkContext) Label(name string) string {
|
||||||
n := strings.Split(name, ".")
|
|
||||||
r := strings.NewReplacer("-", " ", "_", " ")
|
|
||||||
h := r.Replace(n[len(n)-1])
|
|
||||||
|
|
||||||
c.AddHeader(h)
|
|
||||||
|
|
||||||
if c.n.Labels == nil {
|
if c.n.Labels == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -117,6 +125,5 @@ func (c *networkContext) Label(name string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *networkContext) CreatedAt() string {
|
func (c *networkContext) CreatedAt() string {
|
||||||
c.AddHeader(createdAtHeader)
|
|
||||||
return c.n.Created.String()
|
return c.n.Created.String()
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,41 +19,40 @@ func TestNetworkContext(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
networkCtx networkContext
|
networkCtx networkContext
|
||||||
expValue string
|
expValue string
|
||||||
expHeader string
|
|
||||||
call func() string
|
call func() string
|
||||||
}{
|
}{
|
||||||
{networkContext{
|
{networkContext{
|
||||||
n: types.NetworkResource{ID: networkID},
|
n: types.NetworkResource{ID: networkID},
|
||||||
trunc: false,
|
trunc: false,
|
||||||
}, networkID, networkIDHeader, ctx.ID},
|
}, networkID, ctx.ID},
|
||||||
{networkContext{
|
{networkContext{
|
||||||
n: types.NetworkResource{ID: networkID},
|
n: types.NetworkResource{ID: networkID},
|
||||||
trunc: true,
|
trunc: true,
|
||||||
}, stringid.TruncateID(networkID), networkIDHeader, ctx.ID},
|
}, stringid.TruncateID(networkID), ctx.ID},
|
||||||
{networkContext{
|
{networkContext{
|
||||||
n: types.NetworkResource{Name: "network_name"},
|
n: types.NetworkResource{Name: "network_name"},
|
||||||
}, "network_name", nameHeader, ctx.Name},
|
}, "network_name", ctx.Name},
|
||||||
{networkContext{
|
{networkContext{
|
||||||
n: types.NetworkResource{Driver: "driver_name"},
|
n: types.NetworkResource{Driver: "driver_name"},
|
||||||
}, "driver_name", driverHeader, ctx.Driver},
|
}, "driver_name", ctx.Driver},
|
||||||
{networkContext{
|
{networkContext{
|
||||||
n: types.NetworkResource{EnableIPv6: true},
|
n: types.NetworkResource{EnableIPv6: true},
|
||||||
}, "true", ipv6Header, ctx.IPv6},
|
}, "true", ctx.IPv6},
|
||||||
{networkContext{
|
{networkContext{
|
||||||
n: types.NetworkResource{EnableIPv6: false},
|
n: types.NetworkResource{EnableIPv6: false},
|
||||||
}, "false", ipv6Header, ctx.IPv6},
|
}, "false", ctx.IPv6},
|
||||||
{networkContext{
|
{networkContext{
|
||||||
n: types.NetworkResource{Internal: true},
|
n: types.NetworkResource{Internal: true},
|
||||||
}, "true", internalHeader, ctx.Internal},
|
}, "true", ctx.Internal},
|
||||||
{networkContext{
|
{networkContext{
|
||||||
n: types.NetworkResource{Internal: false},
|
n: types.NetworkResource{Internal: false},
|
||||||
}, "false", internalHeader, ctx.Internal},
|
}, "false", ctx.Internal},
|
||||||
{networkContext{
|
{networkContext{
|
||||||
n: types.NetworkResource{},
|
n: types.NetworkResource{},
|
||||||
}, "", labelsHeader, ctx.Labels},
|
}, "", ctx.Labels},
|
||||||
{networkContext{
|
{networkContext{
|
||||||
n: types.NetworkResource{Labels: map[string]string{"label1": "value1", "label2": "value2"}},
|
n: types.NetworkResource{Labels: map[string]string{"label1": "value1", "label2": "value2"}},
|
||||||
}, "label1=value1,label2=value2", labelsHeader, ctx.Labels},
|
}, "label1=value1,label2=value2", ctx.Labels},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
@ -64,11 +63,6 @@ func TestNetworkContext(t *testing.T) {
|
||||||
} else if v != c.expValue {
|
} else if v != c.expValue {
|
||||||
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
h := ctx.FullHeader()
|
|
||||||
if h != c.expHeader {
|
|
||||||
t.Fatalf("Expected %s, was %s\n", c.expHeader, h)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,15 @@ func PluginWrite(ctx Context, plugins []*types.Plugin) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ctx.Write(&pluginContext{}, render)
|
pluginCtx := pluginContext{}
|
||||||
|
pluginCtx.header = map[string]string{
|
||||||
|
"ID": pluginIDHeader,
|
||||||
|
"Name": nameHeader,
|
||||||
|
"Description": descriptionHeader,
|
||||||
|
"Enabled": enabledHeader,
|
||||||
|
"PluginReference": imageHeader,
|
||||||
|
}
|
||||||
|
return ctx.Write(&pluginCtx, render)
|
||||||
}
|
}
|
||||||
|
|
||||||
type pluginContext struct {
|
type pluginContext struct {
|
||||||
|
@ -58,7 +66,6 @@ func (c *pluginContext) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *pluginContext) ID() string {
|
func (c *pluginContext) ID() string {
|
||||||
c.AddHeader(pluginIDHeader)
|
|
||||||
if c.trunc {
|
if c.trunc {
|
||||||
return stringid.TruncateID(c.p.ID)
|
return stringid.TruncateID(c.p.ID)
|
||||||
}
|
}
|
||||||
|
@ -66,12 +73,10 @@ func (c *pluginContext) ID() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *pluginContext) Name() string {
|
func (c *pluginContext) Name() string {
|
||||||
c.AddHeader(nameHeader)
|
|
||||||
return c.p.Name
|
return c.p.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *pluginContext) Description() string {
|
func (c *pluginContext) Description() string {
|
||||||
c.AddHeader(descriptionHeader)
|
|
||||||
desc := strings.Replace(c.p.Config.Description, "\n", "", -1)
|
desc := strings.Replace(c.p.Config.Description, "\n", "", -1)
|
||||||
desc = strings.Replace(desc, "\r", "", -1)
|
desc = strings.Replace(desc, "\r", "", -1)
|
||||||
if c.trunc {
|
if c.trunc {
|
||||||
|
@ -82,11 +87,9 @@ func (c *pluginContext) Description() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *pluginContext) Enabled() bool {
|
func (c *pluginContext) Enabled() bool {
|
||||||
c.AddHeader(enabledHeader)
|
|
||||||
return c.p.Enabled
|
return c.p.Enabled
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *pluginContext) PluginReference() string {
|
func (c *pluginContext) PluginReference() string {
|
||||||
c.AddHeader(imageHeader)
|
|
||||||
return c.p.PluginReference
|
return c.p.PluginReference
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,23 +18,22 @@ func TestPluginContext(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
pluginCtx pluginContext
|
pluginCtx pluginContext
|
||||||
expValue string
|
expValue string
|
||||||
expHeader string
|
|
||||||
call func() string
|
call func() string
|
||||||
}{
|
}{
|
||||||
{pluginContext{
|
{pluginContext{
|
||||||
p: types.Plugin{ID: pluginID},
|
p: types.Plugin{ID: pluginID},
|
||||||
trunc: false,
|
trunc: false,
|
||||||
}, pluginID, pluginIDHeader, ctx.ID},
|
}, pluginID, ctx.ID},
|
||||||
{pluginContext{
|
{pluginContext{
|
||||||
p: types.Plugin{ID: pluginID},
|
p: types.Plugin{ID: pluginID},
|
||||||
trunc: true,
|
trunc: true,
|
||||||
}, stringid.TruncateID(pluginID), pluginIDHeader, ctx.ID},
|
}, stringid.TruncateID(pluginID), ctx.ID},
|
||||||
{pluginContext{
|
{pluginContext{
|
||||||
p: types.Plugin{Name: "plugin_name"},
|
p: types.Plugin{Name: "plugin_name"},
|
||||||
}, "plugin_name", nameHeader, ctx.Name},
|
}, "plugin_name", ctx.Name},
|
||||||
{pluginContext{
|
{pluginContext{
|
||||||
p: types.Plugin{Config: types.PluginConfig{Description: "plugin_description"}},
|
p: types.Plugin{Config: types.PluginConfig{Description: "plugin_description"}},
|
||||||
}, "plugin_description", descriptionHeader, ctx.Description},
|
}, "plugin_description", ctx.Description},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
@ -45,11 +44,6 @@ func TestPluginContext(t *testing.T) {
|
||||||
} else if v != c.expValue {
|
} else if v != c.expValue {
|
||||||
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
h := ctx.FullHeader()
|
|
||||||
if h != c.expHeader {
|
|
||||||
t.Fatalf("Expected %s, was %s\n", c.expHeader, h)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -388,7 +388,15 @@ func ServiceListWrite(ctx Context, services []swarm.Service, info map[string]Ser
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ctx.Write(&serviceContext{}, render)
|
serviceCtx := serviceContext{}
|
||||||
|
serviceCtx.header = map[string]string{
|
||||||
|
"ID": serviceIDHeader,
|
||||||
|
"Name": nameHeader,
|
||||||
|
"Mode": modeHeader,
|
||||||
|
"Replicas": replicasHeader,
|
||||||
|
"Image": imageHeader,
|
||||||
|
}
|
||||||
|
return ctx.Write(&serviceCtx, render)
|
||||||
}
|
}
|
||||||
|
|
||||||
type serviceContext struct {
|
type serviceContext struct {
|
||||||
|
@ -403,27 +411,22 @@ func (c *serviceContext) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *serviceContext) ID() string {
|
func (c *serviceContext) ID() string {
|
||||||
c.AddHeader(serviceIDHeader)
|
|
||||||
return stringid.TruncateID(c.service.ID)
|
return stringid.TruncateID(c.service.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *serviceContext) Name() string {
|
func (c *serviceContext) Name() string {
|
||||||
c.AddHeader(nameHeader)
|
|
||||||
return c.service.Spec.Name
|
return c.service.Spec.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *serviceContext) Mode() string {
|
func (c *serviceContext) Mode() string {
|
||||||
c.AddHeader(modeHeader)
|
|
||||||
return c.mode
|
return c.mode
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *serviceContext) Replicas() string {
|
func (c *serviceContext) Replicas() string {
|
||||||
c.AddHeader(replicasHeader)
|
|
||||||
return c.replicas
|
return c.replicas
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *serviceContext) Image() string {
|
func (c *serviceContext) Image() string {
|
||||||
c.AddHeader(imageHeader)
|
|
||||||
image := c.service.Spec.TaskTemplate.ContainerSpec.Image
|
image := c.service.Spec.TaskTemplate.ContainerSpec.Image
|
||||||
if ref, err := reference.ParseNormalizedNamed(image); err == nil {
|
if ref, err := reference.ParseNormalizedNamed(image); err == nil {
|
||||||
// update image string for display, (strips any digest)
|
// update image string for display, (strips any digest)
|
||||||
|
|
|
@ -129,7 +129,24 @@ func ContainerStatsWrite(ctx Context, containerStats []StatsEntry, osType string
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ctx.Write(&containerStatsContext{os: osType}, render)
|
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 {
|
type containerStatsContext struct {
|
||||||
|
@ -143,12 +160,10 @@ func (c *containerStatsContext) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerStatsContext) Container() string {
|
func (c *containerStatsContext) Container() string {
|
||||||
c.AddHeader(containerHeader)
|
|
||||||
return c.s.Container
|
return c.s.Container
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerStatsContext) Name() string {
|
func (c *containerStatsContext) Name() string {
|
||||||
c.AddHeader(nameHeader)
|
|
||||||
if len(c.s.Name) > 1 {
|
if len(c.s.Name) > 1 {
|
||||||
return c.s.Name[1:]
|
return c.s.Name[1:]
|
||||||
}
|
}
|
||||||
|
@ -156,12 +171,10 @@ func (c *containerStatsContext) Name() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerStatsContext) ID() string {
|
func (c *containerStatsContext) ID() string {
|
||||||
c.AddHeader(containerIDHeader)
|
|
||||||
return c.s.ID
|
return c.s.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerStatsContext) CPUPerc() string {
|
func (c *containerStatsContext) CPUPerc() string {
|
||||||
c.AddHeader(cpuPercHeader)
|
|
||||||
if c.s.IsInvalid {
|
if c.s.IsInvalid {
|
||||||
return fmt.Sprintf("--")
|
return fmt.Sprintf("--")
|
||||||
}
|
}
|
||||||
|
@ -169,11 +182,6 @@ func (c *containerStatsContext) CPUPerc() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerStatsContext) MemUsage() string {
|
func (c *containerStatsContext) MemUsage() string {
|
||||||
header := memUseHeader
|
|
||||||
if c.os == winOSType {
|
|
||||||
header = winMemUseHeader
|
|
||||||
}
|
|
||||||
c.AddHeader(header)
|
|
||||||
if c.s.IsInvalid {
|
if c.s.IsInvalid {
|
||||||
return fmt.Sprintf("-- / --")
|
return fmt.Sprintf("-- / --")
|
||||||
}
|
}
|
||||||
|
@ -184,8 +192,6 @@ func (c *containerStatsContext) MemUsage() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerStatsContext) MemPerc() string {
|
func (c *containerStatsContext) MemPerc() string {
|
||||||
header := memPercHeader
|
|
||||||
c.AddHeader(header)
|
|
||||||
if c.s.IsInvalid || c.os == winOSType {
|
if c.s.IsInvalid || c.os == winOSType {
|
||||||
return fmt.Sprintf("--")
|
return fmt.Sprintf("--")
|
||||||
}
|
}
|
||||||
|
@ -193,7 +199,6 @@ func (c *containerStatsContext) MemPerc() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerStatsContext) NetIO() string {
|
func (c *containerStatsContext) NetIO() string {
|
||||||
c.AddHeader(netIOHeader)
|
|
||||||
if c.s.IsInvalid {
|
if c.s.IsInvalid {
|
||||||
return fmt.Sprintf("--")
|
return fmt.Sprintf("--")
|
||||||
}
|
}
|
||||||
|
@ -201,7 +206,6 @@ func (c *containerStatsContext) NetIO() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerStatsContext) BlockIO() string {
|
func (c *containerStatsContext) BlockIO() string {
|
||||||
c.AddHeader(blockIOHeader)
|
|
||||||
if c.s.IsInvalid {
|
if c.s.IsInvalid {
|
||||||
return fmt.Sprintf("--")
|
return fmt.Sprintf("--")
|
||||||
}
|
}
|
||||||
|
@ -209,7 +213,6 @@ func (c *containerStatsContext) BlockIO() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *containerStatsContext) PIDs() string {
|
func (c *containerStatsContext) PIDs() string {
|
||||||
c.AddHeader(pidsHeader)
|
|
||||||
if c.s.IsInvalid || c.os == winOSType {
|
if c.s.IsInvalid || c.os == winOSType {
|
||||||
return fmt.Sprintf("--")
|
return fmt.Sprintf("--")
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,11 +42,6 @@ func TestContainerStatsContext(t *testing.T) {
|
||||||
if v := te.call(); v != te.expValue {
|
if v := te.call(); v != te.expValue {
|
||||||
t.Fatalf("Expected %q, got %q", te.expValue, v)
|
t.Fatalf("Expected %q, got %q", te.expValue, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
h := ctx.FullHeader()
|
|
||||||
if h != te.expHeader {
|
|
||||||
t.Fatalf("Expected %q, got %q", te.expHeader, h)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,7 +45,17 @@ func VolumeWrite(ctx Context, volumes []*types.Volume) error {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ctx.Write(&volumeContext{}, render)
|
return ctx.Write(newVolumeContext(), render)
|
||||||
|
}
|
||||||
|
|
||||||
|
type volumeHeaderContext map[string]string
|
||||||
|
|
||||||
|
func (c volumeHeaderContext) Label(name string) string {
|
||||||
|
n := strings.Split(name, ".")
|
||||||
|
r := strings.NewReplacer("-", " ", "_", " ")
|
||||||
|
h := r.Replace(n[len(n)-1])
|
||||||
|
|
||||||
|
return h
|
||||||
}
|
}
|
||||||
|
|
||||||
type volumeContext struct {
|
type volumeContext struct {
|
||||||
|
@ -53,32 +63,41 @@ type volumeContext struct {
|
||||||
v types.Volume
|
v types.Volume
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newVolumeContext() *volumeContext {
|
||||||
|
volumeCtx := volumeContext{}
|
||||||
|
volumeCtx.header = volumeHeaderContext{
|
||||||
|
"Name": volumeNameHeader,
|
||||||
|
"Driver": driverHeader,
|
||||||
|
"Scope": scopeHeader,
|
||||||
|
"Mountpoint": mountpointHeader,
|
||||||
|
"Labels": labelsHeader,
|
||||||
|
"Links": linksHeader,
|
||||||
|
"Size": sizeHeader,
|
||||||
|
}
|
||||||
|
return &volumeCtx
|
||||||
|
}
|
||||||
|
|
||||||
func (c *volumeContext) MarshalJSON() ([]byte, error) {
|
func (c *volumeContext) MarshalJSON() ([]byte, error) {
|
||||||
return marshalJSON(c)
|
return marshalJSON(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *volumeContext) Name() string {
|
func (c *volumeContext) Name() string {
|
||||||
c.AddHeader(volumeNameHeader)
|
|
||||||
return c.v.Name
|
return c.v.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *volumeContext) Driver() string {
|
func (c *volumeContext) Driver() string {
|
||||||
c.AddHeader(driverHeader)
|
|
||||||
return c.v.Driver
|
return c.v.Driver
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *volumeContext) Scope() string {
|
func (c *volumeContext) Scope() string {
|
||||||
c.AddHeader(scopeHeader)
|
|
||||||
return c.v.Scope
|
return c.v.Scope
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *volumeContext) Mountpoint() string {
|
func (c *volumeContext) Mountpoint() string {
|
||||||
c.AddHeader(mountpointHeader)
|
|
||||||
return c.v.Mountpoint
|
return c.v.Mountpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *volumeContext) Labels() string {
|
func (c *volumeContext) Labels() string {
|
||||||
c.AddHeader(labelsHeader)
|
|
||||||
if c.v.Labels == nil {
|
if c.v.Labels == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -91,13 +110,6 @@ func (c *volumeContext) Labels() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *volumeContext) Label(name string) string {
|
func (c *volumeContext) Label(name string) string {
|
||||||
|
|
||||||
n := strings.Split(name, ".")
|
|
||||||
r := strings.NewReplacer("-", " ", "_", " ")
|
|
||||||
h := r.Replace(n[len(n)-1])
|
|
||||||
|
|
||||||
c.AddHeader(h)
|
|
||||||
|
|
||||||
if c.v.Labels == nil {
|
if c.v.Labels == nil {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -105,7 +117,6 @@ func (c *volumeContext) Label(name string) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *volumeContext) Links() string {
|
func (c *volumeContext) Links() string {
|
||||||
c.AddHeader(linksHeader)
|
|
||||||
if c.v.UsageData == nil {
|
if c.v.UsageData == nil {
|
||||||
return "N/A"
|
return "N/A"
|
||||||
}
|
}
|
||||||
|
@ -113,7 +124,6 @@ func (c *volumeContext) Links() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *volumeContext) Size() string {
|
func (c *volumeContext) Size() string {
|
||||||
c.AddHeader(sizeHeader)
|
|
||||||
if c.v.UsageData == nil {
|
if c.v.UsageData == nil {
|
||||||
return "N/A"
|
return "N/A"
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,27 +18,26 @@ func TestVolumeContext(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
volumeCtx volumeContext
|
volumeCtx volumeContext
|
||||||
expValue string
|
expValue string
|
||||||
expHeader string
|
|
||||||
call func() string
|
call func() string
|
||||||
}{
|
}{
|
||||||
{volumeContext{
|
{volumeContext{
|
||||||
v: types.Volume{Name: volumeName},
|
v: types.Volume{Name: volumeName},
|
||||||
}, volumeName, volumeNameHeader, ctx.Name},
|
}, volumeName, ctx.Name},
|
||||||
{volumeContext{
|
{volumeContext{
|
||||||
v: types.Volume{Driver: "driver_name"},
|
v: types.Volume{Driver: "driver_name"},
|
||||||
}, "driver_name", driverHeader, ctx.Driver},
|
}, "driver_name", ctx.Driver},
|
||||||
{volumeContext{
|
{volumeContext{
|
||||||
v: types.Volume{Scope: "local"},
|
v: types.Volume{Scope: "local"},
|
||||||
}, "local", scopeHeader, ctx.Scope},
|
}, "local", ctx.Scope},
|
||||||
{volumeContext{
|
{volumeContext{
|
||||||
v: types.Volume{Mountpoint: "mountpoint"},
|
v: types.Volume{Mountpoint: "mountpoint"},
|
||||||
}, "mountpoint", mountpointHeader, ctx.Mountpoint},
|
}, "mountpoint", ctx.Mountpoint},
|
||||||
{volumeContext{
|
{volumeContext{
|
||||||
v: types.Volume{},
|
v: types.Volume{},
|
||||||
}, "", labelsHeader, ctx.Labels},
|
}, "", ctx.Labels},
|
||||||
{volumeContext{
|
{volumeContext{
|
||||||
v: types.Volume{Labels: map[string]string{"label1": "value1", "label2": "value2"}},
|
v: types.Volume{Labels: map[string]string{"label1": "value1", "label2": "value2"}},
|
||||||
}, "label1=value1,label2=value2", labelsHeader, ctx.Labels},
|
}, "label1=value1,label2=value2", ctx.Labels},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
|
@ -49,11 +48,6 @@ func TestVolumeContext(t *testing.T) {
|
||||||
} else if v != c.expValue {
|
} else if v != c.expValue {
|
||||||
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
h := ctx.FullHeader()
|
|
||||||
if h != c.expHeader {
|
|
||||||
t.Fatalf("Expected %s, was %s\n", c.expHeader, h)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue