From 1d7cb7cc48d06631e2bdfd0e25eeccc8e87b042f Mon Sep 17 00:00:00 2001 From: Jhon Honce Date: Mon, 23 Mar 2020 09:04:31 -0700 Subject: V2 podman images/image list * Updated entities to support flags/options * Updated bindings caused by entities changes * Removed handlers.ImageSummary in favor of entities.ImageSummary * Introduced StringSet() container object to simply error checking Signed-off-by: Jhon Honce --- cmd/podmanV2/images/images.go | 21 +--- cmd/podmanV2/images/list.go | 213 +++++++++++++++++++++++++++++++++++- cmd/podmanV2/registry/registry.go | 8 ++ cmd/podmanV2/report/templates.go | 29 +++-- pkg/api/handlers/compat/images.go | 3 +- pkg/api/handlers/libpod/images.go | 5 +- pkg/api/handlers/types.go | 70 ++++++------ pkg/api/server/swagger.go | 3 +- pkg/bindings/images/images.go | 5 +- pkg/domain/entities/engine_image.go | 2 +- pkg/domain/entities/images.go | 61 ++++------- pkg/domain/entities/set.go | 45 ++++++++ pkg/domain/infra/abi/images.go | 29 ----- pkg/domain/infra/abi/images_list.go | 80 ++++++++++++++ pkg/domain/infra/tunnel/images.go | 11 +- 15 files changed, 443 insertions(+), 142 deletions(-) create mode 100644 pkg/domain/entities/set.go create mode 100644 pkg/domain/infra/abi/images_list.go diff --git a/cmd/podmanV2/images/images.go b/cmd/podmanV2/images/images.go index a1e56396a..719846b4c 100644 --- a/cmd/podmanV2/images/images.go +++ b/cmd/podmanV2/images/images.go @@ -9,17 +9,16 @@ import ( ) var ( - // podman _images_ + // podman _images_ Alias for podman image _list_ imagesCmd = &cobra.Command{ Use: strings.Replace(listCmd.Use, "list", "images", 1), + Args: listCmd.Args, Short: listCmd.Short, Long: listCmd.Long, PersistentPreRunE: preRunE, - RunE: images, + RunE: listCmd.RunE, Example: strings.Replace(listCmd.Example, "podman image list", "podman images", -1), } - - imagesOpts = entities.ImageListOptions{} ) func init() { @@ -30,17 +29,5 @@ func init() { imagesCmd.SetHelpTemplate(registry.HelpTemplate()) imagesCmd.SetUsageTemplate(registry.UsageTemplate()) - flags := imagesCmd.Flags() - flags.BoolVarP(&imagesOpts.All, "all", "a", false, "Show all images (default hides intermediate images)") - flags.BoolVar(&imagesOpts.Digests, "digests", false, "Show digests") - flags.StringSliceVarP(&imagesOpts.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])") - flags.StringVar(&imagesOpts.Format, "format", "", "Change the output format to JSON or a Go template") - flags.BoolVarP(&imagesOpts.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(&imagesOpts.NoTrunc, "no-trunc", false, "Do not truncate output") - flags.BoolVar(&imagesOpts.NoTrunc, "notruncate", false, "Do not truncate output") - flags.BoolVarP(&imagesOpts.Quiet, "quiet", "q", false, "Display only image IDs") - flags.StringVar(&imagesOpts.Sort, "sort", "created", "Sort by created, id, repository, size, or tag") - flags.BoolVarP(&imagesOpts.History, "history", "", false, "Display the image name history") + imageListFlagSet(imagesCmd.Flags()) } diff --git a/cmd/podmanV2/images/list.go b/cmd/podmanV2/images/list.go index 0441f8fd8..4714af3e4 100644 --- a/cmd/podmanV2/images/list.go +++ b/cmd/podmanV2/images/list.go @@ -1,16 +1,40 @@ package images import ( + "errors" + "fmt" + "os" + "sort" + "strings" + "text/tabwriter" + "text/template" + "time" + "github.com/containers/libpod/cmd/podmanV2/registry" + "github.com/containers/libpod/cmd/podmanV2/report" "github.com/containers/libpod/pkg/domain/entities" + jsoniter "github.com/json-iterator/go" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) +type listFlagType struct { + format string + history bool + noHeading bool + noTrunc bool + quiet bool + sort string + readOnly bool + digests bool +} + var ( // Command: podman image _list_ listCmd = &cobra.Command{ Use: "list [flag] [IMAGE]", Aliases: []string{"ls"}, + Args: cobra.MaximumNArgs(1), Short: "List images in local storage", Long: "Lists images previously pulled to the system or created on the system.", RunE: images, @@ -18,6 +42,19 @@ var ( podman image list --sort repository --format "table {{.ID}} {{.Repository}} {{.Tag}}" podman image list --filter dangling=true`, } + + // Options to pull data + listOptions = entities.ImageListOptions{} + + // Options for presenting data + listFlag = listFlagType{} + + sortFields = entities.NewStringSet( + "created", + "id", + "repository", + "size", + "tag") ) func init() { @@ -26,9 +63,181 @@ func init() { Command: listCmd, Parent: imageCmd, }) + imageListFlagSet(listCmd.Flags()) +} + +func imageListFlagSet(flags *pflag.FlagSet) { + flags.BoolVarP(&listOptions.All, "all", "a", false, "Show all images (default hides intermediate images)") + flags.StringSliceVarP(&listOptions.Filter, "filter", "f", []string{}, "Filter output based on conditions provided (default [])") + flags.StringVar(&listFlag.format, "format", "", "Change the output format to JSON or a Go template") + flags.BoolVar(&listFlag.digests, "digests", false, "Show digests") + flags.BoolVarP(&listFlag.noHeading, "noheading", "n", false, "Do not print column headings") + flags.BoolVar(&listFlag.noTrunc, "no-trunc", false, "Do not truncate output") + flags.BoolVar(&listFlag.noTrunc, "notruncate", false, "Do not truncate output") + flags.BoolVarP(&listFlag.quiet, "quiet", "q", false, "Display only image IDs") + flags.StringVar(&listFlag.sort, "sort", "created", "Sort by "+sortFields.String()) + flags.BoolVarP(&listFlag.history, "history", "", false, "Display the image name history") } func images(cmd *cobra.Command, args []string) error { - _, _ = registry.Options(cmd) - return nil + if len(listOptions.Filter) > 0 && len(args) > 0 { + return errors.New("cannot specify an image and a filter(s)") + } + + if len(listOptions.Filter) < 1 && len(args) > 0 { + listOptions.Filter = append(listOptions.Filter, "reference="+args[0]) + } + + if cmd.Flag("sort").Changed && !sortFields.Contains(listFlag.sort) { + return fmt.Errorf("\"%s\" is not a valid field for sorting. Choose from: %s", + listFlag.sort, sortFields.String()) + } + + summaries, err := registry.ImageEngine().List(registry.GetContext(), listOptions) + if err != nil { + return err + } + + imageS := summaries + sort.Slice(imageS, sortFunc(listFlag.sort, imageS)) + + if cmd.Flag("format").Changed && listFlag.format == "json" { + return writeJSON(imageS) + } else { + return writeTemplate(imageS, err) + } +} + +func writeJSON(imageS []*entities.ImageSummary) error { + type image struct { + entities.ImageSummary + Created string + } + + imgs := make([]image, 0, len(imageS)) + for _, e := range imageS { + var h image + h.ImageSummary = *e + h.Created = time.Unix(e.Created, 0).Format(time.RFC3339) + h.RepoTags = nil + + imgs = append(imgs, h) + } + + json := jsoniter.ConfigCompatibleWithStandardLibrary + enc := json.NewEncoder(os.Stdout) + return enc.Encode(imgs) +} + +func writeTemplate(imageS []*entities.ImageSummary, err error) error { + type image struct { + entities.ImageSummary + Repository string `json:"repository,omitempty"` + Tag string `json:"tag,omitempty"` + } + + imgs := make([]image, 0, len(imageS)) + for _, e := range imageS { + for _, tag := range e.RepoTags { + var h image + h.ImageSummary = *e + h.Repository, h.Tag = tokenRepoTag(tag) + imgs = append(imgs, h) + } + if e.IsReadOnly() { + listFlag.readOnly = true + } + } + + hdr, row := imageListFormat(listFlag) + format := hdr + "{{range . }}" + row + "{{end}}" + + tmpl := template.Must(template.New("report").Funcs(report.PodmanTemplateFuncs()).Parse(format)) + w := tabwriter.NewWriter(os.Stdout, 8, 2, 2, ' ', 0) + defer w.Flush() + return tmpl.Execute(w, imgs) +} + +func tokenRepoTag(tag string) (string, string) { + tokens := strings.SplitN(tag, ":", 2) + switch len(tokens) { + case 0: + return tag, "" + case 1: + return tokens[0], "" + case 2: + return tokens[0], tokens[1] + default: + return "", "" + } +} + +func sortFunc(key string, data []*entities.ImageSummary) func(i, j int) bool { + switch key { + case "id": + return func(i, j int) bool { + return data[i].ID < data[j].ID + } + case "repository": + return func(i, j int) bool { + return data[i].RepoTags[0] < data[j].RepoTags[0] + } + case "size": + return func(i, j int) bool { + return data[i].Size < data[j].Size + } + case "tag": + return func(i, j int) bool { + return data[i].RepoTags[0] < data[j].RepoTags[0] + } + default: + // case "created": + return func(i, j int) bool { + return data[i].Created >= data[j].Created + } + } +} + +func imageListFormat(flags listFlagType) (string, string) { + if flags.quiet { + return "", "{{slice .ID 0 12}}\n" + } + + // Defaults + hdr := "REPOSITORY\tTAG" + row := "{{.Repository}}\t{{if .Tag}}{{.Tag}}{{else}}{{end}}" + + if flags.digests { + hdr += "\tDIGEST" + row += "\t{{.Digest}}" + } + + hdr += "\tID" + if flags.noTrunc { + row += "\tsha256:{{.ID}}" + } else { + row += "\t{{slice .ID 0 12}}" + } + + hdr += "\tCREATED\tSIZE" + row += "\t{{humanDuration .Created}}\t{{humanSize .Size}}" + + if flags.history { + hdr += "\tHISTORY" + row += "\t{{if .History}}{{join .History \", \"}}{{else}}{{end}}" + } + + if flags.readOnly { + hdr += "\tReadOnly" + row += "\t{{.ReadOnly}}" + } + + if flags.noHeading { + hdr = "" + } else { + hdr += "\n" + } + + row += "\n" + return hdr, row } diff --git a/cmd/podmanV2/registry/registry.go b/cmd/podmanV2/registry/registry.go index 9c12b2456..f0650a7cf 100644 --- a/cmd/podmanV2/registry/registry.go +++ b/cmd/podmanV2/registry/registry.go @@ -21,6 +21,7 @@ var ( imageEngine entities.ImageEngine containerEngine entities.ContainerEngine + cliCtx context.Context EngineOptions entities.EngineOptions @@ -125,3 +126,10 @@ func Options(cmd *cobra.Command) (*entities.EngineOptions, error) { } return nil, nil } + +func GetContext() context.Context { + if cliCtx == nil { + cliCtx = context.TODO() + } + return cliCtx +} diff --git a/cmd/podmanV2/report/templates.go b/cmd/podmanV2/report/templates.go index dc43d4f9b..f3bc06405 100644 --- a/cmd/podmanV2/report/templates.go +++ b/cmd/podmanV2/report/templates.go @@ -4,6 +4,7 @@ import ( "strings" "text/template" "time" + "unicode" "github.com/docker/go-units" ) @@ -15,7 +16,24 @@ var defaultFuncMap = template.FuncMap{ } return s }, - // TODO: Remove on Go 1.14 port + "humanDuration": func(t int64) string { + return units.HumanDuration(time.Since(time.Unix(t, 0))) + " ago" + }, + "humanSize": func(sz int64) string { + s := units.HumanSizeWithPrecision(float64(sz), 3) + i := strings.LastIndexFunc(s, unicode.IsNumber) + return s[:i+1] + " " + s[i+1:] + }, + "join": strings.Join, + "lower": strings.ToLower, + "rfc3339": func(t int64) string { + return time.Unix(t, 0).Format(time.RFC3339) + }, + "replace": strings.Replace, + "split": strings.Split, + "title": strings.Title, + "upper": strings.ToUpper, + // TODO: Remove after Go 1.14 port "slice": func(s string, i, j int) string { if i > j || len(s) < i { return s @@ -25,15 +43,6 @@ var defaultFuncMap = template.FuncMap{ } return s[i:j] }, - "toRFC3339": func(t int64) string { - return time.Unix(t, 0).Format(time.RFC3339) - }, - "toHumanDuration": func(t int64) string { - return units.HumanDuration(time.Since(time.Unix(t, 0))) + " ago" - }, - "toHumanSize": func(sz int64) string { - return units.HumanSize(float64(sz)) - }, } func ReportHeader(columns ...string) []byte { diff --git a/pkg/api/handlers/compat/images.go b/pkg/api/handlers/compat/images.go index b18687bf9..cce718f54 100644 --- a/pkg/api/handlers/compat/images.go +++ b/pkg/api/handlers/compat/images.go @@ -15,6 +15,7 @@ import ( image2 "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/util" "github.com/docker/docker/api/types" "github.com/gorilla/schema" @@ -305,7 +306,7 @@ func GetImages(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed get images")) return } - var summaries = make([]*handlers.ImageSummary, len(images)) + var summaries = make([]*entities.ImageSummary, len(images)) for j, img := range images { is, err := handlers.ImageToImageSummary(img) if err != nil { diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go index b6f2f58e8..f8e666451 100644 --- a/pkg/api/handlers/libpod/images.go +++ b/pkg/api/handlers/libpod/images.go @@ -21,6 +21,7 @@ import ( image2 "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" + "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/util" "github.com/gorilla/schema" "github.com/pkg/errors" @@ -101,7 +102,7 @@ func GetImages(w http.ResponseWriter, r *http.Request) { utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "Failed get images")) return } - var summaries = make([]*handlers.ImageSummary, len(images)) + var summaries = make([]*entities.ImageSummary, len(images)) for j, img := range images { is, err := handlers.ImageToImageSummary(img) if err != nil { @@ -109,7 +110,7 @@ func GetImages(w http.ResponseWriter, r *http.Request) { return } // libpod has additional fields that we need to populate. - is.CreatedTime = img.Created() + is.Created = img.Created().Unix() is.ReadOnly = img.IsReadOnly() summaries[j] = is } diff --git a/pkg/api/handlers/types.go b/pkg/api/handlers/types.go index c6b70251b..84ca0fbed 100644 --- a/pkg/api/handlers/types.go +++ b/pkg/api/handlers/types.go @@ -13,6 +13,7 @@ import ( "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/events" libpodImage "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/domain/entities" docker "github.com/docker/docker/api/types" dockerContainer "github.com/docker/docker/api/types/container" dockerEvents "github.com/docker/docker/api/types/events" @@ -45,12 +46,6 @@ type LibpodImagesPullReport struct { ID string `json:"id"` } -type ImageSummary struct { - docker.ImageSummary - CreatedTime time.Time `json:"CreatedTime,omitempty"` - ReadOnly bool `json:"ReadOnly,omitempty"` -} - type ContainersPruneReport struct { docker.ContainersPruneReport } @@ -195,23 +190,13 @@ func EventToApiEvent(e *events.Event) *Event { }} } -func ImageToImageSummary(l *libpodImage.Image) (*ImageSummary, error) { +func ImageToImageSummary(l *libpodImage.Image) (*entities.ImageSummary, error) { containers, err := l.Containers() if err != nil { return nil, errors.Wrapf(err, "Failed to obtain Containers for image %s", l.ID()) } containerCount := len(containers) - var digests []string - for _, d := range l.Digests() { - digests = append(digests, string(d)) - } - - tags, err := l.RepoTags() - if err != nil { - return nil, errors.Wrapf(err, "Failed to obtain RepoTags for image %s", l.ID()) - } - // FIXME: GetParent() panics // parent, err := l.GetParent(context.TODO()) // if err != nil { @@ -227,20 +212,43 @@ func ImageToImageSummary(l *libpodImage.Image) (*ImageSummary, error) { if err != nil { return nil, errors.Wrapf(err, "Failed to obtain Size for image %s", l.ID()) } - dockerSummary := docker.ImageSummary{ - Containers: int64(containerCount), - Created: l.Created().Unix(), - ID: l.ID(), - Labels: labels, - ParentID: l.Parent, - RepoDigests: digests, - RepoTags: tags, - SharedSize: 0, - Size: int64(*size), - VirtualSize: int64(*size), - } - is := ImageSummary{ - ImageSummary: dockerSummary, + + repoTags, err := l.RepoTags() + if err != nil { + return nil, errors.Wrapf(err, "Failed to obtain RepoTags for image %s", l.ID()) + } + + history, err := l.History(context.TODO()) + if err != nil { + return nil, errors.Wrapf(err, "Failed to obtain History for image %s", l.ID()) + } + historyIds := make([]string, len(history)) + for i, h := range history { + historyIds[i] = h.ID + } + + digests := make([]string, len(l.Digests())) + for i, d := range l.Digests() { + digests[i] = string(d) + } + + is := entities.ImageSummary{ + ID: l.ID(), + ParentId: l.Parent, + RepoTags: repoTags, + Created: l.Created().Unix(), + Size: int64(*size), + SharedSize: 0, + VirtualSize: l.VirtualSize, + Labels: labels, + Containers: containerCount, + ReadOnly: l.IsReadOnly(), + Dangling: l.Dangling(), + Names: l.Names(), + Digest: string(l.Digest()), + Digests: digests, + ConfigDigest: string(l.ConfigDigest), + History: historyIds, } return &is, nil } diff --git a/pkg/api/server/swagger.go b/pkg/api/server/swagger.go index 2e1a269f2..3eee5fda8 100644 --- a/pkg/api/server/swagger.go +++ b/pkg/api/server/swagger.go @@ -2,7 +2,6 @@ package server import ( "github.com/containers/libpod/libpod" - "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/api/handlers/utils" "github.com/containers/libpod/pkg/domain/entities" ) @@ -128,7 +127,7 @@ type swagPodAlreadyStopped struct { // swagger:response DockerImageSummary type swagImageSummary struct { // in:body - Body []handlers.ImageSummary + Body []entities.ImageSummary } // List Containers diff --git a/pkg/bindings/images/images.go b/pkg/bindings/images/images.go index c84aa4601..e67965042 100644 --- a/pkg/bindings/images/images.go +++ b/pkg/bindings/images/images.go @@ -10,6 +10,7 @@ import ( "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/bindings" + "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/inspect" ) @@ -29,8 +30,8 @@ func Exists(ctx context.Context, nameOrID string) (bool, error) { // List returns a list of images in local storage. The all boolean and filters parameters are optional // ways to alter the image query. -func List(ctx context.Context, all *bool, filters map[string][]string) ([]*handlers.ImageSummary, error) { - var imageSummary []*handlers.ImageSummary +func List(ctx context.Context, all *bool, filters map[string][]string) ([]*entities.ImageSummary, error) { + var imageSummary []*entities.ImageSummary conn, err := bindings.GetClient(ctx) if err != nil { return nil, err diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go index 27676d781..d44fdaf53 100644 --- a/pkg/domain/entities/engine_image.go +++ b/pkg/domain/entities/engine_image.go @@ -7,6 +7,6 @@ import ( type ImageEngine interface { Delete(ctx context.Context, nameOrId string, opts ImageDeleteOptions) (*ImageDeleteReport, error) History(ctx context.Context, nameOrId string, opts ImageHistoryOptions) (*ImageHistoryReport, error) - List(ctx context.Context, opts ImageListOptions) (*ImageListReport, error) + List(ctx context.Context, opts ImageListOptions) ([]*ImageSummary, error) Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error) } diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index f18ee2651..f04317e37 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -48,22 +48,24 @@ func (i *Image) Id() string { } type ImageSummary struct { - Identifier - ID string `json:"Id"` - ParentId string `json:",omitempty"` - RepoTags []string `json:",omitempty"` - Created int `json:",omitempty"` - Size int `json:",omitempty"` - SharedSize int `json:",omitempty"` - VirtualSize int `json:",omitempty"` - Labels string `json:",omitempty"` - Containers int `json:",omitempty"` - ReadOnly bool `json:",omitempty"` - Dangling bool `json:",omitempty"` + ID string `json:"Id"` + ParentId string `json:",omitempty"` + RepoTags []string `json:",omitempty"` + Created int64 `json:",omitempty"` + Size int64 `json:",omitempty"` + SharedSize int `json:",omitempty"` + VirtualSize int64 `json:",omitempty"` + Labels map[string]string `json:",omitempty"` + Containers int `json:",omitempty"` + ReadOnly bool `json:",omitempty"` + Dangling bool `json:",omitempty"` // Podman extensions - Digest digest.Digest `json:",omitempty"` - ConfigDigest digest.Digest `json:",omitempty"` + Names []string `json:",omitempty"` + Digest string `json:",omitempty"` + Digests []string `json:",omitempty"` + ConfigDigest string `json:",omitempty"` + History []string `json:",omitempty"` } func (i *ImageSummary) Id() string { @@ -78,18 +80,6 @@ func (i *ImageSummary) IsDangling() bool { return i.Dangling } -type ImageOptions struct { - All bool - Digests bool - Filter []string - Format string - Noheading bool - NoTrunc bool - Quiet bool - Sort string - History bool -} - type ImageDeleteOptions struct { Force bool } @@ -124,21 +114,14 @@ type ImageInspectOptions struct { } type ImageListOptions struct { - All bool `json:"all" schema:"all"` - Digests bool `json:"digests" schema:"digests"` - Filter []string `json:",omitempty"` - Filters url.Values `json:"filters" schema:"filters"` - Format string `json:",omitempty"` - History bool `json:",omitempty"` - Noheading bool `json:",omitempty"` - NoTrunc bool `json:",omitempty"` - Quiet bool `json:",omitempty"` - Sort string `json:",omitempty"` + All bool `json:"all" schema:"all"` + Filter []string `json:",omitempty"` + Filters url.Values `json:"filters" schema:"filters"` } -type ImageListReport struct { - Images []ImageSummary -} +// type ImageListReport struct { +// Images []ImageSummary +// } type ImagePruneOptions struct { All bool diff --git a/pkg/domain/entities/set.go b/pkg/domain/entities/set.go new file mode 100644 index 000000000..c8d6cb1a9 --- /dev/null +++ b/pkg/domain/entities/set.go @@ -0,0 +1,45 @@ +package entities + +import ( + "strings" +) + +type stringSet struct { + m map[string]struct{} +} + +func NewStringSet(elem ...string) *stringSet { + s := &stringSet{} + s.m = make(map[string]struct{}, len(elem)) + for _, e := range elem { + s.Add(e) + } + return s +} + +func (s *stringSet) Add(elem string) { + s.m[elem] = struct{}{} +} + +func (s *stringSet) Remove(elem string) { + delete(s.m, elem) +} + +func (s *stringSet) Contains(elem string) bool { + _, ok := s.m[elem] + return ok +} + +func (s *stringSet) Elements() []string { + keys := make([]string, len(s.m)) + i := 0 + for k := range s.m { + keys[i] = k + i++ + } + return keys +} + +func (s *stringSet) String() string { + return strings.Join(s.Elements(), ", ") +} diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 2db08f259..6e9d7f566 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -39,35 +39,6 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption return &report, nil } -func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) (*entities.ImageListReport, error) { - var ( - images []*libpodImage.Image - err error - ) - - filters := utils.ToLibpodFilters(opts.Filters) - if len(filters) > 0 { - images, err = ir.Libpod.ImageRuntime().GetImagesWithFilters(filters) - } else { - images, err = ir.Libpod.ImageRuntime().GetImages() - } - if err != nil { - return nil, err - } - - report := entities.ImageListReport{ - Images: make([]entities.ImageSummary, len(images)), - } - for i, img := range images { - hold := entities.ImageSummary{} - if err := utils.DeepCopy(&hold, img); err != nil { - return nil, err - } - report.Images[i] = hold - } - return &report, nil -} - func (ir *ImageEngine) History(ctx context.Context, nameOrId string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) { image, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) if err != nil { diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go new file mode 100644 index 000000000..2f4020374 --- /dev/null +++ b/pkg/domain/infra/abi/images_list.go @@ -0,0 +1,80 @@ +// +build ABISupport + +package abi + +import ( + "context" + + libpodImage "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/domain/entities" +) + +func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) { + var ( + images []*libpodImage.Image + err error + ) + + // TODO: Future work support for domain.Filters + // filters := utils.ToLibpodFilters(opts.Filters) + + if len(opts.Filter) > 0 { + images, err = ir.Libpod.ImageRuntime().GetImagesWithFilters(opts.Filter) + } else { + images, err = ir.Libpod.ImageRuntime().GetImages() + } + if err != nil { + return nil, err + } + + summaries := make([]*entities.ImageSummary, len(images)) + for i, img := range images { + var repoTags []string + if opts.All { + pairs, err := libpodImage.ReposToMap(img.Names()) + if err != nil { + return nil, err + } + + for repo, tags := range pairs { + for _, tag := range tags { + repoTags = append(repoTags, repo+":"+tag) + } + } + } else { + repoTags, _ = img.RepoTags() + } + + digests := make([]string, len(img.Digests())) + for j, d := range img.Digests() { + digests[j] = string(d) + } + + e := entities.ImageSummary{ + ID: img.ID(), + + ConfigDigest: string(img.ConfigDigest), + Created: img.Created().Unix(), + Dangling: img.Dangling(), + Digest: string(img.Digest()), + Digests: digests, + History: img.NamesHistory(), + Names: img.Names(), + ParentId: img.Parent, + ReadOnly: img.IsReadOnly(), + SharedSize: 0, + VirtualSize: img.VirtualSize, + RepoTags: repoTags, + } + e.Labels, _ = img.Labels(context.TODO()) + + ctnrs, _ := img.Containers() + e.Containers = len(ctnrs) + + sz, _ := img.Size(context.TODO()) + e.Size = int64(*sz) + + summaries[i] = &e + } + return summaries, nil +} diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 718685e57..60df40498 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -32,23 +32,22 @@ func (ir *ImageEngine) Delete(ctx context.Context, nameOrId string, opts entitie return &report, err } -func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) (*entities.ImageListReport, error) { +func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) { images, err := images.List(ir.ClientCxt, &opts.All, opts.Filters) + if err != nil { return nil, err } - report := entities.ImageListReport{ - Images: make([]entities.ImageSummary, len(images)), - } + is := make([]*entities.ImageSummary, len(images)) for i, img := range images { hold := entities.ImageSummary{} if err := utils.DeepCopy(&hold, img); err != nil { return nil, err } - report.Images[i] = hold + is[i] = &hold } - return &report, nil + return is, nil } func (ir *ImageEngine) History(ctx context.Context, nameOrId string, opts entities.ImageHistoryOptions) (*entities.ImageHistoryReport, error) { -- cgit v1.2.3-54-g00ecf