This fix is an attempt to address
https://github.com/docker/docker/pull/28213#issuecomment-273840405
Currently when specify table format with table `--format "table {{.ID}}..."`,
the delimiter in the header section of the table is always `"\t"`.
That is actually different from the content of the table as the delimiter
could be anything (or even contatenated with `.`, for example):
```
$ docker service ps web --format 'table {{.Name}}.{{.ID}}' --no-trunc
NAME ID
web.1.inyhxhvjcijl0hdbu8lgrwwh7
\_ web.1.p9m4kx2srjqmfms4igam0uqlb
```
This fix is an attampt to address the skewness of the table when delimiter
is not `"\t"`.
The basic idea is that, when header consists of `table` key, the header section
will be redendered the same way as content section. A map mapping each
placeholder name to the HEADER entry name is used for the context of the header.
Unit tests have been updated and added to cover the changes.
This fix is related to #28313.
Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
| ... | ... |
@@ -14,7 +14,7 @@ import ( |
| 14 | 14 |
) |
| 15 | 15 |
|
| 16 | 16 |
const ( |
| 17 |
- defaultContainerTableFormat = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.RunningFor}} ago\t{{.Status}}\t{{.Ports}}\t{{.Names}}"
|
|
| 17 |
+ defaultContainerTableFormat = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.RunningFor}}\t{{.Status}}\t{{.Ports}}\t{{.Names}}"
|
|
| 18 | 18 |
|
| 19 | 19 |
containerIDHeader = "CONTAINER ID" |
| 20 | 20 |
namesHeader = "NAMES" |
| ... | ... |
@@ -71,7 +71,17 @@ func ContainerWrite(ctx Context, containers []types.Container) error {
|
| 71 | 71 |
} |
| 72 | 72 |
return nil |
| 73 | 73 |
} |
| 74 |
- return ctx.Write(&containerContext{}, render)
|
|
| 74 |
+ return ctx.Write(newContainerContext(), render) |
|
| 75 |
+} |
|
| 76 |
+ |
|
| 77 |
+type containerHeaderContext map[string]string |
|
| 78 |
+ |
|
| 79 |
+func (c containerHeaderContext) Label(name string) string {
|
|
| 80 |
+ n := strings.Split(name, ".") |
|
| 81 |
+ r := strings.NewReplacer("-", " ", "_", " ")
|
|
| 82 |
+ h := r.Replace(n[len(n)-1]) |
|
| 83 |
+ |
|
| 84 |
+ return h |
|
| 75 | 85 |
} |
| 76 | 86 |
|
| 77 | 87 |
type containerContext struct {
|
| ... | ... |
@@ -80,12 +90,31 @@ type containerContext struct {
|
| 80 | 80 |
c types.Container |
| 81 | 81 |
} |
| 82 | 82 |
|
| 83 |
+func newContainerContext() *containerContext {
|
|
| 84 |
+ containerCtx := containerContext{}
|
|
| 85 |
+ containerCtx.header = containerHeaderContext{
|
|
| 86 |
+ "ID": containerIDHeader, |
|
| 87 |
+ "Names": namesHeader, |
|
| 88 |
+ "Image": imageHeader, |
|
| 89 |
+ "Command": commandHeader, |
|
| 90 |
+ "CreatedAt": createdAtHeader, |
|
| 91 |
+ "RunningFor": runningForHeader, |
|
| 92 |
+ "Ports": portsHeader, |
|
| 93 |
+ "Status": statusHeader, |
|
| 94 |
+ "Size": sizeHeader, |
|
| 95 |
+ "Labels": labelsHeader, |
|
| 96 |
+ "Mounts": mountsHeader, |
|
| 97 |
+ "LocalVolumes": localVolumes, |
|
| 98 |
+ "Networks": networksHeader, |
|
| 99 |
+ } |
|
| 100 |
+ return &containerCtx |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 83 | 103 |
func (c *containerContext) MarshalJSON() ([]byte, error) {
|
| 84 | 104 |
return marshalJSON(c) |
| 85 | 105 |
} |
| 86 | 106 |
|
| 87 | 107 |
func (c *containerContext) ID() string {
|
| 88 |
- c.AddHeader(containerIDHeader) |
|
| 89 | 108 |
if c.trunc {
|
| 90 | 109 |
return stringid.TruncateID(c.c.ID) |
| 91 | 110 |
} |
| ... | ... |
@@ -93,7 +122,6 @@ func (c *containerContext) ID() string {
|
| 93 | 93 |
} |
| 94 | 94 |
|
| 95 | 95 |
func (c *containerContext) Names() string {
|
| 96 |
- c.AddHeader(namesHeader) |
|
| 97 | 96 |
names := stripNamePrefix(c.c.Names) |
| 98 | 97 |
if c.trunc {
|
| 99 | 98 |
for _, name := range names {
|
| ... | ... |
@@ -107,7 +135,6 @@ func (c *containerContext) Names() string {
|
| 107 | 107 |
} |
| 108 | 108 |
|
| 109 | 109 |
func (c *containerContext) Image() string {
|
| 110 |
- c.AddHeader(imageHeader) |
|
| 111 | 110 |
if c.c.Image == "" {
|
| 112 | 111 |
return "<no image>" |
| 113 | 112 |
} |
| ... | ... |
@@ -120,7 +147,6 @@ func (c *containerContext) Image() string {
|
| 120 | 120 |
} |
| 121 | 121 |
|
| 122 | 122 |
func (c *containerContext) Command() string {
|
| 123 |
- c.AddHeader(commandHeader) |
|
| 124 | 123 |
command := c.c.Command |
| 125 | 124 |
if c.trunc {
|
| 126 | 125 |
command = stringutils.Ellipsis(command, 20) |
| ... | ... |
@@ -129,28 +155,23 @@ func (c *containerContext) Command() string {
|
| 129 | 129 |
} |
| 130 | 130 |
|
| 131 | 131 |
func (c *containerContext) CreatedAt() string {
|
| 132 |
- c.AddHeader(createdAtHeader) |
|
| 133 | 132 |
return time.Unix(int64(c.c.Created), 0).String() |
| 134 | 133 |
} |
| 135 | 134 |
|
| 136 | 135 |
func (c *containerContext) RunningFor() string {
|
| 137 |
- c.AddHeader(runningForHeader) |
|
| 138 | 136 |
createdAt := time.Unix(int64(c.c.Created), 0) |
| 139 |
- return units.HumanDuration(time.Now().UTC().Sub(createdAt)) |
|
| 137 |
+ return units.HumanDuration(time.Now().UTC().Sub(createdAt)) + " ago" |
|
| 140 | 138 |
} |
| 141 | 139 |
|
| 142 | 140 |
func (c *containerContext) Ports() string {
|
| 143 |
- c.AddHeader(portsHeader) |
|
| 144 | 141 |
return api.DisplayablePorts(c.c.Ports) |
| 145 | 142 |
} |
| 146 | 143 |
|
| 147 | 144 |
func (c *containerContext) Status() string {
|
| 148 |
- c.AddHeader(statusHeader) |
|
| 149 | 145 |
return c.c.Status |
| 150 | 146 |
} |
| 151 | 147 |
|
| 152 | 148 |
func (c *containerContext) Size() string {
|
| 153 |
- c.AddHeader(sizeHeader) |
|
| 154 | 149 |
srw := units.HumanSizeWithPrecision(float64(c.c.SizeRw), 3) |
| 155 | 150 |
sv := units.HumanSizeWithPrecision(float64(c.c.SizeRootFs), 3) |
| 156 | 151 |
|
| ... | ... |
@@ -162,7 +183,6 @@ func (c *containerContext) Size() string {
|
| 162 | 162 |
} |
| 163 | 163 |
|
| 164 | 164 |
func (c *containerContext) Labels() string {
|
| 165 |
- c.AddHeader(labelsHeader) |
|
| 166 | 165 |
if c.c.Labels == nil {
|
| 167 | 166 |
return "" |
| 168 | 167 |
} |
| ... | ... |
@@ -175,12 +195,6 @@ func (c *containerContext) Labels() string {
|
| 175 | 175 |
} |
| 176 | 176 |
|
| 177 | 177 |
func (c *containerContext) Label(name string) string {
|
| 178 |
- n := strings.Split(name, ".") |
|
| 179 |
- r := strings.NewReplacer("-", " ", "_", " ")
|
|
| 180 |
- h := r.Replace(n[len(n)-1]) |
|
| 181 |
- |
|
| 182 |
- c.AddHeader(h) |
|
| 183 |
- |
|
| 184 | 178 |
if c.c.Labels == nil {
|
| 185 | 179 |
return "" |
| 186 | 180 |
} |
| ... | ... |
@@ -188,8 +202,6 @@ func (c *containerContext) Label(name string) string {
|
| 188 | 188 |
} |
| 189 | 189 |
|
| 190 | 190 |
func (c *containerContext) Mounts() string {
|
| 191 |
- c.AddHeader(mountsHeader) |
|
| 192 |
- |
|
| 193 | 191 |
var name string |
| 194 | 192 |
var mounts []string |
| 195 | 193 |
for _, m := range c.c.Mounts {
|
| ... | ... |
@@ -207,8 +219,6 @@ func (c *containerContext) Mounts() string {
|
| 207 | 207 |
} |
| 208 | 208 |
|
| 209 | 209 |
func (c *containerContext) LocalVolumes() string {
|
| 210 |
- c.AddHeader(localVolumes) |
|
| 211 |
- |
|
| 212 | 210 |
count := 0 |
| 213 | 211 |
for _, m := range c.c.Mounts {
|
| 214 | 212 |
if m.Driver == "local" {
|
| ... | ... |
@@ -220,8 +230,6 @@ func (c *containerContext) LocalVolumes() string {
|
| 220 | 220 |
} |
| 221 | 221 |
|
| 222 | 222 |
func (c *containerContext) Networks() string {
|
| 223 |
- c.AddHeader(networksHeader) |
|
| 224 |
- |
|
| 225 | 223 |
if c.c.NetworkSettings == nil {
|
| 226 | 224 |
return "" |
| 227 | 225 |
} |
| ... | ... |
@@ -22,22 +22,20 @@ func TestContainerPsContext(t *testing.T) {
|
| 22 | 22 |
container types.Container |
| 23 | 23 |
trunc bool |
| 24 | 24 |
expValue string |
| 25 |
- expHeader string |
|
| 26 | 25 |
call func() string |
| 27 | 26 |
}{
|
| 28 |
- {types.Container{ID: containerID}, true, stringid.TruncateID(containerID), containerIDHeader, ctx.ID},
|
|
| 29 |
- {types.Container{ID: containerID}, false, containerID, containerIDHeader, ctx.ID},
|
|
| 30 |
- {types.Container{Names: []string{"/foobar_baz"}}, true, "foobar_baz", namesHeader, ctx.Names},
|
|
| 31 |
- {types.Container{Image: "ubuntu"}, true, "ubuntu", imageHeader, ctx.Image},
|
|
| 32 |
- {types.Container{Image: "verylongimagename"}, true, "verylongimagename", imageHeader, ctx.Image},
|
|
| 33 |
- {types.Container{Image: "verylongimagename"}, false, "verylongimagename", imageHeader, ctx.Image},
|
|
| 27 |
+ {types.Container{ID: containerID}, true, stringid.TruncateID(containerID), ctx.ID},
|
|
| 28 |
+ {types.Container{ID: containerID}, false, containerID, ctx.ID},
|
|
| 29 |
+ {types.Container{Names: []string{"/foobar_baz"}}, true, "foobar_baz", ctx.Names},
|
|
| 30 |
+ {types.Container{Image: "ubuntu"}, true, "ubuntu", ctx.Image},
|
|
| 31 |
+ {types.Container{Image: "verylongimagename"}, true, "verylongimagename", ctx.Image},
|
|
| 32 |
+ {types.Container{Image: "verylongimagename"}, false, "verylongimagename", ctx.Image},
|
|
| 34 | 33 |
{types.Container{
|
| 35 | 34 |
Image: "a5a665ff33eced1e0803148700880edab4", |
| 36 | 35 |
ImageID: "a5a665ff33eced1e0803148700880edab4269067ed77e27737a708d0d293fbf5", |
| 37 | 36 |
}, |
| 38 | 37 |
true, |
| 39 | 38 |
"a5a665ff33ec", |
| 40 |
- imageHeader, |
|
| 41 | 39 |
ctx.Image, |
| 42 | 40 |
}, |
| 43 | 41 |
{types.Container{
|
| ... | ... |
@@ -46,19 +44,18 @@ func TestContainerPsContext(t *testing.T) {
|
| 46 | 46 |
}, |
| 47 | 47 |
false, |
| 48 | 48 |
"a5a665ff33eced1e0803148700880edab4", |
| 49 |
- imageHeader, |
|
| 50 | 49 |
ctx.Image, |
| 51 | 50 |
}, |
| 52 |
- {types.Container{Image: ""}, true, "<no image>", imageHeader, ctx.Image},
|
|
| 53 |
- {types.Container{Command: "sh -c 'ls -la'"}, true, `"sh -c 'ls -la'"`, commandHeader, ctx.Command},
|
|
| 54 |
- {types.Container{Created: unix}, true, time.Unix(unix, 0).String(), createdAtHeader, ctx.CreatedAt},
|
|
| 55 |
- {types.Container{Ports: []types.Port{{PrivatePort: 8080, PublicPort: 8080, Type: "tcp"}}}, true, "8080/tcp", portsHeader, ctx.Ports},
|
|
| 56 |
- {types.Container{Status: "RUNNING"}, true, "RUNNING", statusHeader, ctx.Status},
|
|
| 57 |
- {types.Container{SizeRw: 10}, true, "10B", sizeHeader, ctx.Size},
|
|
| 58 |
- {types.Container{SizeRw: 10, SizeRootFs: 20}, true, "10B (virtual 20B)", sizeHeader, ctx.Size},
|
|
| 59 |
- {types.Container{}, true, "", labelsHeader, ctx.Labels},
|
|
| 60 |
- {types.Container{Labels: map[string]string{"cpu": "6", "storage": "ssd"}}, true, "cpu=6,storage=ssd", labelsHeader, ctx.Labels},
|
|
| 61 |
- {types.Container{Created: unix}, true, "About a minute", runningForHeader, ctx.RunningFor},
|
|
| 51 |
+ {types.Container{Image: ""}, true, "<no image>", ctx.Image},
|
|
| 52 |
+ {types.Container{Command: "sh -c 'ls -la'"}, true, `"sh -c 'ls -la'"`, ctx.Command},
|
|
| 53 |
+ {types.Container{Created: unix}, true, time.Unix(unix, 0).String(), ctx.CreatedAt},
|
|
| 54 |
+ {types.Container{Ports: []types.Port{{PrivatePort: 8080, PublicPort: 8080, Type: "tcp"}}}, true, "8080/tcp", ctx.Ports},
|
|
| 55 |
+ {types.Container{Status: "RUNNING"}, true, "RUNNING", ctx.Status},
|
|
| 56 |
+ {types.Container{SizeRw: 10}, true, "10B", ctx.Size},
|
|
| 57 |
+ {types.Container{SizeRw: 10, SizeRootFs: 20}, true, "10B (virtual 20B)", ctx.Size},
|
|
| 58 |
+ {types.Container{}, true, "", ctx.Labels},
|
|
| 59 |
+ {types.Container{Labels: map[string]string{"cpu": "6", "storage": "ssd"}}, true, "cpu=6,storage=ssd", ctx.Labels},
|
|
| 60 |
+ {types.Container{Created: unix}, true, "About a minute ago", ctx.RunningFor},
|
|
| 62 | 61 |
{types.Container{
|
| 63 | 62 |
Mounts: []types.MountPoint{
|
| 64 | 63 |
{
|
| ... | ... |
@@ -67,7 +64,7 @@ func TestContainerPsContext(t *testing.T) {
|
| 67 | 67 |
Source: "/a/path", |
| 68 | 68 |
}, |
| 69 | 69 |
}, |
| 70 |
- }, true, "this-is-a-lo...", mountsHeader, ctx.Mounts}, |
|
| 70 |
+ }, true, "this-is-a-lo...", ctx.Mounts}, |
|
| 71 | 71 |
{types.Container{
|
| 72 | 72 |
Mounts: []types.MountPoint{
|
| 73 | 73 |
{
|
| ... | ... |
@@ -75,7 +72,7 @@ func TestContainerPsContext(t *testing.T) {
|
| 75 | 75 |
Source: "/a/path", |
| 76 | 76 |
}, |
| 77 | 77 |
}, |
| 78 |
- }, false, "/a/path", mountsHeader, ctx.Mounts}, |
|
| 78 |
+ }, false, "/a/path", ctx.Mounts}, |
|
| 79 | 79 |
{types.Container{
|
| 80 | 80 |
Mounts: []types.MountPoint{
|
| 81 | 81 |
{
|
| ... | ... |
@@ -84,7 +81,7 @@ func TestContainerPsContext(t *testing.T) {
|
| 84 | 84 |
Source: "/a/path", |
| 85 | 85 |
}, |
| 86 | 86 |
}, |
| 87 |
- }, false, "733908409c91817de8e92b0096373245f329f19a88e2c849f02460e9b3d1c203", mountsHeader, ctx.Mounts}, |
|
| 87 |
+ }, false, "733908409c91817de8e92b0096373245f329f19a88e2c849f02460e9b3d1c203", ctx.Mounts}, |
|
| 88 | 88 |
} |
| 89 | 89 |
|
| 90 | 90 |
for _, c := range cases {
|
| ... | ... |
@@ -95,11 +92,6 @@ func TestContainerPsContext(t *testing.T) {
|
| 95 | 95 |
} else if v != c.expValue {
|
| 96 | 96 |
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
| 97 | 97 |
} |
| 98 |
- |
|
| 99 |
- h := ctx.FullHeader() |
|
| 100 |
- if h != c.expHeader {
|
|
| 101 |
- t.Fatalf("Expected %s, was %s\n", c.expHeader, h)
|
|
| 102 |
- } |
|
| 103 | 98 |
} |
| 104 | 99 |
|
| 105 | 100 |
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) {
|
| 115 | 115 |
t.Fatalf("Expected ubuntu, was %s\n", node)
|
| 116 | 116 |
} |
| 117 | 117 |
|
| 118 |
- h := ctx.FullHeader() |
|
| 119 |
- if h != "SWARM ID\tNODE NAME" {
|
|
| 120 |
- t.Fatalf("Expected %s, was %s\n", "SWARM ID\tNODE NAME", h)
|
|
| 121 |
- |
|
| 122 |
- } |
|
| 123 |
- |
|
| 124 | 118 |
c2 := types.Container{}
|
| 125 | 119 |
ctx = containerContext{c: c2, trunc: true}
|
| 126 | 120 |
|
| ... | ... |
@@ -128,13 +114,6 @@ func TestContainerPsContext(t *testing.T) {
|
| 128 | 128 |
if label != "" {
|
| 129 | 129 |
t.Fatalf("Expected an empty string, was %s", label)
|
| 130 | 130 |
} |
| 131 |
- |
|
| 132 |
- ctx = containerContext{c: c2, trunc: true}
|
|
| 133 |
- FullHeader := ctx.FullHeader() |
|
| 134 |
- if FullHeader != "" {
|
|
| 135 |
- t.Fatalf("Expected FullHeader to be empty, was %s", FullHeader)
|
|
| 136 |
- } |
|
| 137 |
- |
|
| 138 | 131 |
} |
| 139 | 132 |
|
| 140 | 133 |
func TestContainerContextWrite(t *testing.T) {
|
| ... | ... |
@@ -333,8 +312,8 @@ func TestContainerContextWriteJSON(t *testing.T) {
|
| 333 | 333 |
} |
| 334 | 334 |
expectedCreated := time.Unix(unix, 0).String() |
| 335 | 335 |
expectedJSONs := []map[string]interface{}{
|
| 336 |
- {"Command": "\"\"", "CreatedAt": expectedCreated, "ID": "containerID1", "Image": "ubuntu", "Labels": "", "LocalVolumes": "0", "Mounts": "", "Names": "foobar_baz", "Networks": "", "Ports": "", "RunningFor": "About a minute", "Size": "0B", "Status": ""},
|
|
| 337 |
- {"Command": "\"\"", "CreatedAt": expectedCreated, "ID": "containerID2", "Image": "ubuntu", "Labels": "", "LocalVolumes": "0", "Mounts": "", "Names": "foobar_bar", "Networks": "", "Ports": "", "RunningFor": "About a minute", "Size": "0B", "Status": ""},
|
|
| 336 |
+ {"Command": "\"\"", "CreatedAt": expectedCreated, "ID": "containerID1", "Image": "ubuntu", "Labels": "", "LocalVolumes": "0", "Mounts": "", "Names": "foobar_baz", "Networks": "", "Ports": "", "RunningFor": "About a minute ago", "Size": "0B", "Status": ""},
|
|
| 337 |
+ {"Command": "\"\"", "CreatedAt": expectedCreated, "ID": "containerID2", "Image": "ubuntu", "Labels": "", "LocalVolumes": "0", "Mounts": "", "Names": "foobar_bar", "Networks": "", "Ports": "", "RunningFor": "About a minute ago", "Size": "0B", "Status": ""},
|
|
| 338 | 338 |
} |
| 339 | 339 |
out := bytes.NewBufferString("")
|
| 340 | 340 |
err := ContainerWrite(Context{Format: "{{json .}}", Output: out}, containers)
|
| ... | ... |
@@ -1,9 +1,5 @@ |
| 1 | 1 |
package formatter |
| 2 | 2 |
|
| 3 |
-import ( |
|
| 4 |
- "strings" |
|
| 5 |
-) |
|
| 6 |
- |
|
| 7 | 3 |
const ( |
| 8 | 4 |
imageHeader = "IMAGE" |
| 9 | 5 |
createdSinceHeader = "CREATED" |
| ... | ... |
@@ -16,29 +12,17 @@ const ( |
| 16 | 16 |
) |
| 17 | 17 |
|
| 18 | 18 |
type subContext interface {
|
| 19 |
- FullHeader() string |
|
| 20 |
- AddHeader(header string) |
|
| 19 |
+ FullHeader() interface{}
|
|
| 21 | 20 |
} |
| 22 | 21 |
|
| 23 | 22 |
// HeaderContext provides the subContext interface for managing headers |
| 24 | 23 |
type HeaderContext struct {
|
| 25 |
- header []string |
|
| 26 |
-} |
|
| 27 |
- |
|
| 28 |
-// FullHeader returns the header as a string |
|
| 29 |
-func (c *HeaderContext) FullHeader() string {
|
|
| 30 |
- if c.header == nil {
|
|
| 31 |
- return "" |
|
| 32 |
- } |
|
| 33 |
- return strings.Join(c.header, "\t") |
|
| 24 |
+ header interface{}
|
|
| 34 | 25 |
} |
| 35 | 26 |
|
| 36 |
-// AddHeader adds another column to the header |
|
| 37 |
-func (c *HeaderContext) AddHeader(header string) {
|
|
| 38 |
- if c.header == nil {
|
|
| 39 |
- c.header = []string{}
|
|
| 40 |
- } |
|
| 41 |
- c.header = append(c.header, strings.ToUpper(header)) |
|
| 27 |
+// FullHeader returns the header as an interface |
|
| 28 |
+func (c *HeaderContext) FullHeader() interface{} {
|
|
| 29 |
+ return c.header |
|
| 42 | 30 |
} |
| 43 | 31 |
|
| 44 | 32 |
func stripNamePrefix(ss []string) []string {
|
| ... | ... |
@@ -77,7 +77,15 @@ func (ctx *DiskUsageContext) Write() {
|
| 77 | 77 |
return |
| 78 | 78 |
} |
| 79 | 79 |
|
| 80 |
- ctx.postFormat(tmpl, &diskUsageContainersContext{containers: []*types.Container{}})
|
|
| 80 |
+ diskUsageContainersCtx := diskUsageContainersContext{containers: []*types.Container{}}
|
|
| 81 |
+ diskUsageContainersCtx.header = map[string]string{
|
|
| 82 |
+ "Type": typeHeader, |
|
| 83 |
+ "TotalCount": totalHeader, |
|
| 84 |
+ "Active": activeHeader, |
|
| 85 |
+ "Size": sizeHeader, |
|
| 86 |
+ "Reclaimable": reclaimableHeader, |
|
| 87 |
+ } |
|
| 88 |
+ ctx.postFormat(tmpl, &diskUsageContainersCtx) |
|
| 81 | 89 |
|
| 82 | 90 |
return |
| 83 | 91 |
} |
| ... | ... |
@@ -114,7 +122,7 @@ func (ctx *DiskUsageContext) Write() {
|
| 114 | 114 |
return |
| 115 | 115 |
} |
| 116 | 116 |
} |
| 117 |
- ctx.postFormat(tmpl, &imageContext{})
|
|
| 117 |
+ ctx.postFormat(tmpl, newImageContext()) |
|
| 118 | 118 |
|
| 119 | 119 |
// Now containers |
| 120 | 120 |
ctx.Output.Write([]byte("\nContainers space usage:\n\n"))
|
| ... | ... |
@@ -133,7 +141,7 @@ func (ctx *DiskUsageContext) Write() {
|
| 133 | 133 |
return |
| 134 | 134 |
} |
| 135 | 135 |
} |
| 136 |
- ctx.postFormat(tmpl, &containerContext{})
|
|
| 136 |
+ ctx.postFormat(tmpl, newContainerContext()) |
|
| 137 | 137 |
|
| 138 | 138 |
// And volumes |
| 139 | 139 |
ctx.Output.Write([]byte("\nLocal Volumes space usage:\n\n"))
|
| ... | ... |
@@ -149,7 +157,7 @@ func (ctx *DiskUsageContext) Write() {
|
| 149 | 149 |
return |
| 150 | 150 |
} |
| 151 | 151 |
} |
| 152 |
- ctx.postFormat(tmpl, &volumeContext{v: types.Volume{}})
|
|
| 152 |
+ ctx.postFormat(tmpl, newVolumeContext()) |
|
| 153 | 153 |
} |
| 154 | 154 |
|
| 155 | 155 |
type diskUsageImagesContext struct {
|
| ... | ... |
@@ -163,17 +171,14 @@ func (c *diskUsageImagesContext) MarshalJSON() ([]byte, error) {
|
| 163 | 163 |
} |
| 164 | 164 |
|
| 165 | 165 |
func (c *diskUsageImagesContext) Type() string {
|
| 166 |
- c.AddHeader(typeHeader) |
|
| 167 | 166 |
return "Images" |
| 168 | 167 |
} |
| 169 | 168 |
|
| 170 | 169 |
func (c *diskUsageImagesContext) TotalCount() string {
|
| 171 |
- c.AddHeader(totalHeader) |
|
| 172 | 170 |
return fmt.Sprintf("%d", len(c.images))
|
| 173 | 171 |
} |
| 174 | 172 |
|
| 175 | 173 |
func (c *diskUsageImagesContext) Active() string {
|
| 176 |
- c.AddHeader(activeHeader) |
|
| 177 | 174 |
used := 0 |
| 178 | 175 |
for _, i := range c.images {
|
| 179 | 176 |
if i.Containers > 0 {
|
| ... | ... |
@@ -185,7 +190,6 @@ func (c *diskUsageImagesContext) Active() string {
|
| 185 | 185 |
} |
| 186 | 186 |
|
| 187 | 187 |
func (c *diskUsageImagesContext) Size() string {
|
| 188 |
- c.AddHeader(sizeHeader) |
|
| 189 | 188 |
return units.HumanSize(float64(c.totalSize)) |
| 190 | 189 |
|
| 191 | 190 |
} |
| ... | ... |
@@ -193,7 +197,6 @@ func (c *diskUsageImagesContext) Size() string {
|
| 193 | 193 |
func (c *diskUsageImagesContext) Reclaimable() string {
|
| 194 | 194 |
var used int64 |
| 195 | 195 |
|
| 196 |
- c.AddHeader(reclaimableHeader) |
|
| 197 | 196 |
for _, i := range c.images {
|
| 198 | 197 |
if i.Containers != 0 {
|
| 199 | 198 |
if i.VirtualSize == -1 || i.SharedSize == -1 {
|
| ... | ... |
@@ -221,12 +224,10 @@ func (c *diskUsageContainersContext) MarshalJSON() ([]byte, error) {
|
| 221 | 221 |
} |
| 222 | 222 |
|
| 223 | 223 |
func (c *diskUsageContainersContext) Type() string {
|
| 224 |
- c.AddHeader(typeHeader) |
|
| 225 | 224 |
return "Containers" |
| 226 | 225 |
} |
| 227 | 226 |
|
| 228 | 227 |
func (c *diskUsageContainersContext) TotalCount() string {
|
| 229 |
- c.AddHeader(totalHeader) |
|
| 230 | 228 |
return fmt.Sprintf("%d", len(c.containers))
|
| 231 | 229 |
} |
| 232 | 230 |
|
| ... | ... |
@@ -237,7 +238,6 @@ func (c *diskUsageContainersContext) isActive(container types.Container) bool {
|
| 237 | 237 |
} |
| 238 | 238 |
|
| 239 | 239 |
func (c *diskUsageContainersContext) Active() string {
|
| 240 |
- c.AddHeader(activeHeader) |
|
| 241 | 240 |
used := 0 |
| 242 | 241 |
for _, container := range c.containers {
|
| 243 | 242 |
if c.isActive(*container) {
|
| ... | ... |
@@ -251,7 +251,6 @@ func (c *diskUsageContainersContext) Active() string {
|
| 251 | 251 |
func (c *diskUsageContainersContext) Size() string {
|
| 252 | 252 |
var size int64 |
| 253 | 253 |
|
| 254 |
- c.AddHeader(sizeHeader) |
|
| 255 | 254 |
for _, container := range c.containers {
|
| 256 | 255 |
size += container.SizeRw |
| 257 | 256 |
} |
| ... | ... |
@@ -263,7 +262,6 @@ func (c *diskUsageContainersContext) Reclaimable() string {
|
| 263 | 263 |
var reclaimable int64 |
| 264 | 264 |
var totalSize int64 |
| 265 | 265 |
|
| 266 |
- c.AddHeader(reclaimableHeader) |
|
| 267 | 266 |
for _, container := range c.containers {
|
| 268 | 267 |
if !c.isActive(*container) {
|
| 269 | 268 |
reclaimable += container.SizeRw |
| ... | ... |
@@ -289,17 +287,14 @@ func (c *diskUsageVolumesContext) MarshalJSON() ([]byte, error) {
|
| 289 | 289 |
} |
| 290 | 290 |
|
| 291 | 291 |
func (c *diskUsageVolumesContext) Type() string {
|
| 292 |
- c.AddHeader(typeHeader) |
|
| 293 | 292 |
return "Local Volumes" |
| 294 | 293 |
} |
| 295 | 294 |
|
| 296 | 295 |
func (c *diskUsageVolumesContext) TotalCount() string {
|
| 297 |
- c.AddHeader(totalHeader) |
|
| 298 | 296 |
return fmt.Sprintf("%d", len(c.volumes))
|
| 299 | 297 |
} |
| 300 | 298 |
|
| 301 | 299 |
func (c *diskUsageVolumesContext) Active() string {
|
| 302 |
- c.AddHeader(activeHeader) |
|
| 303 | 300 |
|
| 304 | 301 |
used := 0 |
| 305 | 302 |
for _, v := range c.volumes {
|
| ... | ... |
@@ -314,7 +309,6 @@ func (c *diskUsageVolumesContext) Active() string {
|
| 314 | 314 |
func (c *diskUsageVolumesContext) Size() string {
|
| 315 | 315 |
var size int64 |
| 316 | 316 |
|
| 317 |
- c.AddHeader(sizeHeader) |
|
| 318 | 317 |
for _, v := range c.volumes {
|
| 319 | 318 |
if v.UsageData.Size != -1 {
|
| 320 | 319 |
size += v.UsageData.Size |
| ... | ... |
@@ -328,7 +322,6 @@ func (c *diskUsageVolumesContext) Reclaimable() string {
|
| 328 | 328 |
var reclaimable int64 |
| 329 | 329 |
var totalSize int64 |
| 330 | 330 |
|
| 331 |
- c.AddHeader(reclaimableHeader) |
|
| 332 | 331 |
for _, v := range c.volumes {
|
| 333 | 332 |
if v.UsageData.Size != -1 {
|
| 334 | 333 |
if v.UsageData.RefCount == 0 {
|
| 335 | 334 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,56 @@ |
| 0 |
+package formatter |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "bytes" |
|
| 4 |
+ //"encoding/json" |
|
| 5 |
+ //"strings" |
|
| 6 |
+ "testing" |
|
| 7 |
+ //"time" |
|
| 8 |
+ |
|
| 9 |
+ //"github.com/docker/docker/api/types" |
|
| 10 |
+ //"github.com/docker/docker/pkg/stringid" |
|
| 11 |
+ "github.com/docker/docker/pkg/testutil/assert" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+func TestDiskUsageContextFormatWrite(t *testing.T) {
|
|
| 15 |
+ // Check default output format (verbose and non-verbose mode) for table headers |
|
| 16 |
+ cases := []struct {
|
|
| 17 |
+ context DiskUsageContext |
|
| 18 |
+ expected string |
|
| 19 |
+ }{
|
|
| 20 |
+ {
|
|
| 21 |
+ DiskUsageContext{Verbose: false},
|
|
| 22 |
+ `TYPE TOTAL ACTIVE SIZE RECLAIMABLE |
|
| 23 |
+Images 0 0 0B 0B |
|
| 24 |
+Containers 0 0 0B 0B |
|
| 25 |
+Local Volumes 0 0 0B 0B |
|
| 26 |
+`, |
|
| 27 |
+ }, |
|
| 28 |
+ {
|
|
| 29 |
+ DiskUsageContext{Verbose: true},
|
|
| 30 |
+ `Images space usage: |
|
| 31 |
+ |
|
| 32 |
+REPOSITORY TAG IMAGE ID CREATED ago SIZE SHARED SIZE UNIQUE SiZE CONTAINERS |
|
| 33 |
+ |
|
| 34 |
+Containers space usage: |
|
| 35 |
+ |
|
| 36 |
+CONTAINER ID IMAGE COMMAND LOCAL VOLUMES SIZE CREATED ago STATUS NAMES |
|
| 37 |
+ |
|
| 38 |
+Local Volumes space usage: |
|
| 39 |
+ |
|
| 40 |
+VOLUME NAME LINKS SIZE |
|
| 41 |
+`, |
|
| 42 |
+ }, |
|
| 43 |
+ } |
|
| 44 |
+ |
|
| 45 |
+ for _, testcase := range cases {
|
|
| 46 |
+ //networks := []types.NetworkResource{
|
|
| 47 |
+ // {ID: "networkID1", Name: "foobar_baz", Driver: "foo", Scope: "local", Created: timestamp1},
|
|
| 48 |
+ // {ID: "networkID2", Name: "foobar_bar", Driver: "bar", Scope: "local", Created: timestamp2},
|
|
| 49 |
+ //} |
|
| 50 |
+ out := bytes.NewBufferString("")
|
|
| 51 |
+ testcase.context.Output = out |
|
| 52 |
+ testcase.context.Write() |
|
| 53 |
+ assert.Equal(t, out.String(), testcase.expected) |
|
| 54 |
+ } |
|
| 55 |
+} |
| ... | ... |
@@ -44,7 +44,7 @@ type Context struct {
|
| 44 | 44 |
|
| 45 | 45 |
// internal element |
| 46 | 46 |
finalFormat string |
| 47 |
- header string |
|
| 47 |
+ header interface{}
|
|
| 48 | 48 |
buffer *bytes.Buffer |
| 49 | 49 |
} |
| 50 | 50 |
|
| ... | ... |
@@ -71,14 +71,10 @@ func (c *Context) parseFormat() (*template.Template, error) {
|
| 71 | 71 |
|
| 72 | 72 |
func (c *Context) postFormat(tmpl *template.Template, subContext subContext) {
|
| 73 | 73 |
if c.Format.IsTable() {
|
| 74 |
- if len(c.header) == 0 {
|
|
| 75 |
- // 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 |
|
| 76 |
- tmpl.Execute(bytes.NewBufferString(""), subContext)
|
|
| 77 |
- c.header = subContext.FullHeader() |
|
| 78 |
- } |
|
| 79 |
- |
|
| 80 | 74 |
t := tabwriter.NewWriter(c.Output, 20, 1, 3, ' ', 0) |
| 81 |
- t.Write([]byte(c.header)) |
|
| 75 |
+ buffer := bytes.NewBufferString("")
|
|
| 76 |
+ tmpl.Execute(buffer, subContext.FullHeader()) |
|
| 77 |
+ buffer.WriteTo(t) |
|
| 82 | 78 |
t.Write([]byte("\n"))
|
| 83 | 79 |
c.buffer.WriteTo(t) |
| 84 | 80 |
t.Flush() |
| ... | ... |
@@ -91,7 +87,7 @@ func (c *Context) contextFormat(tmpl *template.Template, subContext subContext) |
| 91 | 91 |
if err := tmpl.Execute(c.buffer, subContext); err != nil {
|
| 92 | 92 |
return fmt.Errorf("Template parsing error: %v\n", err)
|
| 93 | 93 |
} |
| 94 |
- if c.Format.IsTable() && len(c.header) == 0 {
|
|
| 94 |
+ if c.Format.IsTable() && c.header != nil {
|
|
| 95 | 95 |
c.header = subContext.FullHeader() |
| 96 | 96 |
} |
| 97 | 97 |
c.buffer.WriteString("\n")
|
| ... | ... |
@@ -11,8 +11,8 @@ import ( |
| 11 | 11 |
) |
| 12 | 12 |
|
| 13 | 13 |
const ( |
| 14 |
- defaultImageTableFormat = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.Size}}"
|
|
| 15 |
- defaultImageTableFormatWithDigest = "table {{.Repository}}\t{{.Tag}}\t{{.Digest}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.Size}}"
|
|
| 14 |
+ defaultImageTableFormat = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}}\t{{.Size}}"
|
|
| 15 |
+ defaultImageTableFormatWithDigest = "table {{.Repository}}\t{{.Tag}}\t{{.Digest}}\t{{.ID}}\t{{.CreatedSince}}\t{{.Size}}"
|
|
| 16 | 16 |
|
| 17 | 17 |
imageIDHeader = "IMAGE ID" |
| 18 | 18 |
repositoryHeader = "REPOSITORY" |
| ... | ... |
@@ -76,7 +76,21 @@ func ImageWrite(ctx ImageContext, images []types.ImageSummary) error {
|
| 76 | 76 |
render := func(format func(subContext subContext) error) error {
|
| 77 | 77 |
return imageFormat(ctx, images, format) |
| 78 | 78 |
} |
| 79 |
- return ctx.Write(&imageContext{}, render)
|
|
| 79 |
+ imageCtx := imageContext{}
|
|
| 80 |
+ imageCtx.header = map[string]string{
|
|
| 81 |
+ "ID": imageIDHeader, |
|
| 82 |
+ "Repository": repositoryHeader, |
|
| 83 |
+ "Tag": tagHeader, |
|
| 84 |
+ "Digest": digestHeader, |
|
| 85 |
+ "CreatedSince": createdSinceHeader, |
|
| 86 |
+ "CreatedAt": createdAtHeader, |
|
| 87 |
+ "Size": sizeHeader, |
|
| 88 |
+ "Containers": containersHeader, |
|
| 89 |
+ "VirtualSize": sizeHeader, |
|
| 90 |
+ "SharedSize": sharedSizeHeader, |
|
| 91 |
+ "UniqueSize": uniqueSizeHeader, |
|
| 92 |
+ } |
|
| 93 |
+ return ctx.Write(newImageContext(), render) |
|
| 80 | 94 |
} |
| 81 | 95 |
|
| 82 | 96 |
func imageFormat(ctx ImageContext, images []types.ImageSummary, format func(subContext subContext) error) error {
|
| ... | ... |
@@ -192,12 +206,29 @@ type imageContext struct {
|
| 192 | 192 |
digest string |
| 193 | 193 |
} |
| 194 | 194 |
|
| 195 |
+func newImageContext() *imageContext {
|
|
| 196 |
+ imageCtx := imageContext{}
|
|
| 197 |
+ imageCtx.header = map[string]string{
|
|
| 198 |
+ "ID": imageIDHeader, |
|
| 199 |
+ "Repository": repositoryHeader, |
|
| 200 |
+ "Tag": tagHeader, |
|
| 201 |
+ "Digest": digestHeader, |
|
| 202 |
+ "CreatedSince": createdSinceHeader, |
|
| 203 |
+ "CreatedAt": createdAtHeader, |
|
| 204 |
+ "Size": sizeHeader, |
|
| 205 |
+ "Containers": containersHeader, |
|
| 206 |
+ "VirtualSize": sizeHeader, |
|
| 207 |
+ "SharedSize": sharedSizeHeader, |
|
| 208 |
+ "UniqueSize": uniqueSizeHeader, |
|
| 209 |
+ } |
|
| 210 |
+ return &imageCtx |
|
| 211 |
+} |
|
| 212 |
+ |
|
| 195 | 213 |
func (c *imageContext) MarshalJSON() ([]byte, error) {
|
| 196 | 214 |
return marshalJSON(c) |
| 197 | 215 |
} |
| 198 | 216 |
|
| 199 | 217 |
func (c *imageContext) ID() string {
|
| 200 |
- c.AddHeader(imageIDHeader) |
|
| 201 | 218 |
if c.trunc {
|
| 202 | 219 |
return stringid.TruncateID(c.i.ID) |
| 203 | 220 |
} |
| ... | ... |
@@ -205,38 +236,31 @@ func (c *imageContext) ID() string {
|
| 205 | 205 |
} |
| 206 | 206 |
|
| 207 | 207 |
func (c *imageContext) Repository() string {
|
| 208 |
- c.AddHeader(repositoryHeader) |
|
| 209 | 208 |
return c.repo |
| 210 | 209 |
} |
| 211 | 210 |
|
| 212 | 211 |
func (c *imageContext) Tag() string {
|
| 213 |
- c.AddHeader(tagHeader) |
|
| 214 | 212 |
return c.tag |
| 215 | 213 |
} |
| 216 | 214 |
|
| 217 | 215 |
func (c *imageContext) Digest() string {
|
| 218 |
- c.AddHeader(digestHeader) |
|
| 219 | 216 |
return c.digest |
| 220 | 217 |
} |
| 221 | 218 |
|
| 222 | 219 |
func (c *imageContext) CreatedSince() string {
|
| 223 |
- c.AddHeader(createdSinceHeader) |
|
| 224 | 220 |
createdAt := time.Unix(int64(c.i.Created), 0) |
| 225 |
- return units.HumanDuration(time.Now().UTC().Sub(createdAt)) |
|
| 221 |
+ return units.HumanDuration(time.Now().UTC().Sub(createdAt)) + " ago" |
|
| 226 | 222 |
} |
| 227 | 223 |
|
| 228 | 224 |
func (c *imageContext) CreatedAt() string {
|
| 229 |
- c.AddHeader(createdAtHeader) |
|
| 230 | 225 |
return time.Unix(int64(c.i.Created), 0).String() |
| 231 | 226 |
} |
| 232 | 227 |
|
| 233 | 228 |
func (c *imageContext) Size() string {
|
| 234 |
- c.AddHeader(sizeHeader) |
|
| 235 | 229 |
return units.HumanSizeWithPrecision(float64(c.i.Size), 3) |
| 236 | 230 |
} |
| 237 | 231 |
|
| 238 | 232 |
func (c *imageContext) Containers() string {
|
| 239 |
- c.AddHeader(containersHeader) |
|
| 240 | 233 |
if c.i.Containers == -1 {
|
| 241 | 234 |
return "N/A" |
| 242 | 235 |
} |
| ... | ... |
@@ -244,12 +268,10 @@ func (c *imageContext) Containers() string {
|
| 244 | 244 |
} |
| 245 | 245 |
|
| 246 | 246 |
func (c *imageContext) VirtualSize() string {
|
| 247 |
- c.AddHeader(sizeHeader) |
|
| 248 | 247 |
return units.HumanSize(float64(c.i.VirtualSize)) |
| 249 | 248 |
} |
| 250 | 249 |
|
| 251 | 250 |
func (c *imageContext) SharedSize() string {
|
| 252 |
- c.AddHeader(sharedSizeHeader) |
|
| 253 | 251 |
if c.i.SharedSize == -1 {
|
| 254 | 252 |
return "N/A" |
| 255 | 253 |
} |
| ... | ... |
@@ -257,7 +279,6 @@ func (c *imageContext) SharedSize() string {
|
| 257 | 257 |
} |
| 258 | 258 |
|
| 259 | 259 |
func (c *imageContext) UniqueSize() string {
|
| 260 |
- c.AddHeader(uniqueSizeHeader) |
|
| 261 | 260 |
if c.i.VirtualSize == -1 || c.i.SharedSize == -1 {
|
| 262 | 261 |
return "N/A" |
| 263 | 262 |
} |
| ... | ... |
@@ -18,27 +18,26 @@ func TestImageContext(t *testing.T) {
|
| 18 | 18 |
|
| 19 | 19 |
var ctx imageContext |
| 20 | 20 |
cases := []struct {
|
| 21 |
- imageCtx imageContext |
|
| 22 |
- expValue string |
|
| 23 |
- expHeader string |
|
| 24 |
- call func() string |
|
| 21 |
+ imageCtx imageContext |
|
| 22 |
+ expValue string |
|
| 23 |
+ call func() string |
|
| 25 | 24 |
}{
|
| 26 | 25 |
{imageContext{
|
| 27 | 26 |
i: types.ImageSummary{ID: imageID},
|
| 28 | 27 |
trunc: true, |
| 29 |
- }, stringid.TruncateID(imageID), imageIDHeader, ctx.ID}, |
|
| 28 |
+ }, stringid.TruncateID(imageID), ctx.ID}, |
|
| 30 | 29 |
{imageContext{
|
| 31 | 30 |
i: types.ImageSummary{ID: imageID},
|
| 32 | 31 |
trunc: false, |
| 33 |
- }, imageID, imageIDHeader, ctx.ID}, |
|
| 32 |
+ }, imageID, ctx.ID}, |
|
| 34 | 33 |
{imageContext{
|
| 35 | 34 |
i: types.ImageSummary{Size: 10, VirtualSize: 10},
|
| 36 | 35 |
trunc: true, |
| 37 |
- }, "10B", sizeHeader, ctx.Size}, |
|
| 36 |
+ }, "10B", ctx.Size}, |
|
| 38 | 37 |
{imageContext{
|
| 39 | 38 |
i: types.ImageSummary{Created: unix},
|
| 40 | 39 |
trunc: true, |
| 41 |
- }, time.Unix(unix, 0).String(), createdAtHeader, ctx.CreatedAt}, |
|
| 40 |
+ }, time.Unix(unix, 0).String(), ctx.CreatedAt}, |
|
| 42 | 41 |
// FIXME |
| 43 | 42 |
// {imageContext{
|
| 44 | 43 |
// i: types.ImageSummary{Created: unix},
|
| ... | ... |
@@ -47,15 +46,15 @@ func TestImageContext(t *testing.T) {
|
| 47 | 47 |
{imageContext{
|
| 48 | 48 |
i: types.ImageSummary{},
|
| 49 | 49 |
repo: "busybox", |
| 50 |
- }, "busybox", repositoryHeader, ctx.Repository}, |
|
| 50 |
+ }, "busybox", ctx.Repository}, |
|
| 51 | 51 |
{imageContext{
|
| 52 | 52 |
i: types.ImageSummary{},
|
| 53 | 53 |
tag: "latest", |
| 54 |
- }, "latest", tagHeader, ctx.Tag}, |
|
| 54 |
+ }, "latest", ctx.Tag}, |
|
| 55 | 55 |
{imageContext{
|
| 56 | 56 |
i: types.ImageSummary{},
|
| 57 | 57 |
digest: "sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a", |
| 58 |
- }, "sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a", digestHeader, ctx.Digest}, |
|
| 58 |
+ }, "sha256:d149ab53f8718e987c3a3024bb8aa0e2caadf6c0328f1d9d850b2a2a67f2819a", ctx.Digest}, |
|
| 59 | 59 |
} |
| 60 | 60 |
|
| 61 | 61 |
for _, c := range cases {
|
| ... | ... |
@@ -66,11 +65,6 @@ func TestImageContext(t *testing.T) {
|
| 66 | 66 |
} else if v != c.expValue {
|
| 67 | 67 |
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
| 68 | 68 |
} |
| 69 |
- |
|
| 70 |
- h := ctx.FullHeader() |
|
| 71 |
- if h != c.expHeader {
|
|
| 72 |
- t.Fatalf("Expected %s, was %s\n", c.expHeader, h)
|
|
| 73 |
- } |
|
| 74 | 69 |
} |
| 75 | 70 |
} |
| 76 | 71 |
|
| ... | ... |
@@ -44,7 +44,28 @@ func NetworkWrite(ctx Context, networks []types.NetworkResource) error {
|
| 44 | 44 |
} |
| 45 | 45 |
return nil |
| 46 | 46 |
} |
| 47 |
- return ctx.Write(&networkContext{}, render)
|
|
| 47 |
+ networkCtx := networkContext{}
|
|
| 48 |
+ networkCtx.header = networkHeaderContext{
|
|
| 49 |
+ "ID": networkIDHeader, |
|
| 50 |
+ "Name": nameHeader, |
|
| 51 |
+ "Driver": driverHeader, |
|
| 52 |
+ "Scope": scopeHeader, |
|
| 53 |
+ "IPv6": ipv6Header, |
|
| 54 |
+ "Internal": internalHeader, |
|
| 55 |
+ "Labels": labelsHeader, |
|
| 56 |
+ "CreatedAt": createdAtHeader, |
|
| 57 |
+ } |
|
| 58 |
+ return ctx.Write(&networkCtx, render) |
|
| 59 |
+} |
|
| 60 |
+ |
|
| 61 |
+type networkHeaderContext map[string]string |
|
| 62 |
+ |
|
| 63 |
+func (c networkHeaderContext) Label(name string) string {
|
|
| 64 |
+ n := strings.Split(name, ".") |
|
| 65 |
+ r := strings.NewReplacer("-", " ", "_", " ")
|
|
| 66 |
+ h := r.Replace(n[len(n)-1]) |
|
| 67 |
+ |
|
| 68 |
+ return h |
|
| 48 | 69 |
} |
| 49 | 70 |
|
| 50 | 71 |
type networkContext struct {
|
| ... | ... |
@@ -58,7 +79,6 @@ func (c *networkContext) MarshalJSON() ([]byte, error) {
|
| 58 | 58 |
} |
| 59 | 59 |
|
| 60 | 60 |
func (c *networkContext) ID() string {
|
| 61 |
- c.AddHeader(networkIDHeader) |
|
| 62 | 61 |
if c.trunc {
|
| 63 | 62 |
return stringid.TruncateID(c.n.ID) |
| 64 | 63 |
} |
| ... | ... |
@@ -66,32 +86,26 @@ func (c *networkContext) ID() string {
|
| 66 | 66 |
} |
| 67 | 67 |
|
| 68 | 68 |
func (c *networkContext) Name() string {
|
| 69 |
- c.AddHeader(nameHeader) |
|
| 70 | 69 |
return c.n.Name |
| 71 | 70 |
} |
| 72 | 71 |
|
| 73 | 72 |
func (c *networkContext) Driver() string {
|
| 74 |
- c.AddHeader(driverHeader) |
|
| 75 | 73 |
return c.n.Driver |
| 76 | 74 |
} |
| 77 | 75 |
|
| 78 | 76 |
func (c *networkContext) Scope() string {
|
| 79 |
- c.AddHeader(scopeHeader) |
|
| 80 | 77 |
return c.n.Scope |
| 81 | 78 |
} |
| 82 | 79 |
|
| 83 | 80 |
func (c *networkContext) IPv6() string {
|
| 84 |
- c.AddHeader(ipv6Header) |
|
| 85 | 81 |
return fmt.Sprintf("%v", c.n.EnableIPv6)
|
| 86 | 82 |
} |
| 87 | 83 |
|
| 88 | 84 |
func (c *networkContext) Internal() string {
|
| 89 |
- c.AddHeader(internalHeader) |
|
| 90 | 85 |
return fmt.Sprintf("%v", c.n.Internal)
|
| 91 | 86 |
} |
| 92 | 87 |
|
| 93 | 88 |
func (c *networkContext) Labels() string {
|
| 94 |
- c.AddHeader(labelsHeader) |
|
| 95 | 89 |
if c.n.Labels == nil {
|
| 96 | 90 |
return "" |
| 97 | 91 |
} |
| ... | ... |
@@ -104,12 +118,6 @@ func (c *networkContext) Labels() string {
|
| 104 | 104 |
} |
| 105 | 105 |
|
| 106 | 106 |
func (c *networkContext) Label(name string) string {
|
| 107 |
- n := strings.Split(name, ".") |
|
| 108 |
- r := strings.NewReplacer("-", " ", "_", " ")
|
|
| 109 |
- h := r.Replace(n[len(n)-1]) |
|
| 110 |
- |
|
| 111 |
- c.AddHeader(h) |
|
| 112 |
- |
|
| 113 | 107 |
if c.n.Labels == nil {
|
| 114 | 108 |
return "" |
| 115 | 109 |
} |
| ... | ... |
@@ -117,6 +125,5 @@ func (c *networkContext) Label(name string) string {
|
| 117 | 117 |
} |
| 118 | 118 |
|
| 119 | 119 |
func (c *networkContext) CreatedAt() string {
|
| 120 |
- c.AddHeader(createdAtHeader) |
|
| 121 | 120 |
return c.n.Created.String() |
| 122 | 121 |
} |
| ... | ... |
@@ -19,41 +19,40 @@ func TestNetworkContext(t *testing.T) {
|
| 19 | 19 |
cases := []struct {
|
| 20 | 20 |
networkCtx networkContext |
| 21 | 21 |
expValue string |
| 22 |
- expHeader string |
|
| 23 | 22 |
call func() string |
| 24 | 23 |
}{
|
| 25 | 24 |
{networkContext{
|
| 26 | 25 |
n: types.NetworkResource{ID: networkID},
|
| 27 | 26 |
trunc: false, |
| 28 |
- }, networkID, networkIDHeader, ctx.ID}, |
|
| 27 |
+ }, networkID, ctx.ID}, |
|
| 29 | 28 |
{networkContext{
|
| 30 | 29 |
n: types.NetworkResource{ID: networkID},
|
| 31 | 30 |
trunc: true, |
| 32 |
- }, stringid.TruncateID(networkID), networkIDHeader, ctx.ID}, |
|
| 31 |
+ }, stringid.TruncateID(networkID), ctx.ID}, |
|
| 33 | 32 |
{networkContext{
|
| 34 | 33 |
n: types.NetworkResource{Name: "network_name"},
|
| 35 |
- }, "network_name", nameHeader, ctx.Name}, |
|
| 34 |
+ }, "network_name", ctx.Name}, |
|
| 36 | 35 |
{networkContext{
|
| 37 | 36 |
n: types.NetworkResource{Driver: "driver_name"},
|
| 38 |
- }, "driver_name", driverHeader, ctx.Driver}, |
|
| 37 |
+ }, "driver_name", ctx.Driver}, |
|
| 39 | 38 |
{networkContext{
|
| 40 | 39 |
n: types.NetworkResource{EnableIPv6: true},
|
| 41 |
- }, "true", ipv6Header, ctx.IPv6}, |
|
| 40 |
+ }, "true", ctx.IPv6}, |
|
| 42 | 41 |
{networkContext{
|
| 43 | 42 |
n: types.NetworkResource{EnableIPv6: false},
|
| 44 |
- }, "false", ipv6Header, ctx.IPv6}, |
|
| 43 |
+ }, "false", ctx.IPv6}, |
|
| 45 | 44 |
{networkContext{
|
| 46 | 45 |
n: types.NetworkResource{Internal: true},
|
| 47 |
- }, "true", internalHeader, ctx.Internal}, |
|
| 46 |
+ }, "true", ctx.Internal}, |
|
| 48 | 47 |
{networkContext{
|
| 49 | 48 |
n: types.NetworkResource{Internal: false},
|
| 50 |
- }, "false", internalHeader, ctx.Internal}, |
|
| 49 |
+ }, "false", ctx.Internal}, |
|
| 51 | 50 |
{networkContext{
|
| 52 | 51 |
n: types.NetworkResource{},
|
| 53 |
- }, "", labelsHeader, ctx.Labels}, |
|
| 52 |
+ }, "", ctx.Labels}, |
|
| 54 | 53 |
{networkContext{
|
| 55 | 54 |
n: types.NetworkResource{Labels: map[string]string{"label1": "value1", "label2": "value2"}},
|
| 56 |
- }, "label1=value1,label2=value2", labelsHeader, ctx.Labels}, |
|
| 55 |
+ }, "label1=value1,label2=value2", ctx.Labels}, |
|
| 57 | 56 |
} |
| 58 | 57 |
|
| 59 | 58 |
for _, c := range cases {
|
| ... | ... |
@@ -64,11 +63,6 @@ func TestNetworkContext(t *testing.T) {
|
| 64 | 64 |
} else if v != c.expValue {
|
| 65 | 65 |
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
| 66 | 66 |
} |
| 67 |
- |
|
| 68 |
- h := ctx.FullHeader() |
|
| 69 |
- if h != c.expHeader {
|
|
| 70 |
- t.Fatalf("Expected %s, was %s\n", c.expHeader, h)
|
|
| 71 |
- } |
|
| 72 | 67 |
} |
| 73 | 68 |
} |
| 74 | 69 |
|
| ... | ... |
@@ -44,7 +44,15 @@ func PluginWrite(ctx Context, plugins []*types.Plugin) error {
|
| 44 | 44 |
} |
| 45 | 45 |
return nil |
| 46 | 46 |
} |
| 47 |
- return ctx.Write(&pluginContext{}, render)
|
|
| 47 |
+ pluginCtx := pluginContext{}
|
|
| 48 |
+ pluginCtx.header = map[string]string{
|
|
| 49 |
+ "ID": pluginIDHeader, |
|
| 50 |
+ "Name": nameHeader, |
|
| 51 |
+ "Description": descriptionHeader, |
|
| 52 |
+ "Enabled": enabledHeader, |
|
| 53 |
+ "PluginReference": imageHeader, |
|
| 54 |
+ } |
|
| 55 |
+ return ctx.Write(&pluginCtx, render) |
|
| 48 | 56 |
} |
| 49 | 57 |
|
| 50 | 58 |
type pluginContext struct {
|
| ... | ... |
@@ -58,7 +66,6 @@ func (c *pluginContext) MarshalJSON() ([]byte, error) {
|
| 58 | 58 |
} |
| 59 | 59 |
|
| 60 | 60 |
func (c *pluginContext) ID() string {
|
| 61 |
- c.AddHeader(pluginIDHeader) |
|
| 62 | 61 |
if c.trunc {
|
| 63 | 62 |
return stringid.TruncateID(c.p.ID) |
| 64 | 63 |
} |
| ... | ... |
@@ -66,12 +73,10 @@ func (c *pluginContext) ID() string {
|
| 66 | 66 |
} |
| 67 | 67 |
|
| 68 | 68 |
func (c *pluginContext) Name() string {
|
| 69 |
- c.AddHeader(nameHeader) |
|
| 70 | 69 |
return c.p.Name |
| 71 | 70 |
} |
| 72 | 71 |
|
| 73 | 72 |
func (c *pluginContext) Description() string {
|
| 74 |
- c.AddHeader(descriptionHeader) |
|
| 75 | 73 |
desc := strings.Replace(c.p.Config.Description, "\n", "", -1) |
| 76 | 74 |
desc = strings.Replace(desc, "\r", "", -1) |
| 77 | 75 |
if c.trunc {
|
| ... | ... |
@@ -82,11 +87,9 @@ func (c *pluginContext) Description() string {
|
| 82 | 82 |
} |
| 83 | 83 |
|
| 84 | 84 |
func (c *pluginContext) Enabled() bool {
|
| 85 |
- c.AddHeader(enabledHeader) |
|
| 86 | 85 |
return c.p.Enabled |
| 87 | 86 |
} |
| 88 | 87 |
|
| 89 | 88 |
func (c *pluginContext) PluginReference() string {
|
| 90 |
- c.AddHeader(imageHeader) |
|
| 91 | 89 |
return c.p.PluginReference |
| 92 | 90 |
} |
| ... | ... |
@@ -18,23 +18,22 @@ func TestPluginContext(t *testing.T) {
|
| 18 | 18 |
cases := []struct {
|
| 19 | 19 |
pluginCtx pluginContext |
| 20 | 20 |
expValue string |
| 21 |
- expHeader string |
|
| 22 | 21 |
call func() string |
| 23 | 22 |
}{
|
| 24 | 23 |
{pluginContext{
|
| 25 | 24 |
p: types.Plugin{ID: pluginID},
|
| 26 | 25 |
trunc: false, |
| 27 |
- }, pluginID, pluginIDHeader, ctx.ID}, |
|
| 26 |
+ }, pluginID, ctx.ID}, |
|
| 28 | 27 |
{pluginContext{
|
| 29 | 28 |
p: types.Plugin{ID: pluginID},
|
| 30 | 29 |
trunc: true, |
| 31 |
- }, stringid.TruncateID(pluginID), pluginIDHeader, ctx.ID}, |
|
| 30 |
+ }, stringid.TruncateID(pluginID), ctx.ID}, |
|
| 32 | 31 |
{pluginContext{
|
| 33 | 32 |
p: types.Plugin{Name: "plugin_name"},
|
| 34 |
- }, "plugin_name", nameHeader, ctx.Name}, |
|
| 33 |
+ }, "plugin_name", ctx.Name}, |
|
| 35 | 34 |
{pluginContext{
|
| 36 | 35 |
p: types.Plugin{Config: types.PluginConfig{Description: "plugin_description"}},
|
| 37 |
- }, "plugin_description", descriptionHeader, ctx.Description}, |
|
| 36 |
+ }, "plugin_description", ctx.Description}, |
|
| 38 | 37 |
} |
| 39 | 38 |
|
| 40 | 39 |
for _, c := range cases {
|
| ... | ... |
@@ -45,11 +44,6 @@ func TestPluginContext(t *testing.T) {
|
| 45 | 45 |
} else if v != c.expValue {
|
| 46 | 46 |
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
| 47 | 47 |
} |
| 48 |
- |
|
| 49 |
- h := ctx.FullHeader() |
|
| 50 |
- if h != c.expHeader {
|
|
| 51 |
- t.Fatalf("Expected %s, was %s\n", c.expHeader, h)
|
|
| 52 |
- } |
|
| 53 | 48 |
} |
| 54 | 49 |
} |
| 55 | 50 |
|
| ... | ... |
@@ -372,7 +372,15 @@ func ServiceListWrite(ctx Context, services []swarm.Service, info map[string]Ser |
| 372 | 372 |
} |
| 373 | 373 |
return nil |
| 374 | 374 |
} |
| 375 |
- return ctx.Write(&serviceContext{}, render)
|
|
| 375 |
+ serviceCtx := serviceContext{}
|
|
| 376 |
+ serviceCtx.header = map[string]string{
|
|
| 377 |
+ "ID": serviceIDHeader, |
|
| 378 |
+ "Name": nameHeader, |
|
| 379 |
+ "Mode": modeHeader, |
|
| 380 |
+ "Replicas": replicasHeader, |
|
| 381 |
+ "Image": imageHeader, |
|
| 382 |
+ } |
|
| 383 |
+ return ctx.Write(&serviceCtx, render) |
|
| 376 | 384 |
} |
| 377 | 385 |
|
| 378 | 386 |
type serviceContext struct {
|
| ... | ... |
@@ -387,27 +395,22 @@ func (c *serviceContext) MarshalJSON() ([]byte, error) {
|
| 387 | 387 |
} |
| 388 | 388 |
|
| 389 | 389 |
func (c *serviceContext) ID() string {
|
| 390 |
- c.AddHeader(serviceIDHeader) |
|
| 391 | 390 |
return stringid.TruncateID(c.service.ID) |
| 392 | 391 |
} |
| 393 | 392 |
|
| 394 | 393 |
func (c *serviceContext) Name() string {
|
| 395 |
- c.AddHeader(nameHeader) |
|
| 396 | 394 |
return c.service.Spec.Name |
| 397 | 395 |
} |
| 398 | 396 |
|
| 399 | 397 |
func (c *serviceContext) Mode() string {
|
| 400 |
- c.AddHeader(modeHeader) |
|
| 401 | 398 |
return c.mode |
| 402 | 399 |
} |
| 403 | 400 |
|
| 404 | 401 |
func (c *serviceContext) Replicas() string {
|
| 405 |
- c.AddHeader(replicasHeader) |
|
| 406 | 402 |
return c.replicas |
| 407 | 403 |
} |
| 408 | 404 |
|
| 409 | 405 |
func (c *serviceContext) Image() string {
|
| 410 |
- c.AddHeader(imageHeader) |
|
| 411 | 406 |
image := c.service.Spec.TaskTemplate.ContainerSpec.Image |
| 412 | 407 |
if ref, err := reference.ParseNormalizedNamed(image); err == nil {
|
| 413 | 408 |
// update image string for display, (strips any digest) |
| ... | ... |
@@ -129,7 +129,24 @@ func ContainerStatsWrite(ctx Context, containerStats []StatsEntry, osType string |
| 129 | 129 |
} |
| 130 | 130 |
return nil |
| 131 | 131 |
} |
| 132 |
- return ctx.Write(&containerStatsContext{os: osType}, render)
|
|
| 132 |
+ memUsage := memUseHeader |
|
| 133 |
+ if osType == winOSType {
|
|
| 134 |
+ memUsage = winMemUseHeader |
|
| 135 |
+ } |
|
| 136 |
+ containerStatsCtx := containerStatsContext{}
|
|
| 137 |
+ containerStatsCtx.header = map[string]string{
|
|
| 138 |
+ "Container": containerHeader, |
|
| 139 |
+ "Name": nameHeader, |
|
| 140 |
+ "ID": containerIDHeader, |
|
| 141 |
+ "CPUPerc": cpuPercHeader, |
|
| 142 |
+ "MemUsage": memUsage, |
|
| 143 |
+ "MemPerc": memPercHeader, |
|
| 144 |
+ "NetIO": netIOHeader, |
|
| 145 |
+ "BlockIO": blockIOHeader, |
|
| 146 |
+ "PIDs": pidsHeader, |
|
| 147 |
+ } |
|
| 148 |
+ containerStatsCtx.os = osType |
|
| 149 |
+ return ctx.Write(&containerStatsCtx, render) |
|
| 133 | 150 |
} |
| 134 | 151 |
|
| 135 | 152 |
type containerStatsContext struct {
|
| ... | ... |
@@ -143,12 +160,10 @@ func (c *containerStatsContext) MarshalJSON() ([]byte, error) {
|
| 143 | 143 |
} |
| 144 | 144 |
|
| 145 | 145 |
func (c *containerStatsContext) Container() string {
|
| 146 |
- c.AddHeader(containerHeader) |
|
| 147 | 146 |
return c.s.Container |
| 148 | 147 |
} |
| 149 | 148 |
|
| 150 | 149 |
func (c *containerStatsContext) Name() string {
|
| 151 |
- c.AddHeader(nameHeader) |
|
| 152 | 150 |
if len(c.s.Name) > 1 {
|
| 153 | 151 |
return c.s.Name[1:] |
| 154 | 152 |
} |
| ... | ... |
@@ -156,12 +171,10 @@ func (c *containerStatsContext) Name() string {
|
| 156 | 156 |
} |
| 157 | 157 |
|
| 158 | 158 |
func (c *containerStatsContext) ID() string {
|
| 159 |
- c.AddHeader(containerIDHeader) |
|
| 160 | 159 |
return c.s.ID |
| 161 | 160 |
} |
| 162 | 161 |
|
| 163 | 162 |
func (c *containerStatsContext) CPUPerc() string {
|
| 164 |
- c.AddHeader(cpuPercHeader) |
|
| 165 | 163 |
if c.s.IsInvalid {
|
| 166 | 164 |
return fmt.Sprintf("--")
|
| 167 | 165 |
} |
| ... | ... |
@@ -169,11 +182,6 @@ func (c *containerStatsContext) CPUPerc() string {
|
| 169 | 169 |
} |
| 170 | 170 |
|
| 171 | 171 |
func (c *containerStatsContext) MemUsage() string {
|
| 172 |
- header := memUseHeader |
|
| 173 |
- if c.os == winOSType {
|
|
| 174 |
- header = winMemUseHeader |
|
| 175 |
- } |
|
| 176 |
- c.AddHeader(header) |
|
| 177 | 172 |
if c.s.IsInvalid {
|
| 178 | 173 |
return fmt.Sprintf("-- / --")
|
| 179 | 174 |
} |
| ... | ... |
@@ -184,8 +192,6 @@ func (c *containerStatsContext) MemUsage() string {
|
| 184 | 184 |
} |
| 185 | 185 |
|
| 186 | 186 |
func (c *containerStatsContext) MemPerc() string {
|
| 187 |
- header := memPercHeader |
|
| 188 |
- c.AddHeader(header) |
|
| 189 | 187 |
if c.s.IsInvalid || c.os == winOSType {
|
| 190 | 188 |
return fmt.Sprintf("--")
|
| 191 | 189 |
} |
| ... | ... |
@@ -193,7 +199,6 @@ func (c *containerStatsContext) MemPerc() string {
|
| 193 | 193 |
} |
| 194 | 194 |
|
| 195 | 195 |
func (c *containerStatsContext) NetIO() string {
|
| 196 |
- c.AddHeader(netIOHeader) |
|
| 197 | 196 |
if c.s.IsInvalid {
|
| 198 | 197 |
return fmt.Sprintf("--")
|
| 199 | 198 |
} |
| ... | ... |
@@ -201,7 +206,6 @@ func (c *containerStatsContext) NetIO() string {
|
| 201 | 201 |
} |
| 202 | 202 |
|
| 203 | 203 |
func (c *containerStatsContext) BlockIO() string {
|
| 204 |
- c.AddHeader(blockIOHeader) |
|
| 205 | 204 |
if c.s.IsInvalid {
|
| 206 | 205 |
return fmt.Sprintf("--")
|
| 207 | 206 |
} |
| ... | ... |
@@ -209,7 +213,6 @@ func (c *containerStatsContext) BlockIO() string {
|
| 209 | 209 |
} |
| 210 | 210 |
|
| 211 | 211 |
func (c *containerStatsContext) PIDs() string {
|
| 212 |
- c.AddHeader(pidsHeader) |
|
| 213 | 212 |
if c.s.IsInvalid || c.os == winOSType {
|
| 214 | 213 |
return fmt.Sprintf("--")
|
| 215 | 214 |
} |
| ... | ... |
@@ -42,11 +42,6 @@ func TestContainerStatsContext(t *testing.T) {
|
| 42 | 42 |
if v := te.call(); v != te.expValue {
|
| 43 | 43 |
t.Fatalf("Expected %q, got %q", te.expValue, v)
|
| 44 | 44 |
} |
| 45 |
- |
|
| 46 |
- h := ctx.FullHeader() |
|
| 47 |
- if h != te.expHeader {
|
|
| 48 |
- t.Fatalf("Expected %q, got %q", te.expHeader, h)
|
|
| 49 |
- } |
|
| 50 | 45 |
} |
| 51 | 46 |
} |
| 52 | 47 |
|
| ... | ... |
@@ -45,7 +45,17 @@ func VolumeWrite(ctx Context, volumes []*types.Volume) error {
|
| 45 | 45 |
} |
| 46 | 46 |
return nil |
| 47 | 47 |
} |
| 48 |
- return ctx.Write(&volumeContext{}, render)
|
|
| 48 |
+ return ctx.Write(newVolumeContext(), render) |
|
| 49 |
+} |
|
| 50 |
+ |
|
| 51 |
+type volumeHeaderContext map[string]string |
|
| 52 |
+ |
|
| 53 |
+func (c volumeHeaderContext) Label(name string) string {
|
|
| 54 |
+ n := strings.Split(name, ".") |
|
| 55 |
+ r := strings.NewReplacer("-", " ", "_", " ")
|
|
| 56 |
+ h := r.Replace(n[len(n)-1]) |
|
| 57 |
+ |
|
| 58 |
+ return h |
|
| 49 | 59 |
} |
| 50 | 60 |
|
| 51 | 61 |
type volumeContext struct {
|
| ... | ... |
@@ -53,32 +63,41 @@ type volumeContext struct {
|
| 53 | 53 |
v types.Volume |
| 54 | 54 |
} |
| 55 | 55 |
|
| 56 |
+func newVolumeContext() *volumeContext {
|
|
| 57 |
+ volumeCtx := volumeContext{}
|
|
| 58 |
+ volumeCtx.header = volumeHeaderContext{
|
|
| 59 |
+ "Name": volumeNameHeader, |
|
| 60 |
+ "Driver": driverHeader, |
|
| 61 |
+ "Scope": scopeHeader, |
|
| 62 |
+ "Mountpoint": mountpointHeader, |
|
| 63 |
+ "Labels": labelsHeader, |
|
| 64 |
+ "Links": linksHeader, |
|
| 65 |
+ "Size": sizeHeader, |
|
| 66 |
+ } |
|
| 67 |
+ return &volumeCtx |
|
| 68 |
+} |
|
| 69 |
+ |
|
| 56 | 70 |
func (c *volumeContext) MarshalJSON() ([]byte, error) {
|
| 57 | 71 |
return marshalJSON(c) |
| 58 | 72 |
} |
| 59 | 73 |
|
| 60 | 74 |
func (c *volumeContext) Name() string {
|
| 61 |
- c.AddHeader(volumeNameHeader) |
|
| 62 | 75 |
return c.v.Name |
| 63 | 76 |
} |
| 64 | 77 |
|
| 65 | 78 |
func (c *volumeContext) Driver() string {
|
| 66 |
- c.AddHeader(driverHeader) |
|
| 67 | 79 |
return c.v.Driver |
| 68 | 80 |
} |
| 69 | 81 |
|
| 70 | 82 |
func (c *volumeContext) Scope() string {
|
| 71 |
- c.AddHeader(scopeHeader) |
|
| 72 | 83 |
return c.v.Scope |
| 73 | 84 |
} |
| 74 | 85 |
|
| 75 | 86 |
func (c *volumeContext) Mountpoint() string {
|
| 76 |
- c.AddHeader(mountpointHeader) |
|
| 77 | 87 |
return c.v.Mountpoint |
| 78 | 88 |
} |
| 79 | 89 |
|
| 80 | 90 |
func (c *volumeContext) Labels() string {
|
| 81 |
- c.AddHeader(labelsHeader) |
|
| 82 | 91 |
if c.v.Labels == nil {
|
| 83 | 92 |
return "" |
| 84 | 93 |
} |
| ... | ... |
@@ -91,13 +110,6 @@ func (c *volumeContext) Labels() string {
|
| 91 | 91 |
} |
| 92 | 92 |
|
| 93 | 93 |
func (c *volumeContext) Label(name string) string {
|
| 94 |
- |
|
| 95 |
- n := strings.Split(name, ".") |
|
| 96 |
- r := strings.NewReplacer("-", " ", "_", " ")
|
|
| 97 |
- h := r.Replace(n[len(n)-1]) |
|
| 98 |
- |
|
| 99 |
- c.AddHeader(h) |
|
| 100 |
- |
|
| 101 | 94 |
if c.v.Labels == nil {
|
| 102 | 95 |
return "" |
| 103 | 96 |
} |
| ... | ... |
@@ -105,7 +117,6 @@ func (c *volumeContext) Label(name string) string {
|
| 105 | 105 |
} |
| 106 | 106 |
|
| 107 | 107 |
func (c *volumeContext) Links() string {
|
| 108 |
- c.AddHeader(linksHeader) |
|
| 109 | 108 |
if c.v.UsageData == nil {
|
| 110 | 109 |
return "N/A" |
| 111 | 110 |
} |
| ... | ... |
@@ -113,7 +124,6 @@ func (c *volumeContext) Links() string {
|
| 113 | 113 |
} |
| 114 | 114 |
|
| 115 | 115 |
func (c *volumeContext) Size() string {
|
| 116 |
- c.AddHeader(sizeHeader) |
|
| 117 | 116 |
if c.v.UsageData == nil {
|
| 118 | 117 |
return "N/A" |
| 119 | 118 |
} |
| ... | ... |
@@ -18,27 +18,26 @@ func TestVolumeContext(t *testing.T) {
|
| 18 | 18 |
cases := []struct {
|
| 19 | 19 |
volumeCtx volumeContext |
| 20 | 20 |
expValue string |
| 21 |
- expHeader string |
|
| 22 | 21 |
call func() string |
| 23 | 22 |
}{
|
| 24 | 23 |
{volumeContext{
|
| 25 | 24 |
v: types.Volume{Name: volumeName},
|
| 26 |
- }, volumeName, volumeNameHeader, ctx.Name}, |
|
| 25 |
+ }, volumeName, ctx.Name}, |
|
| 27 | 26 |
{volumeContext{
|
| 28 | 27 |
v: types.Volume{Driver: "driver_name"},
|
| 29 |
- }, "driver_name", driverHeader, ctx.Driver}, |
|
| 28 |
+ }, "driver_name", ctx.Driver}, |
|
| 30 | 29 |
{volumeContext{
|
| 31 | 30 |
v: types.Volume{Scope: "local"},
|
| 32 |
- }, "local", scopeHeader, ctx.Scope}, |
|
| 31 |
+ }, "local", ctx.Scope}, |
|
| 33 | 32 |
{volumeContext{
|
| 34 | 33 |
v: types.Volume{Mountpoint: "mountpoint"},
|
| 35 |
- }, "mountpoint", mountpointHeader, ctx.Mountpoint}, |
|
| 34 |
+ }, "mountpoint", ctx.Mountpoint}, |
|
| 36 | 35 |
{volumeContext{
|
| 37 | 36 |
v: types.Volume{},
|
| 38 |
- }, "", labelsHeader, ctx.Labels}, |
|
| 37 |
+ }, "", ctx.Labels}, |
|
| 39 | 38 |
{volumeContext{
|
| 40 | 39 |
v: types.Volume{Labels: map[string]string{"label1": "value1", "label2": "value2"}},
|
| 41 |
- }, "label1=value1,label2=value2", labelsHeader, ctx.Labels}, |
|
| 40 |
+ }, "label1=value1,label2=value2", ctx.Labels}, |
|
| 42 | 41 |
} |
| 43 | 42 |
|
| 44 | 43 |
for _, c := range cases {
|
| ... | ... |
@@ -49,11 +48,6 @@ func TestVolumeContext(t *testing.T) {
|
| 49 | 49 |
} else if v != c.expValue {
|
| 50 | 50 |
t.Fatalf("Expected %s, was %s\n", c.expValue, v)
|
| 51 | 51 |
} |
| 52 |
- |
|
| 53 |
- h := ctx.FullHeader() |
|
| 54 |
- if h != c.expHeader {
|
|
| 55 |
- t.Fatalf("Expected %s, was %s\n", c.expHeader, h)
|
|
| 56 |
- } |
|
| 57 | 52 |
} |
| 58 | 53 |
} |
| 59 | 54 |
|