github.com/brahmaroutu/docker@v1.2.1-0.20160809185609-eb28dde01f16/api/client/formatter/image.go (about)

     1  package formatter
     2  
     3  import (
     4  	"bytes"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/docker/docker/pkg/stringid"
     9  	"github.com/docker/docker/reference"
    10  	"github.com/docker/engine-api/types"
    11  	"github.com/docker/go-units"
    12  )
    13  
    14  const (
    15  	defaultImageTableFormat           = "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.Size}}"
    16  	defaultImageTableFormatWithDigest = "table {{.Repository}}\t{{.Tag}}\t{{.Digest}}\t{{.ID}}\t{{.CreatedSince}} ago\t{{.Size}}"
    17  
    18  	imageIDHeader    = "IMAGE ID"
    19  	repositoryHeader = "REPOSITORY"
    20  	tagHeader        = "TAG"
    21  	digestHeader     = "DIGEST"
    22  )
    23  
    24  // ImageContext contains image specific information required by the formater, encapsulate a Context struct.
    25  type ImageContext struct {
    26  	Context
    27  	Digest bool
    28  	// Images
    29  	Images []types.Image
    30  }
    31  
    32  func isDangling(image types.Image) bool {
    33  	return len(image.RepoTags) == 1 && image.RepoTags[0] == "<none>:<none>" && len(image.RepoDigests) == 1 && image.RepoDigests[0] == "<none>@<none>"
    34  }
    35  
    36  func (ctx ImageContext) Write() {
    37  	switch ctx.Format {
    38  	case tableFormatKey:
    39  		ctx.Format = defaultImageTableFormat
    40  		if ctx.Digest {
    41  			ctx.Format = defaultImageTableFormatWithDigest
    42  		}
    43  		if ctx.Quiet {
    44  			ctx.Format = defaultQuietFormat
    45  		}
    46  	case rawFormatKey:
    47  		if ctx.Quiet {
    48  			ctx.Format = `image_id: {{.ID}}`
    49  		} else {
    50  			if ctx.Digest {
    51  				ctx.Format = `repository: {{ .Repository }}
    52  tag: {{.Tag}}
    53  digest: {{.Digest}}
    54  image_id: {{.ID}}
    55  created_at: {{.CreatedAt}}
    56  virtual_size: {{.Size}}
    57  `
    58  			} else {
    59  				ctx.Format = `repository: {{ .Repository }}
    60  tag: {{.Tag}}
    61  image_id: {{.ID}}
    62  created_at: {{.CreatedAt}}
    63  virtual_size: {{.Size}}
    64  `
    65  			}
    66  		}
    67  	}
    68  
    69  	ctx.buffer = bytes.NewBufferString("")
    70  	ctx.preformat()
    71  	if ctx.table && ctx.Digest && !strings.Contains(ctx.Format, "{{.Digest}}") {
    72  		ctx.finalFormat += "\t{{.Digest}}"
    73  	}
    74  
    75  	tmpl, err := ctx.parseFormat()
    76  	if err != nil {
    77  		return
    78  	}
    79  
    80  	for _, image := range ctx.Images {
    81  		images := []*imageContext{}
    82  		if isDangling(image) {
    83  			images = append(images, &imageContext{
    84  				trunc:  ctx.Trunc,
    85  				i:      image,
    86  				repo:   "<none>",
    87  				tag:    "<none>",
    88  				digest: "<none>",
    89  			})
    90  		} else {
    91  			repoTags := map[string][]string{}
    92  			repoDigests := map[string][]string{}
    93  
    94  			for _, refString := range append(image.RepoTags) {
    95  				ref, err := reference.ParseNamed(refString)
    96  				if err != nil {
    97  					continue
    98  				}
    99  				if nt, ok := ref.(reference.NamedTagged); ok {
   100  					repoTags[ref.Name()] = append(repoTags[ref.Name()], nt.Tag())
   101  				}
   102  			}
   103  			for _, refString := range append(image.RepoDigests) {
   104  				ref, err := reference.ParseNamed(refString)
   105  				if err != nil {
   106  					continue
   107  				}
   108  				if c, ok := ref.(reference.Canonical); ok {
   109  					repoDigests[ref.Name()] = append(repoDigests[ref.Name()], c.Digest().String())
   110  				}
   111  			}
   112  
   113  			for repo, tags := range repoTags {
   114  				digests := repoDigests[repo]
   115  
   116  				// Do not display digests as their own row
   117  				delete(repoDigests, repo)
   118  
   119  				if !ctx.Digest {
   120  					// Ignore digest references, just show tag once
   121  					digests = nil
   122  				}
   123  
   124  				for _, tag := range tags {
   125  					if len(digests) == 0 {
   126  						images = append(images, &imageContext{
   127  							trunc:  ctx.Trunc,
   128  							i:      image,
   129  							repo:   repo,
   130  							tag:    tag,
   131  							digest: "<none>",
   132  						})
   133  						continue
   134  					}
   135  					// Display the digests for each tag
   136  					for _, dgst := range digests {
   137  						images = append(images, &imageContext{
   138  							trunc:  ctx.Trunc,
   139  							i:      image,
   140  							repo:   repo,
   141  							tag:    tag,
   142  							digest: dgst,
   143  						})
   144  					}
   145  
   146  				}
   147  			}
   148  
   149  			// Show rows for remaining digest only references
   150  			for repo, digests := range repoDigests {
   151  				// If digests are displayed, show row per digest
   152  				if ctx.Digest {
   153  					for _, dgst := range digests {
   154  						images = append(images, &imageContext{
   155  							trunc:  ctx.Trunc,
   156  							i:      image,
   157  							repo:   repo,
   158  							tag:    "<none>",
   159  							digest: dgst,
   160  						})
   161  					}
   162  				} else {
   163  					images = append(images, &imageContext{
   164  						trunc: ctx.Trunc,
   165  						i:     image,
   166  						repo:  repo,
   167  						tag:   "<none>",
   168  					})
   169  				}
   170  			}
   171  		}
   172  		for _, imageCtx := range images {
   173  			err = ctx.contextFormat(tmpl, imageCtx)
   174  			if err != nil {
   175  				return
   176  			}
   177  		}
   178  	}
   179  
   180  	ctx.postformat(tmpl, &imageContext{})
   181  }
   182  
   183  type imageContext struct {
   184  	baseSubContext
   185  	trunc  bool
   186  	i      types.Image
   187  	repo   string
   188  	tag    string
   189  	digest string
   190  }
   191  
   192  func (c *imageContext) ID() string {
   193  	c.addHeader(imageIDHeader)
   194  	if c.trunc {
   195  		return stringid.TruncateID(c.i.ID)
   196  	}
   197  	return c.i.ID
   198  }
   199  
   200  func (c *imageContext) Repository() string {
   201  	c.addHeader(repositoryHeader)
   202  	return c.repo
   203  }
   204  
   205  func (c *imageContext) Tag() string {
   206  	c.addHeader(tagHeader)
   207  	return c.tag
   208  }
   209  
   210  func (c *imageContext) Digest() string {
   211  	c.addHeader(digestHeader)
   212  	return c.digest
   213  }
   214  
   215  func (c *imageContext) CreatedSince() string {
   216  	c.addHeader(createdSinceHeader)
   217  	createdAt := time.Unix(int64(c.i.Created), 0)
   218  	return units.HumanDuration(time.Now().UTC().Sub(createdAt))
   219  }
   220  
   221  func (c *imageContext) CreatedAt() string {
   222  	c.addHeader(createdAtHeader)
   223  	return time.Unix(int64(c.i.Created), 0).String()
   224  }
   225  
   226  func (c *imageContext) Size() string {
   227  	c.addHeader(sizeHeader)
   228  	return units.HumanSize(float64(c.i.Size))
   229  }