diff options
Diffstat (limited to 'cmd/podman/images.go')
-rw-r--r-- | cmd/podman/images.go | 405 |
1 files changed, 0 insertions, 405 deletions
diff --git a/cmd/podman/images.go b/cmd/podman/images.go deleted file mode 100644 index ed33402ab..000000000 --- a/cmd/podman/images.go +++ /dev/null @@ -1,405 +0,0 @@ -package main - -import ( - "context" - "fmt" - "reflect" - "sort" - "strings" - "time" - "unicode" - - "github.com/containers/buildah/pkg/formats" - "github.com/containers/libpod/cmd/podman/cliconfig" - "github.com/containers/libpod/libpod/image" - "github.com/containers/libpod/pkg/adapter" - units "github.com/docker/go-units" - digest "github.com/opencontainers/go-digest" - "github.com/pkg/errors" - "github.com/sirupsen/logrus" - "github.com/spf13/cobra" -) - -type imagesTemplateParams struct { - Repository string - Tag string - ID string - Digest digest.Digest - Digests []digest.Digest - CreatedAt time.Time - CreatedSince string - Size string - ReadOnly bool - History string -} - -type imagesJSONParams struct { - ID string `json:"ID"` - Name []string `json:"Names"` - Created string `json:"Created"` - Digest digest.Digest `json:"Digest"` - Digests []digest.Digest `json:"Digests"` - CreatedAt time.Time `json:"CreatedAt"` - Size *uint64 `json:"Size"` - ReadOnly bool `json:"ReadOnly"` - History []string `json:"History"` -} - -type imagesOptions struct { - quiet bool - noHeading bool - noTrunc bool - digests bool - format string - outputformat string - sort string - all bool - history bool -} - -// Type declaration and functions for sorting the images output -type imagesSorted []imagesTemplateParams - -func (a imagesSorted) Len() int { return len(a) } -func (a imagesSorted) Swap(i, j int) { a[i], a[j] = a[j], a[i] } - -type imagesSortedCreated struct{ imagesSorted } - -func (a imagesSortedCreated) Less(i, j int) bool { - return a.imagesSorted[i].CreatedAt.After(a.imagesSorted[j].CreatedAt) -} - -type imagesSortedID struct{ imagesSorted } - -func (a imagesSortedID) Less(i, j int) bool { return a.imagesSorted[i].ID < a.imagesSorted[j].ID } - -type imagesSortedTag struct{ imagesSorted } - -func (a imagesSortedTag) Less(i, j int) bool { return a.imagesSorted[i].Tag < a.imagesSorted[j].Tag } - -type imagesSortedRepository struct{ imagesSorted } - -func (a imagesSortedRepository) Less(i, j int) bool { - return a.imagesSorted[i].Repository < a.imagesSorted[j].Repository -} - -type imagesSortedSize struct{ imagesSorted } - -func (a imagesSortedSize) Less(i, j int) bool { - size1, _ := units.FromHumanSize(a.imagesSorted[i].Size) - size2, _ := units.FromHumanSize(a.imagesSorted[j].Size) - return size1 < size2 -} - -var ( - imagesCommand cliconfig.ImagesValues - imagesDescription = "Lists images previously pulled to the system or created on the system." - - _imagesCommand = cobra.Command{ - Use: "images [flags] [IMAGE]", - Short: "List images in local storage", - Long: imagesDescription, - RunE: func(cmd *cobra.Command, args []string) error { - imagesCommand.InputArgs = args - imagesCommand.GlobalFlags = MainGlobalOpts - imagesCommand.Remote = remoteclient - return imagesCmd(&imagesCommand) - }, - Example: `podman images --format json - podman images --sort repository --format "table {{.ID}} {{.Repository}} {{.Tag}}" - podman images --filter dangling=true`, - } -) - -func imagesInit(command *cliconfig.ImagesValues) { - command.SetHelpTemplate(HelpTemplate()) - command.SetUsageTemplate(UsageTemplate()) - - flags := command.Flags() - flags.BoolVarP(&command.All, "all", "a", false, "Show all images (default hides intermediate images)") - flags.BoolVar(&command.Digests, "digests", false, "Show digests") - flags.StringSliceVarP(&command.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])") - flags.StringVar(&command.Format, "format", "", "Change the output format to JSON or a Go template") - flags.BoolVarP(&command.Noheading, "noheading", "n", false, "Do not print column headings") - // TODO Need to learn how to deal with second name being a string instead of a char. - // This needs to be "no-trunc, notruncate" - flags.BoolVar(&command.NoTrunc, "no-trunc", false, "Do not truncate output") - flags.BoolVarP(&command.Quiet, "quiet", "q", false, "Display only image IDs") - flags.StringVar(&command.Sort, "sort", "created", "Sort by created, id, repository, size, or tag") - flags.BoolVarP(&command.History, "history", "", false, "Display the image name history") - -} - -func init() { - imagesCommand.Command = &_imagesCommand - imagesInit(&imagesCommand) -} - -func imagesCmd(c *cliconfig.ImagesValues) error { - var ( - image string - ) - - ctx := getContext() - runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand) - if err != nil { - return errors.Wrapf(err, "Could not get runtime") - } - defer runtime.DeferredShutdown(false) - if len(c.InputArgs) == 1 { - image = c.InputArgs[0] - } - if len(c.InputArgs) > 1 { - return errors.New("'podman images' requires at most 1 argument") - } - if len(c.Filter) > 0 && image != "" { - return errors.New("can not specify an image and a filter") - } - filters := c.Filter - if len(filters) < 1 && len(image) > 0 { - filters = append(filters, fmt.Sprintf("reference=%s", image)) - } - - var sortValues = map[string]bool{ - "created": true, - "id": true, - "repository": true, - "size": true, - "tag": true, - } - if !sortValues[c.Sort] { - keys := make([]string, 0, len(sortValues)) - for k := range sortValues { - keys = append(keys, k) - } - return errors.Errorf("invalid sort value %q, required values: %s", c.Sort, strings.Join(keys, ", ")) - } - - opts := imagesOptions{ - quiet: c.Quiet, - noHeading: c.Noheading, - noTrunc: c.NoTrunc, - digests: c.Digests, - format: c.Format, - sort: c.Sort, - all: c.All, - history: c.History, - } - - outputformat := opts.setOutputFormat() - // These fields were renamed, so we need to provide backward compat for - // the old names. - if strings.Contains(outputformat, "{{.Created}}") { - outputformat = strings.Replace(outputformat, "{{.Created}}", "{{.CreatedSince}}", -1) - } - if strings.Contains(outputformat, "{{.CreatedTime}}") { - outputformat = strings.Replace(outputformat, "{{.CreatedTime}}", "{{.CreatedAt}}", -1) - } - opts.outputformat = outputformat - - filteredImages, err := runtime.GetFilteredImages(filters, false) - if err != nil { - return errors.Wrapf(err, "unable to get images") - } - - for _, image := range filteredImages { - if image.IsReadOnly() { - opts.outputformat += "{{.ReadOnly}}\t" - break - } - } - return generateImagesOutput(ctx, filteredImages, opts) -} - -func (i imagesOptions) setOutputFormat() string { - if i.format != "" { - // "\t" from the command line is not being recognized as a tab - // replacing the string "\t" to a tab character if the user passes in "\t" - return strings.Replace(i.format, `\t`, "\t", -1) - } - if i.quiet { - return formats.IDString - } - format := "table {{.Repository}}\t{{if .Tag}}{{.Tag}}{{else}}<none>{{end}}\t" - if i.noHeading { - format = "{{.Repository}}\t{{if .Tag}}{{.Tag}}{{else}}<none>{{end}}\t" - } - if i.digests { - format += "{{.Digest}}\t" - } - format += "{{.ID}}\t{{.CreatedSince}}\t{{.Size}}\t" - if i.history { - format += "{{if .History}}{{.History}}{{else}}<none>{{end}}\t" - } - return format -} - -// imagesToGeneric creates an empty array of interfaces for output -func imagesToGeneric(templParams []imagesTemplateParams, jsonParams []imagesJSONParams) []interface{} { - genericParams := []interface{}{} - if len(templParams) > 0 { - for _, v := range templParams { - genericParams = append(genericParams, interface{}(v)) - } - return genericParams - } - for _, v := range jsonParams { - genericParams = append(genericParams, interface{}(v)) - } - return genericParams -} - -func sortImagesOutput(sortBy string, imagesOutput imagesSorted) imagesSorted { - switch sortBy { - case "id": - sort.Sort(imagesSortedID{imagesOutput}) - case "size": - sort.Sort(imagesSortedSize{imagesOutput}) - case "tag": - sort.Sort(imagesSortedTag{imagesOutput}) - case "repository": - sort.Sort(imagesSortedRepository{imagesOutput}) - default: - // default is created time - sort.Sort(imagesSortedCreated{imagesOutput}) - } - return imagesOutput -} - -// getImagesTemplateOutput returns the images information to be printed in human readable format -func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerImage, opts imagesOptions) imagesSorted { - var imagesOutput imagesSorted - for _, img := range images { - // If all is false and the image doesn't have a name, check to see if the top layer of the image is a parent - // to another image's top layer. If it is, then it is an intermediate image so don't print out if the --all flag - // is not set. - isParent, err := img.IsParent(ctx) - if err != nil { - logrus.Errorf("error checking if image is a parent %q: %v", img.ID(), err) - } - if !opts.all && len(img.Names()) == 0 && isParent { - continue - } - createdTime := img.Created() - - imageID := "sha256:" + img.ID() - if !opts.noTrunc { - imageID = shortID(img.ID()) - } - - // get all specified repo:tag and repo@digest pairs and print them separately - repopairs, err := image.ReposToMap(img.Names()) - if err != nil { - logrus.Errorf("error finding tag/digest for %s", img.ID()) - } - outer: - for repo, tags := range repopairs { - for _, tag := range tags { - size, err := img.Size(ctx) - var sizeStr string - if err != nil { - sizeStr = err.Error() - } else { - sizeStr = units.HumanSizeWithPrecision(float64(*size), 3) - lastNumIdx := strings.LastIndexFunc(sizeStr, unicode.IsNumber) - sizeStr = sizeStr[:lastNumIdx+1] + " " + sizeStr[lastNumIdx+1:] - } - var imageDigest digest.Digest - if len(tag) == 71 && strings.HasPrefix(tag, "sha256:") { - imageDigest = digest.Digest(tag) - tag = "" - } else if img.Digest() != "" { - imageDigest = img.Digest() - } - params := imagesTemplateParams{ - Repository: repo, - Tag: tag, - ID: imageID, - Digest: imageDigest, - Digests: img.Digests(), - CreatedAt: createdTime, - CreatedSince: units.HumanDuration(time.Since(createdTime)) + " ago", - Size: sizeStr, - ReadOnly: img.IsReadOnly(), - History: strings.Join(img.NamesHistory(), ", "), - } - imagesOutput = append(imagesOutput, params) - if opts.quiet { // Show only one image ID when quiet - break outer - } - } - } - } - - // Sort images by created time - sortImagesOutput(opts.sort, imagesOutput) - return imagesOutput -} - -// getImagesJSONOutput returns the images information in its raw form -func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage) []imagesJSONParams { - imagesOutput := []imagesJSONParams{} - for _, img := range images { - size, err := img.Size(ctx) - if err != nil { - size = nil - } - params := imagesJSONParams{ - ID: img.ID(), - Name: img.Names(), - Digest: img.Digest(), - Digests: img.Digests(), - Created: units.HumanDuration(time.Since(img.Created())) + " ago", - CreatedAt: img.Created(), - Size: size, - ReadOnly: img.IsReadOnly(), - History: img.NamesHistory(), - } - imagesOutput = append(imagesOutput, params) - } - return imagesOutput -} - -// generateImagesOutput generates the images based on the format provided - -func generateImagesOutput(ctx context.Context, images []*adapter.ContainerImage, opts imagesOptions) error { - templateMap := GenImageOutputMap() - var out formats.Writer - - switch opts.format { - case formats.JSONString: - imagesOutput := getImagesJSONOutput(ctx, images) - out = formats.JSONStructArray{Output: imagesToGeneric([]imagesTemplateParams{}, imagesOutput)} - default: - imagesOutput := getImagesTemplateOutput(ctx, images, opts) - out = formats.StdoutTemplateArray{Output: imagesToGeneric(imagesOutput, []imagesJSONParams{}), Template: opts.outputformat, Fields: templateMap} - } - return out.Out() -} - -// GenImageOutputMap generates the map used for outputting the images header -// without requiring a populated image. This replaces the previous HeaderMap -// call. -func GenImageOutputMap() map[string]string { - io := imagesTemplateParams{} - v := reflect.Indirect(reflect.ValueOf(io)) - values := make(map[string]string) - - for i := 0; i < v.NumField(); i++ { - key := v.Type().Field(i).Name - value := key - if value == "ID" { - value = "Image" + value - } - - if value == "ReadOnly" { - values[key] = "R/O" - continue - } - if value == "CreatedSince" { - value = "created" - } - values[key] = strings.ToUpper(splitCamelCase(value)) - } - return values -} |