github.com/rsampaio/docker@v0.7.2-0.20150827203920-fdc73cc3fc31/api/client/ps/custom.go (about) 1 package ps 2 3 import ( 4 "bytes" 5 "fmt" 6 "strconv" 7 "strings" 8 "text/tabwriter" 9 "text/template" 10 "time" 11 12 "github.com/docker/docker/api" 13 "github.com/docker/docker/api/types" 14 "github.com/docker/docker/pkg/stringid" 15 "github.com/docker/docker/pkg/stringutils" 16 "github.com/docker/docker/pkg/units" 17 ) 18 19 const ( 20 tableKey = "table" 21 22 idHeader = "CONTAINER ID" 23 imageHeader = "IMAGE" 24 namesHeader = "NAMES" 25 commandHeader = "COMMAND" 26 createdAtHeader = "CREATED AT" 27 runningForHeader = "CREATED" 28 statusHeader = "STATUS" 29 portsHeader = "PORTS" 30 sizeHeader = "SIZE" 31 labelsHeader = "LABELS" 32 ) 33 34 type containerContext struct { 35 trunc bool 36 header []string 37 c types.Container 38 } 39 40 func (c *containerContext) ID() string { 41 c.addHeader(idHeader) 42 if c.trunc { 43 return stringid.TruncateID(c.c.ID) 44 } 45 return c.c.ID 46 } 47 48 func (c *containerContext) Names() string { 49 c.addHeader(namesHeader) 50 names := stripNamePrefix(c.c.Names) 51 if c.trunc { 52 for _, name := range names { 53 if len(strings.Split(name, "/")) == 1 { 54 names = []string{name} 55 break 56 } 57 } 58 } 59 return strings.Join(names, ",") 60 } 61 62 func (c *containerContext) Image() string { 63 c.addHeader(imageHeader) 64 if c.c.Image == "" { 65 return "<no image>" 66 } 67 if c.trunc { 68 return stringutils.Truncate(c.c.Image, 12) 69 } 70 return c.c.Image 71 } 72 73 func (c *containerContext) Command() string { 74 c.addHeader(commandHeader) 75 command := c.c.Command 76 if c.trunc { 77 command = stringutils.Truncate(command, 20) 78 } 79 return strconv.Quote(command) 80 } 81 82 func (c *containerContext) CreatedAt() string { 83 c.addHeader(createdAtHeader) 84 return time.Unix(int64(c.c.Created), 0).String() 85 } 86 87 func (c *containerContext) RunningFor() string { 88 c.addHeader(runningForHeader) 89 createdAt := time.Unix(int64(c.c.Created), 0) 90 return units.HumanDuration(time.Now().UTC().Sub(createdAt)) 91 } 92 93 func (c *containerContext) Ports() string { 94 c.addHeader(portsHeader) 95 return api.DisplayablePorts(c.c.Ports) 96 } 97 98 func (c *containerContext) Status() string { 99 c.addHeader(statusHeader) 100 return c.c.Status 101 } 102 103 func (c *containerContext) Size() string { 104 c.addHeader(sizeHeader) 105 srw := units.HumanSize(float64(c.c.SizeRw)) 106 sv := units.HumanSize(float64(c.c.SizeRootFs)) 107 108 sf := srw 109 if c.c.SizeRootFs > 0 { 110 sf = fmt.Sprintf("%s (virtual %s)", srw, sv) 111 } 112 return sf 113 } 114 115 func (c *containerContext) Labels() string { 116 c.addHeader(labelsHeader) 117 if c.c.Labels == nil { 118 return "" 119 } 120 121 var joinLabels []string 122 for k, v := range c.c.Labels { 123 joinLabels = append(joinLabels, fmt.Sprintf("%s=%s", k, v)) 124 } 125 return strings.Join(joinLabels, ",") 126 } 127 128 func (c *containerContext) Label(name string) string { 129 n := strings.Split(name, ".") 130 r := strings.NewReplacer("-", " ", "_", " ") 131 h := r.Replace(n[len(n)-1]) 132 133 c.addHeader(h) 134 135 if c.c.Labels == nil { 136 return "" 137 } 138 return c.c.Labels[name] 139 } 140 141 func (c *containerContext) fullHeader() string { 142 if c.header == nil { 143 return "" 144 } 145 return strings.Join(c.header, "\t") 146 } 147 148 func (c *containerContext) addHeader(header string) { 149 if c.header == nil { 150 c.header = []string{} 151 } 152 c.header = append(c.header, strings.ToUpper(header)) 153 } 154 155 func customFormat(ctx Context, containers []types.Container) { 156 var ( 157 table bool 158 header string 159 format = ctx.Format 160 buffer = bytes.NewBufferString("") 161 ) 162 163 if strings.HasPrefix(ctx.Format, tableKey) { 164 table = true 165 format = format[len(tableKey):] 166 } 167 168 format = strings.Trim(format, " ") 169 r := strings.NewReplacer(`\t`, "\t", `\n`, "\n") 170 format = r.Replace(format) 171 172 if table && ctx.Size { 173 format += "\t{{.Size}}" 174 } 175 176 tmpl, err := template.New("").Parse(format) 177 if err != nil { 178 buffer.WriteString(fmt.Sprintf("Template parsing error: %v\n", err)) 179 buffer.WriteTo(ctx.Output) 180 return 181 } 182 183 for _, container := range containers { 184 containerCtx := &containerContext{ 185 trunc: ctx.Trunc, 186 c: container, 187 } 188 if err := tmpl.Execute(buffer, containerCtx); err != nil { 189 buffer = bytes.NewBufferString(fmt.Sprintf("Template parsing error: %v\n", err)) 190 buffer.WriteTo(ctx.Output) 191 return 192 } 193 if table && len(header) == 0 { 194 header = containerCtx.fullHeader() 195 } 196 buffer.WriteString("\n") 197 } 198 199 if table { 200 if len(header) == 0 { 201 // 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 202 containerCtx := &containerContext{} 203 tmpl.Execute(bytes.NewBufferString(""), containerCtx) 204 header = containerCtx.fullHeader() 205 } 206 207 t := tabwriter.NewWriter(ctx.Output, 20, 1, 3, ' ', 0) 208 t.Write([]byte(header)) 209 t.Write([]byte("\n")) 210 buffer.WriteTo(t) 211 t.Flush() 212 } else { 213 buffer.WriteTo(ctx.Output) 214 } 215 } 216 217 func stripNamePrefix(ss []string) []string { 218 for i, s := range ss { 219 ss[i] = s[1:] 220 } 221 222 return ss 223 }