github.com/kobeld/docker@v1.12.0-rc1/api/client/formatter/formatter.go (about)

     1  package formatter
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"strings"
     8  	"text/tabwriter"
     9  	"text/template"
    10  
    11  	"github.com/docker/docker/reference"
    12  	"github.com/docker/docker/utils/templates"
    13  	"github.com/docker/engine-api/types"
    14  )
    15  
    16  const (
    17  	tableFormatKey = "table"
    18  	rawFormatKey   = "raw"
    19  
    20  	defaultContainerTableFormat       = "table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.RunningFor}} ago\t{{.Status}}\t{{.Ports}}\t{{.Names}}"
    21  	defaultImageTableFormat           = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.Size}}"
    22  	defaultImageTableFormatWithDigest = "table {{.Repository}}\t{{.Tag}}\t{{.Digest}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.Size}}"
    23  	defaultQuietFormat                = "{{.ID}}"
    24  )
    25  
    26  // Context contains information required by the formatter to print the output as desired.
    27  type Context struct {
    28  	// Output is the output stream to which the formatted string is written.
    29  	Output io.Writer
    30  	// Format is used to choose raw, table or custom format for the output.
    31  	Format string
    32  	// Quiet when set to true will simply print minimal information.
    33  	Quiet bool
    34  	// Trunc when set to true will truncate the output of certain fields such as Container ID.
    35  	Trunc bool
    36  
    37  	// internal element
    38  	table       bool
    39  	finalFormat string
    40  	header      string
    41  	buffer      *bytes.Buffer
    42  }
    43  
    44  func (c *Context) preformat() {
    45  	c.finalFormat = c.Format
    46  
    47  	if strings.HasPrefix(c.Format, tableKey) {
    48  		c.table = true
    49  		c.finalFormat = c.finalFormat[len(tableKey):]
    50  	}
    51  
    52  	c.finalFormat = strings.Trim(c.finalFormat, " ")
    53  	r := strings.NewReplacer(`\t`, "\t", `\n`, "\n")
    54  	c.finalFormat = r.Replace(c.finalFormat)
    55  }
    56  
    57  func (c *Context) parseFormat() (*template.Template, error) {
    58  	tmpl, err := templates.Parse(c.finalFormat)
    59  	if err != nil {
    60  		c.buffer.WriteString(fmt.Sprintf("Template parsing error: %v\n", err))
    61  		c.buffer.WriteTo(c.Output)
    62  	}
    63  	return tmpl, err
    64  }
    65  
    66  func (c *Context) postformat(tmpl *template.Template, subContext subContext) {
    67  	if c.table {
    68  		if len(c.header) == 0 {
    69  			// 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
    70  			tmpl.Execute(bytes.NewBufferString(""), subContext)
    71  			c.header = subContext.fullHeader()
    72  		}
    73  
    74  		t := tabwriter.NewWriter(c.Output, 20, 1, 3, ' ', 0)
    75  		t.Write([]byte(c.header))
    76  		t.Write([]byte("\n"))
    77  		c.buffer.WriteTo(t)
    78  		t.Flush()
    79  	} else {
    80  		c.buffer.WriteTo(c.Output)
    81  	}
    82  }
    83  
    84  func (c *Context) contextFormat(tmpl *template.Template, subContext subContext) error {
    85  	if err := tmpl.Execute(c.buffer, subContext); err != nil {
    86  		c.buffer = bytes.NewBufferString(fmt.Sprintf("Template parsing error: %v\n", err))
    87  		c.buffer.WriteTo(c.Output)
    88  		return err
    89  	}
    90  	if c.table && len(c.header) == 0 {
    91  		c.header = subContext.fullHeader()
    92  	}
    93  	c.buffer.WriteString("\n")
    94  	return nil
    95  }
    96  
    97  // ContainerContext contains container specific information required by the formater, encapsulate a Context struct.
    98  type ContainerContext struct {
    99  	Context
   100  	// Size when set to true will display the size of the output.
   101  	Size bool
   102  	// Containers
   103  	Containers []types.Container
   104  }
   105  
   106  // ImageContext contains image specific information required by the formater, encapsulate a Context struct.
   107  type ImageContext struct {
   108  	Context
   109  	Digest bool
   110  	// Images
   111  	Images []types.Image
   112  }
   113  
   114  func (ctx ContainerContext) Write() {
   115  	switch ctx.Format {
   116  	case tableFormatKey:
   117  		if ctx.Quiet {
   118  			ctx.Format = defaultQuietFormat
   119  		} else {
   120  			ctx.Format = defaultContainerTableFormat
   121  			if ctx.Size {
   122  				ctx.Format += `\t{{.Size}}`
   123  			}
   124  		}
   125  	case rawFormatKey:
   126  		if ctx.Quiet {
   127  			ctx.Format = `container_id: {{.ID}}`
   128  		} else {
   129  			ctx.Format = `container_id: {{.ID}}\nimage: {{.Image}}\ncommand: {{.Command}}\ncreated_at: {{.CreatedAt}}\nstatus: {{.Status}}\nnames: {{.Names}}\nlabels: {{.Labels}}\nports: {{.Ports}}\n`
   130  			if ctx.Size {
   131  				ctx.Format += `size: {{.Size}}\n`
   132  			}
   133  		}
   134  	}
   135  
   136  	ctx.buffer = bytes.NewBufferString("")
   137  	ctx.preformat()
   138  
   139  	tmpl, err := ctx.parseFormat()
   140  	if err != nil {
   141  		return
   142  	}
   143  
   144  	for _, container := range ctx.Containers {
   145  		containerCtx := &containerContext{
   146  			trunc: ctx.Trunc,
   147  			c:     container,
   148  		}
   149  		err = ctx.contextFormat(tmpl, containerCtx)
   150  		if err != nil {
   151  			return
   152  		}
   153  	}
   154  
   155  	ctx.postformat(tmpl, &containerContext{})
   156  }
   157  
   158  func (ctx ImageContext) Write() {
   159  	switch ctx.Format {
   160  	case tableFormatKey:
   161  		ctx.Format = defaultImageTableFormat
   162  		if ctx.Digest {
   163  			ctx.Format = defaultImageTableFormatWithDigest
   164  		}
   165  		if ctx.Quiet {
   166  			ctx.Format = defaultQuietFormat
   167  		}
   168  	case rawFormatKey:
   169  		if ctx.Quiet {
   170  			ctx.Format = `image_id: {{.ID}}`
   171  		} else {
   172  			if ctx.Digest {
   173  				ctx.Format = `repository: {{ .Repository }}
   174  tag: {{.Tag}}
   175  digest: {{.Digest}}
   176  image_id: {{.ID}}
   177  created_at: {{.CreatedAt}}
   178  virtual_size: {{.Size}}
   179  `
   180  			} else {
   181  				ctx.Format = `repository: {{ .Repository }}
   182  tag: {{.Tag}}
   183  image_id: {{.ID}}
   184  created_at: {{.CreatedAt}}
   185  virtual_size: {{.Size}}
   186  `
   187  			}
   188  		}
   189  	}
   190  
   191  	ctx.buffer = bytes.NewBufferString("")
   192  	ctx.preformat()
   193  	if ctx.table && ctx.Digest && !strings.Contains(ctx.Format, "{{.Digest}}") {
   194  		ctx.finalFormat += "\t{{.Digest}}"
   195  	}
   196  
   197  	tmpl, err := ctx.parseFormat()
   198  	if err != nil {
   199  		return
   200  	}
   201  
   202  	for _, image := range ctx.Images {
   203  
   204  		repoTags := image.RepoTags
   205  		repoDigests := image.RepoDigests
   206  
   207  		if len(repoTags) == 1 && repoTags[0] == "<none>:<none>" && len(repoDigests) == 1 && repoDigests[0] == "<none>@<none>" {
   208  			// dangling image - clear out either repoTags or repoDigests so we only show it once below
   209  			repoDigests = []string{}
   210  		}
   211  		// combine the tags and digests lists
   212  		tagsAndDigests := append(repoTags, repoDigests...)
   213  		for _, repoAndRef := range tagsAndDigests {
   214  			repo := "<none>"
   215  			tag := "<none>"
   216  			digest := "<none>"
   217  
   218  			if !strings.HasPrefix(repoAndRef, "<none>") {
   219  				ref, err := reference.ParseNamed(repoAndRef)
   220  				if err != nil {
   221  					continue
   222  				}
   223  				repo = ref.Name()
   224  
   225  				switch x := ref.(type) {
   226  				case reference.Canonical:
   227  					digest = x.Digest().String()
   228  				case reference.NamedTagged:
   229  					tag = x.Tag()
   230  				}
   231  			}
   232  			imageCtx := &imageContext{
   233  				trunc:  ctx.Trunc,
   234  				i:      image,
   235  				repo:   repo,
   236  				tag:    tag,
   237  				digest: digest,
   238  			}
   239  			err = ctx.contextFormat(tmpl, imageCtx)
   240  			if err != nil {
   241  				return
   242  			}
   243  		}
   244  	}
   245  
   246  	ctx.postformat(tmpl, &imageContext{})
   247  }