From 3428de0672afcd94ce65d7c29fd23e44e7e2b465 Mon Sep 17 00:00:00 2001
From: baude <bbaude@redhat.com>
Date: Tue, 20 Mar 2018 10:21:13 -0500
Subject: Migrate podman images to image library

Signed-off-by: baude <bbaude@redhat.com>

Closes: #523
Approved by: mheon
---
 cmd/podman/images.go  |  89 +++++++++++++++---------------
 libpod/image/image.go |  26 +++++++++
 libpod/image/utils.go |  20 +++++++
 libpod/runtime_img.go | 146 +++++++-------------------------------------------
 4 files changed, 111 insertions(+), 170 deletions(-)

diff --git a/cmd/podman/images.go b/cmd/podman/images.go
index 846f00c98..67f7920f2 100644
--- a/cmd/podman/images.go
+++ b/cmd/podman/images.go
@@ -10,7 +10,7 @@ import (
 	"github.com/pkg/errors"
 	"github.com/projectatomic/libpod/cmd/podman/formats"
 	"github.com/projectatomic/libpod/libpod"
-	"github.com/projectatomic/libpod/pkg/inspect"
+	"github.com/projectatomic/libpod/libpod/image"
 	"github.com/urfave/cli"
 )
 
@@ -81,6 +81,10 @@ var (
 )
 
 func imagesCmd(c *cli.Context) error {
+	var (
+		filterFuncs []libpod.ImageResultFilter
+		newImage    *image.Image
+	)
 	if err := validateFlags(c, imagesFlags); err != nil {
 		return err
 	}
@@ -90,18 +94,19 @@ func imagesCmd(c *cli.Context) error {
 		return errors.Wrapf(err, "Could not get runtime")
 	}
 	defer runtime.Shutdown(false)
-	var filterFuncs []libpod.ImageResultFilter
-	var imageInput string
 	if len(c.Args()) == 1 {
-		imageInput = c.Args().Get(0)
+		newImage, err = runtime.ImageRuntime().NewFromLocal(c.Args().Get(0))
+		if err != nil {
+			return err
+		}
 	}
 
 	if len(c.Args()) > 1 {
 		return errors.New("'podman images' requires at most 1 argument")
 	}
 
-	if len(c.StringSlice("filter")) > 0 || len(strings.TrimSpace(imageInput)) != 0 {
-		filterFuncs, err = CreateFilterFuncs(runtime, c, imageInput)
+	if len(c.StringSlice("filter")) > 0 || newImage != nil {
+		filterFuncs, err = CreateFilterFuncs(runtime, c, newImage)
 		if err != nil {
 			return err
 		}
@@ -123,14 +128,14 @@ func imagesCmd(c *cli.Context) error {
 		children to the image once built. until buildah supports caching builds,
 		it will not generate these intermediate images.
 	*/
-	images, err := runtime.GetImageResults()
+	images, err := runtime.ImageRuntime().GetImages()
 	if err != nil {
 		return errors.Wrapf(err, "unable to get images")
 	}
 
-	var filteredImages []inspect.ImageResult
+	var filteredImages []*image.Image
 	// filter the images
-	if len(c.StringSlice("filter")) > 0 || len(strings.TrimSpace(imageInput)) != 0 {
+	if len(c.StringSlice("filter")) > 0 || newImage != nil {
 		filteredImages = libpod.FilterImages(images, filterFuncs)
 	} else {
 		filteredImages = images
@@ -174,24 +179,28 @@ func imagesToGeneric(templParams []imagesTemplateParams, JSONParams []imagesJSON
 }
 
 // getImagesTemplateOutput returns the images information to be printed in human readable format
-func getImagesTemplateOutput(runtime *libpod.Runtime, images []inspect.ImageResult, opts imagesOptions) (imagesOutput []imagesTemplateParams) {
+func getImagesTemplateOutput(runtime *libpod.Runtime, images []*image.Image, opts imagesOptions) (imagesOutput []imagesTemplateParams) {
 	for _, img := range images {
-		createdTime := img.Created
+		createdTime := img.Created()
 
-		imageID := "sha256:" + img.ID
+		imageID := "sha256:" + img.ID()
 		if !opts.noTrunc {
-			imageID = shortID(img.ID)
+			imageID = shortID(img.ID())
 		}
 		// get all specified repo:tag pairs and print them separately
-		for repo, tags := range libpod.ReposToMap(img.RepoTags) {
+		for repo, tags := range image.ReposToMap(img.Names()) {
 			for _, tag := range tags {
+				size, err := img.Size()
+				if err != nil {
+					size = nil
+				}
 				params := imagesTemplateParams{
 					Repository: repo,
 					Tag:        tag,
 					ID:         imageID,
-					Digest:     img.Digest,
+					Digest:     img.Digest(),
 					Created:    units.HumanDuration(time.Since((createdTime))) + " ago",
-					Size:       units.HumanSizeWithPrecision(float64(*img.Size), 3),
+					Size:       units.HumanSizeWithPrecision(float64(*size), 3),
 				}
 				imagesOutput = append(imagesOutput, params)
 			}
@@ -201,14 +210,18 @@ func getImagesTemplateOutput(runtime *libpod.Runtime, images []inspect.ImageResu
 }
 
 // getImagesJSONOutput returns the images information in its raw form
-func getImagesJSONOutput(runtime *libpod.Runtime, images []inspect.ImageResult) (imagesOutput []imagesJSONParams) {
+func getImagesJSONOutput(runtime *libpod.Runtime, images []*image.Image) (imagesOutput []imagesJSONParams) {
 	for _, img := range images {
+		size, err := img.Size()
+		if err != nil {
+			size = nil
+		}
 		params := imagesJSONParams{
-			ID:      img.ID,
-			Name:    img.RepoTags,
-			Digest:  img.Digest,
-			Created: img.Created,
-			Size:    img.Size,
+			ID:      img.ID(),
+			Name:    img.Names(),
+			Digest:  img.Digest(),
+			Created: img.Created(),
+			Size:    size,
 		}
 		imagesOutput = append(imagesOutput, params)
 	}
@@ -217,7 +230,7 @@ func getImagesJSONOutput(runtime *libpod.Runtime, images []inspect.ImageResult)
 
 // generateImagesOutput generates the images based on the format provided
 
-func generateImagesOutput(runtime *libpod.Runtime, images []inspect.ImageResult, opts imagesOptions) error {
+func generateImagesOutput(runtime *libpod.Runtime, images []*image.Image, opts imagesOptions) error {
 	if len(images) == 0 {
 		return nil
 	}
@@ -253,35 +266,23 @@ func (i *imagesTemplateParams) HeaderMap() map[string]string {
 
 // CreateFilterFuncs returns an array of filter functions based on the user inputs
 // and is later used to filter images for output
-func CreateFilterFuncs(r *libpod.Runtime, c *cli.Context, userInput string) ([]libpod.ImageResultFilter, error) {
+func CreateFilterFuncs(r *libpod.Runtime, c *cli.Context, image *image.Image) ([]libpod.ImageResultFilter, error) {
 	var filterFuncs []libpod.ImageResultFilter
 	for _, filter := range c.StringSlice("filter") {
 		splitFilter := strings.Split(filter, "=")
 		switch splitFilter[0] {
 		case "before":
-			before := r.NewImage(splitFilter[1])
-			_, beforeID, _ := before.GetLocalImageName()
-
-			if before.LocalName == "" {
-				return nil, errors.Errorf("unable to find image % in local stores", splitFilter[1])
-			}
-			img, err := r.GetImage(beforeID)
+			before, err := r.ImageRuntime().NewFromLocal(splitFilter[1])
 			if err != nil {
-				return nil, err
+				return nil, errors.Wrapf(err, "unable to find image % in local stores", splitFilter[1])
 			}
-			filterFuncs = append(filterFuncs, libpod.ImageCreatedBefore(img.Created))
+			filterFuncs = append(filterFuncs, libpod.ImageCreatedBefore(before.Created()))
 		case "after":
-			after := r.NewImage(splitFilter[1])
-			_, afterID, _ := after.GetLocalImageName()
-
-			if after.LocalName == "" {
-				return nil, errors.Errorf("unable to find image % in local stores", splitFilter[1])
-			}
-			img, err := r.GetImage(afterID)
+			after, err := r.ImageRuntime().NewFromLocal(splitFilter[1])
 			if err != nil {
-				return nil, err
+				return nil, errors.Wrapf(err, "unable to find image % in local stores", splitFilter[1])
 			}
-			filterFuncs = append(filterFuncs, libpod.ImageCreatedAfter(img.Created))
+			filterFuncs = append(filterFuncs, libpod.ImageCreatedAfter(after.Created()))
 		case "dangling":
 			filterFuncs = append(filterFuncs, libpod.ImageDangling())
 		case "label":
@@ -291,8 +292,8 @@ func CreateFilterFuncs(r *libpod.Runtime, c *cli.Context, userInput string) ([]l
 			return nil, errors.Errorf("invalid filter %s ", splitFilter[0])
 		}
 	}
-	if len(strings.TrimSpace(userInput)) != 0 {
-		filterFuncs = append(filterFuncs, libpod.OutputImageFilter(userInput))
+	if image != nil {
+		filterFuncs = append(filterFuncs, libpod.OutputImageFilter(image))
 	}
 	return filterFuncs, nil
 }
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 5e69a0a98..15fc5174c 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -29,12 +29,16 @@ import (
 // Image is the primary struct for dealing with images
 // It is still very much a work in progress
 type Image struct {
+	// Adding these two structs for now but will cull when we near
+	// completion of this library.
 	inspect.ImageData
+	inspect.ImageResult
 	InputName string
 	Local     bool
 	//runtime   *libpod.Runtime
 	image        *storage.Image
 	imageruntime *Runtime
+	repotagsMap  map[string][]string
 }
 
 // Runtime contains the store
@@ -496,6 +500,28 @@ func (i *Image) History() ([]ociv1.History, []types.BlobInfo, error) {
 	return oci.History, img.LayerInfos(), nil
 }
 
+// Dangling returns a bool if the image is "dangling"
+func (i *Image) Dangling() bool {
+	return len(i.Names()) == 0
+}
+
+// Labels returns the image's labels
+func (i *Image) Labels() (map[string]string, error) {
+	sr, err := i.toStorageReference()
+	if err != nil {
+		return nil, err
+	}
+	ic, err := sr.NewImage(&types.SystemContext{})
+	if err != nil {
+		return nil, err
+	}
+	imgInspect, err := ic.Inspect()
+	if err != nil {
+		return nil, err
+	}
+	return imgInspect.Labels, nil
+}
+
 // Import imports and image into the store and returns an image
 func Import(path, reference string, writer io.Writer, signingOptions SigningOptions, imageConfig ociv1.Image, runtime *Runtime) (*Image, error) {
 	file := TarballTransport + ":" + path
diff --git a/libpod/image/utils.go b/libpod/image/utils.go
index 76ec349f9..c1b2aacde 100644
--- a/libpod/image/utils.go
+++ b/libpod/image/utils.go
@@ -90,3 +90,23 @@ func getPolicyContext(ctx *types.SystemContext) (*signature.PolicyContext, error
 func hasTransport(image string) bool {
 	return strings.Contains(image, "://")
 }
+
+// ReposToMap parses the specified repotags and returns a map with repositories
+// as keys and the corresponding arrays of tags as values.
+func ReposToMap(repotags []string) map[string][]string {
+	// map format is repo -> tag
+	repos := make(map[string][]string)
+	for _, repo := range repotags {
+		var repository, tag string
+		if len(repo) > 0 {
+			li := strings.LastIndex(repo, ":")
+			repository = repo[0:li]
+			tag = repo[li+1:]
+		}
+		repos[repository] = append(repos[repository], tag)
+	}
+	if len(repos) == 0 {
+		repos["<none>"] = []string{"<none>"}
+	}
+	return repos
+}
diff --git a/libpod/runtime_img.go b/libpod/runtime_img.go
index a58626855..e3cb4e2c3 100644
--- a/libpod/runtime_img.go
+++ b/libpod/runtime_img.go
@@ -110,7 +110,7 @@ type imageDecomposeStruct struct {
 }
 
 // ImageResultFilter is a mock function for image filtering
-type ImageResultFilter func(inspect.ImageResult) bool
+type ImageResultFilter func(*image.Image) bool
 
 func (k *Image) assembleFqName() string {
 	return fmt.Sprintf("%s/%s:%s", k.Registry, k.ImageName, k.Tag)
@@ -983,8 +983,6 @@ func (r *Runtime) GetImages(params *ImageFilterParams, filters ...ImageFilter) (
 
 			if include {
 				newImage := img
-				// TODO I dont think this is needed.  Will verify along the way
-				//newImage.Names = []string{name}
 				imagesFiltered = append(imagesFiltered, newImage)
 			}
 		}
@@ -1184,98 +1182,11 @@ func getPolicyContext(ctx *types.SystemContext) (*signature.PolicyContext, error
 	return policyContext, nil
 }
 
-// sizer knows its size.
-type sizer interface {
-	Size() (int64, error)
-}
-
-func imageSize(img types.ImageSource) *uint64 {
-	if s, ok := img.(sizer); ok {
-		if sum, err := s.Size(); err == nil {
-			usum := uint64(sum)
-			return &usum
-		}
-	}
-	return nil
-}
-
-// ReposToMap parses the specified repotags and returns a map with repositories
-// as keys and the corresponding arrays of tags as values.
-func ReposToMap(repotags []string) map[string][]string {
-	// map format is repo -> tag
-	repos := make(map[string][]string)
-	for _, repo := range repotags {
-		var repository, tag string
-		if len(repo) > 0 {
-			li := strings.LastIndex(repo, ":")
-			repository = repo[0:li]
-			tag = repo[li+1:]
-		}
-		repos[repository] = append(repos[repository], tag)
-	}
-	if len(repos) == 0 {
-		repos["<none>"] = []string{"<none>"}
-	}
-	return repos
-}
-
-// GetImageResults gets the images for podman images and returns them as
-// an array of ImageResults
-func (r *Runtime) GetImageResults() ([]inspect.ImageResult, error) {
-	var results []inspect.ImageResult
-
-	images, err := r.store.Images()
-	if err != nil {
-		return nil, err
-	}
-	for _, image := range images {
-		storeRef, err := is.Transport.ParseStoreReference(r.store, image.ID)
-		if err != nil {
-			return nil, err
-		}
-		systemContext := &types.SystemContext{}
-		img, err := storeRef.NewImageSource(systemContext)
-		if err != nil {
-			return nil, err
-		}
-		ic, err := storeRef.NewImage(&types.SystemContext{})
-		if err != nil {
-			return nil, err
-		}
-		imgInspect, err := ic.Inspect()
-		if err != nil {
-			return nil, err
-		}
-		dangling := false
-		if len(image.Names) == 0 {
-			dangling = true
-		}
-
-		for repo, tags := range ReposToMap(image.Names) {
-			// use the first pair as the image's default repo and tag
-			results = append(results, inspect.ImageResult{
-				ID:         image.ID,
-				Repository: repo,
-				RepoTags:   image.Names,
-				Tag:        tags[0],
-				Size:       imageSize(img),
-				Digest:     image.Digest,
-				Created:    image.Created,
-				Labels:     imgInspect.Labels,
-				Dangling:   dangling,
-			})
-			break
-		}
-
-	}
-	return results, nil
-}
-
 // ImageCreatedBefore allows you to filter on images created before
 // the given time.Time
 func ImageCreatedBefore(createTime time.Time) ImageResultFilter {
-	return func(i inspect.ImageResult) bool {
-		if i.Created.Before(createTime) {
+	return func(i *image.Image) bool {
+		if i.Created().Before(createTime) {
 			return true
 		}
 		return false
@@ -1285,8 +1196,8 @@ func ImageCreatedBefore(createTime time.Time) ImageResultFilter {
 // ImageCreatedAfter allows you to filter on images created after
 // the given time.Time
 func ImageCreatedAfter(createTime time.Time) ImageResultFilter {
-	return func(i inspect.ImageResult) bool {
-		if i.Created.After(createTime) {
+	return func(i *image.Image) bool {
+		if i.Created().After(createTime) {
 			return true
 		}
 		return false
@@ -1295,8 +1206,8 @@ func ImageCreatedAfter(createTime time.Time) ImageResultFilter {
 
 // ImageDangling allows you to filter images for dangling images
 func ImageDangling() ImageResultFilter {
-	return func(i inspect.ImageResult) bool {
-		if i.Dangling {
+	return func(i *image.Image) bool {
+		if i.Dangling() {
 			return true
 		}
 		return false
@@ -1306,51 +1217,34 @@ func ImageDangling() ImageResultFilter {
 // ImageLabel allows you to filter by images labels key and/or value
 func ImageLabel(labelfilter string) ImageResultFilter {
 	// We need to handle both label=key and label=key=value
-	return func(i inspect.ImageResult) bool {
+	return func(i *image.Image) bool {
 		var value string
 		splitFilter := strings.Split(labelfilter, "=")
 		key := splitFilter[0]
 		if len(splitFilter) > 1 {
 			value = splitFilter[1]
 		}
-		for labelKey, labelValue := range i.Labels {
-			// handles label=key
-			if key == labelKey && len(strings.TrimSpace(value)) == 0 {
-				return true
-			}
-			//handles label=key=value
-			if key == labelKey && value == labelValue {
-				return true
-			}
+		labels, err := i.Labels()
+		if err != nil {
+			return false
 		}
-		return false
+		if len(strings.TrimSpace(labels[key])) > 0 && len(strings.TrimSpace(value)) == 0 {
+			return true
+		}
+		return labels[key] == value
 	}
 }
 
 // OutputImageFilter allows you to filter by an a specific image name
-func OutputImageFilter(name string) ImageResultFilter {
-	return func(i inspect.ImageResult) bool {
-		li := strings.LastIndex(name, ":")
-		var repository, tag string
-		if li < 0 {
-			repository = name
-		} else {
-			repository = name[0:li]
-			tag = name[li+1:]
-		}
-		if repository == i.Repository && len(strings.TrimSpace(tag)) == 0 {
-			return true
-		}
-		if repository == i.Repository && tag == i.Tag {
-			return true
-		}
-		return false
+func OutputImageFilter(userImage *image.Image) ImageResultFilter {
+	return func(i *image.Image) bool {
+		return userImage.ID() == i.ID()
 	}
 }
 
 // FilterImages filters images using a set of predefined fitler funcs
-func FilterImages(images []inspect.ImageResult, filters []ImageResultFilter) []inspect.ImageResult {
-	var filteredImages []inspect.ImageResult
+func FilterImages(images []*image.Image, filters []ImageResultFilter) []*image.Image {
+	var filteredImages []*image.Image
 	for _, image := range images {
 		include := true
 		for _, filter := range filters {
-- 
cgit v1.2.3-54-g00ecf