Merge pull request #2113 from baude/remoteimages

remote-client support for images
This commit is contained in:
OpenShift Merge Robot
2019-01-11 05:54:16 -08:00
committed by GitHub
12 changed files with 273 additions and 67 deletions

View File

@ -1,25 +1,26 @@
package image package imagefilters
import ( import (
"context" "context"
"strings" "strings"
"time" "time"
"github.com/containers/libpod/libpod/adapter"
"github.com/containers/libpod/pkg/inspect" "github.com/containers/libpod/pkg/inspect"
) )
// ResultFilter is a mock function for image filtering // ResultFilter is a mock function for image filtering
type ResultFilter func(*Image) bool type ResultFilter func(*adapter.ContainerImage) bool
// Filter is a function to determine whether an image is included in // Filter is a function to determine whether an image is included in
// command output. Images to be outputted are tested using the function. A true // command output. Images to be outputted are tested using the function. A true
// return will include the image, a false return will exclude it. // return will include the image, a false return will exclude it.
type Filter func(*Image, *inspect.ImageData) bool type Filter func(*adapter.ContainerImage, *inspect.ImageData) bool
// CreatedBeforeFilter allows you to filter on images created before // CreatedBeforeFilter allows you to filter on images created before
// the given time.Time // the given time.Time
func CreatedBeforeFilter(createTime time.Time) ResultFilter { func CreatedBeforeFilter(createTime time.Time) ResultFilter {
return func(i *Image) bool { return func(i *adapter.ContainerImage) bool {
return i.Created().Before(createTime) return i.Created().Before(createTime)
} }
} }
@ -27,14 +28,14 @@ func CreatedBeforeFilter(createTime time.Time) ResultFilter {
// CreatedAfterFilter allows you to filter on images created after // CreatedAfterFilter allows you to filter on images created after
// the given time.Time // the given time.Time
func CreatedAfterFilter(createTime time.Time) ResultFilter { func CreatedAfterFilter(createTime time.Time) ResultFilter {
return func(i *Image) bool { return func(i *adapter.ContainerImage) bool {
return i.Created().After(createTime) return i.Created().After(createTime)
} }
} }
// DanglingFilter allows you to filter images for dangling images // DanglingFilter allows you to filter images for dangling images
func DanglingFilter() ResultFilter { func DanglingFilter() ResultFilter {
return func(i *Image) bool { return func(i *adapter.ContainerImage) bool {
return i.Dangling() return i.Dangling()
} }
} }
@ -42,7 +43,7 @@ func DanglingFilter() ResultFilter {
// LabelFilter allows you to filter by images labels key and/or value // LabelFilter allows you to filter by images labels key and/or value
func LabelFilter(ctx context.Context, labelfilter string) ResultFilter { func LabelFilter(ctx context.Context, labelfilter string) ResultFilter {
// We need to handle both label=key and label=key=value // We need to handle both label=key and label=key=value
return func(i *Image) bool { return func(i *adapter.ContainerImage) bool {
var value string var value string
splitFilter := strings.Split(labelfilter, "=") splitFilter := strings.Split(labelfilter, "=")
key := splitFilter[0] key := splitFilter[0]
@ -61,15 +62,15 @@ func LabelFilter(ctx context.Context, labelfilter string) ResultFilter {
} }
// OutputImageFilter allows you to filter by an a specific image name // OutputImageFilter allows you to filter by an a specific image name
func OutputImageFilter(userImage *Image) ResultFilter { func OutputImageFilter(userImage *adapter.ContainerImage) ResultFilter {
return func(i *Image) bool { return func(i *adapter.ContainerImage) bool {
return userImage.ID() == i.ID() return userImage.ID() == i.ID()
} }
} }
// FilterImages filters images using a set of predefined filter funcs // FilterImages filters images using a set of predefined filter funcs
func FilterImages(images []*Image, filters []ResultFilter) []*Image { func FilterImages(images []*adapter.ContainerImage, filters []ResultFilter) []*adapter.ContainerImage {
var filteredImages []*Image var filteredImages []*adapter.ContainerImage
for _, image := range images { for _, image := range images {
include := true include := true
for _, filter := range filters { for _, filter := range filters {

View File

@ -2,6 +2,8 @@ package main
import ( import (
"context" "context"
"github.com/containers/libpod/cmd/podman/imagefilters"
"github.com/containers/libpod/libpod/adapter"
"reflect" "reflect"
"sort" "sort"
"strings" "strings"
@ -9,11 +11,9 @@ import (
"unicode" "unicode"
"github.com/containers/libpod/cmd/podman/formats" "github.com/containers/libpod/cmd/podman/formats"
"github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image" "github.com/containers/libpod/libpod/image"
"github.com/docker/go-units" "github.com/docker/go-units"
digest "github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -145,20 +145,20 @@ var (
func imagesCmd(c *cli.Context) error { func imagesCmd(c *cli.Context) error {
var ( var (
filterFuncs []image.ResultFilter filterFuncs []imagefilters.ResultFilter
newImage *image.Image newImage *adapter.ContainerImage
) )
if err := validateFlags(c, imagesFlags); err != nil { if err := validateFlags(c, imagesFlags); err != nil {
return err return err
} }
runtime, err := libpodruntime.GetRuntime(c) localRuntime, err := adapter.GetRuntime(c)
if err != nil { if err != nil {
return errors.Wrapf(err, "Could not get runtime") return errors.Wrapf(err, "Could not get runtime")
} }
defer runtime.Shutdown(false) defer localRuntime.Runtime.Shutdown(false)
if len(c.Args()) == 1 { if len(c.Args()) == 1 {
newImage, err = runtime.ImageRuntime().NewFromLocal(c.Args().Get(0)) newImage, err = localRuntime.NewImageFromLocal(c.Args().Get(0))
if err != nil { if err != nil {
return err return err
} }
@ -171,7 +171,7 @@ func imagesCmd(c *cli.Context) error {
ctx := getContext() ctx := getContext()
if len(c.StringSlice("filter")) > 0 || newImage != nil { if len(c.StringSlice("filter")) > 0 || newImage != nil {
filterFuncs, err = CreateFilterFuncs(ctx, runtime, c, newImage) filterFuncs, err = CreateFilterFuncs(ctx, localRuntime, c, newImage)
if err != nil { if err != nil {
return err return err
} }
@ -195,20 +195,20 @@ func imagesCmd(c *cli.Context) error {
children to the image once built. until buildah supports caching builds, children to the image once built. until buildah supports caching builds,
it will not generate these intermediate images. it will not generate these intermediate images.
*/ */
images, err := runtime.ImageRuntime().GetImages() images, err := localRuntime.GetImages()
if err != nil { if err != nil {
return errors.Wrapf(err, "unable to get images") return errors.Wrapf(err, "unable to get images")
} }
var filteredImages []*image.Image var filteredImages []*adapter.ContainerImage
// filter the images //filter the images
if len(c.StringSlice("filter")) > 0 || newImage != nil { if len(c.StringSlice("filter")) > 0 || newImage != nil {
filteredImages = image.FilterImages(images, filterFuncs) filteredImages = imagefilters.FilterImages(images, filterFuncs)
} else { } else {
filteredImages = images filteredImages = images
} }
return generateImagesOutput(ctx, runtime, filteredImages, opts) return generateImagesOutput(ctx, filteredImages, opts)
} }
func (i imagesOptions) setOutputFormat() string { func (i imagesOptions) setOutputFormat() string {
@ -263,7 +263,7 @@ func sortImagesOutput(sortBy string, imagesOutput imagesSorted) imagesSorted {
} }
// getImagesTemplateOutput returns the images information to be printed in human readable format // getImagesTemplateOutput returns the images information to be printed in human readable format
func getImagesTemplateOutput(ctx context.Context, runtime *libpod.Runtime, images []*image.Image, opts imagesOptions) (imagesOutput imagesSorted) { func getImagesTemplateOutput(ctx context.Context, images []*adapter.ContainerImage, opts imagesOptions) (imagesOutput imagesSorted) {
for _, img := range images { 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 // 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 // to another image's top layer. If it is, then it is an intermediate image so don't print out if the --all flag
@ -319,7 +319,7 @@ func getImagesTemplateOutput(ctx context.Context, runtime *libpod.Runtime, image
} }
// getImagesJSONOutput returns the images information in its raw form // getImagesJSONOutput returns the images information in its raw form
func getImagesJSONOutput(ctx context.Context, runtime *libpod.Runtime, images []*image.Image) (imagesOutput []imagesJSONParams) { func getImagesJSONOutput(ctx context.Context, images []*adapter.ContainerImage) (imagesOutput []imagesJSONParams) {
for _, img := range images { for _, img := range images {
size, err := img.Size(ctx) size, err := img.Size(ctx)
if err != nil { if err != nil {
@ -339,7 +339,7 @@ func getImagesJSONOutput(ctx context.Context, runtime *libpod.Runtime, images []
// generateImagesOutput generates the images based on the format provided // generateImagesOutput generates the images based on the format provided
func generateImagesOutput(ctx context.Context, runtime *libpod.Runtime, images []*image.Image, opts imagesOptions) error { func generateImagesOutput(ctx context.Context, images []*adapter.ContainerImage, opts imagesOptions) error {
if len(images) == 0 { if len(images) == 0 {
return nil return nil
} }
@ -347,10 +347,10 @@ func generateImagesOutput(ctx context.Context, runtime *libpod.Runtime, images [
switch opts.format { switch opts.format {
case formats.JSONString: case formats.JSONString:
imagesOutput := getImagesJSONOutput(ctx, runtime, images) imagesOutput := getImagesJSONOutput(ctx, images)
out = formats.JSONStructArray{Output: imagesToGeneric([]imagesTemplateParams{}, imagesOutput)} out = formats.JSONStructArray{Output: imagesToGeneric([]imagesTemplateParams{}, imagesOutput)}
default: default:
imagesOutput := getImagesTemplateOutput(ctx, runtime, images, opts) imagesOutput := getImagesTemplateOutput(ctx, images, opts)
out = formats.StdoutTemplateArray{Output: imagesToGeneric(imagesOutput, []imagesJSONParams{}), Template: opts.outputformat, Fields: imagesOutput[0].HeaderMap()} out = formats.StdoutTemplateArray{Output: imagesToGeneric(imagesOutput, []imagesJSONParams{}), Template: opts.outputformat, Fields: imagesOutput[0].HeaderMap()}
} }
return formats.Writer(out).Out() return formats.Writer(out).Out()
@ -375,34 +375,34 @@ func (i *imagesTemplateParams) HeaderMap() map[string]string {
// CreateFilterFuncs returns an array of filter functions based on the user inputs // CreateFilterFuncs returns an array of filter functions based on the user inputs
// and is later used to filter images for output // and is later used to filter images for output
func CreateFilterFuncs(ctx context.Context, r *libpod.Runtime, c *cli.Context, img *image.Image) ([]image.ResultFilter, error) { func CreateFilterFuncs(ctx context.Context, r *adapter.LocalRuntime, c *cli.Context, img *adapter.ContainerImage) ([]imagefilters.ResultFilter, error) {
var filterFuncs []image.ResultFilter var filterFuncs []imagefilters.ResultFilter
for _, filter := range c.StringSlice("filter") { for _, filter := range c.StringSlice("filter") {
splitFilter := strings.Split(filter, "=") splitFilter := strings.Split(filter, "=")
switch splitFilter[0] { switch splitFilter[0] {
case "before": case "before":
before, err := r.ImageRuntime().NewFromLocal(splitFilter[1]) before, err := r.NewImageFromLocal(splitFilter[1])
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1]) return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
} }
filterFuncs = append(filterFuncs, image.CreatedBeforeFilter(before.Created())) filterFuncs = append(filterFuncs, imagefilters.CreatedBeforeFilter(before.Created()))
case "after": case "after":
after, err := r.ImageRuntime().NewFromLocal(splitFilter[1]) after, err := r.NewImageFromLocal(splitFilter[1])
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1]) return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
} }
filterFuncs = append(filterFuncs, image.CreatedAfterFilter(after.Created())) filterFuncs = append(filterFuncs, imagefilters.CreatedAfterFilter(after.Created()))
case "dangling": case "dangling":
filterFuncs = append(filterFuncs, image.DanglingFilter()) filterFuncs = append(filterFuncs, imagefilters.DanglingFilter())
case "label": case "label":
labelFilter := strings.Join(splitFilter[1:], "=") labelFilter := strings.Join(splitFilter[1:], "=")
filterFuncs = append(filterFuncs, image.LabelFilter(ctx, labelFilter)) filterFuncs = append(filterFuncs, imagefilters.LabelFilter(ctx, labelFilter))
default: default:
return nil, errors.Errorf("invalid filter %s ", splitFilter[0]) return nil, errors.Errorf("invalid filter %s ", splitFilter[0])
} }
} }
if img != nil { if img != nil {
filterFuncs = append(filterFuncs, image.OutputImageFilter(img)) filterFuncs = append(filterFuncs, imagefilters.OutputImageFilter(img))
} }
return filterFuncs, nil return filterFuncs, nil
} }

View File

@ -37,7 +37,8 @@ type ImageInList (
size: int, size: int,
virtualSize: int, virtualSize: int,
containers: int, containers: int,
labels: [string]string labels: [string]string,
isParent: bool
) )
# ImageHistory describes the returned structure from ImageHistory. # ImageHistory describes the returned structure from ImageHistory.

View File

@ -0,0 +1,17 @@
// +build remoteclient
package adapter
import (
"github.com/containers/libpod/libpod"
)
// Images returns information for the host system and its components
func (r RemoteRuntime) Images() ([]libpod.InfoData, error) {
conn, err := r.Connect()
if err != nil {
return nil, err
}
_ = conn
return nil, nil
}

View File

@ -5,6 +5,7 @@ package adapter
import ( import (
"github.com/containers/libpod/cmd/podman/libpodruntime" "github.com/containers/libpod/cmd/podman/libpodruntime"
"github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod"
"github.com/containers/libpod/libpod/image"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
@ -14,6 +15,11 @@ type LocalRuntime struct {
Remote bool Remote bool
} }
// ContainerImage ...
type ContainerImage struct {
*image.Image
}
// GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it // GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it
func GetRuntime(c *cli.Context) (*LocalRuntime, error) { func GetRuntime(c *cli.Context) (*LocalRuntime, error) {
runtime, err := libpodruntime.GetRuntime(c) runtime, err := libpodruntime.GetRuntime(c)
@ -24,3 +30,26 @@ func GetRuntime(c *cli.Context) (*LocalRuntime, error) {
Runtime: runtime, Runtime: runtime,
}, nil }, nil
} }
// GetImages returns a slice of images in containerimages
func (r *LocalRuntime) GetImages() ([]*ContainerImage, error) {
var containerImages []*ContainerImage
images, err := r.Runtime.ImageRuntime().GetImages()
if err != nil {
return nil, err
}
for _, i := range images {
containerImages = append(containerImages, &ContainerImage{i})
}
return containerImages, nil
}
// NewImageFromLocal returns a containerimage representation of a image from local storage
func (r *LocalRuntime) NewImageFromLocal(name string) (*ContainerImage, error) {
img, err := r.Runtime.ImageRuntime().NewFromLocal(name)
if err != nil {
return nil, err
}
return &ContainerImage{img}, nil
}

View File

@ -2,23 +2,43 @@
package adapter package adapter
import "github.com/urfave/cli" import (
"context"
"fmt"
"github.com/containers/libpod/cmd/podman/varlink"
"github.com/containers/libpod/libpod/image"
"github.com/opencontainers/go-digest"
"github.com/urfave/cli"
"github.com/varlink/go/varlink"
"strings"
"time"
)
// ImageRuntime is wrapper for image runtime
type RemoteImageRuntime struct{}
// RemoteRuntime describes a wrapper runtime struct // RemoteRuntime describes a wrapper runtime struct
type RemoteRuntime struct{} type RemoteRuntime struct {
}
// LocalRuntime describes a typical libpod runtime // LocalRuntime describes a typical libpod runtime
type LocalRuntime struct { type LocalRuntime struct {
Runtime *RemoteRuntime Runtime *RemoteRuntime
Remote bool Remote bool
Conn *varlink.Connection
} }
// GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it // GetRuntime returns a LocalRuntime struct with the actual runtime embedded in it
func GetRuntime(c *cli.Context) (*LocalRuntime, error) { func GetRuntime(c *cli.Context) (*LocalRuntime, error) {
runtime := RemoteRuntime{} runtime := RemoteRuntime{}
conn, err := runtime.Connect()
if err != nil {
return nil, err
}
return &LocalRuntime{ return &LocalRuntime{
Runtime: &runtime, Runtime: &runtime,
Remote: true, Remote: true,
Conn: conn,
}, nil }, nil
} }
@ -26,3 +46,130 @@ func GetRuntime(c *cli.Context) (*LocalRuntime, error) {
func (r RemoteRuntime) Shutdown(force bool) error { func (r RemoteRuntime) Shutdown(force bool) error {
return nil return nil
} }
// ContainerImage
type ContainerImage struct {
remoteImage
}
type remoteImage struct {
ID string
Labels map[string]string
RepoTags []string
RepoDigests []string
Parent string
Size int64
Tag string
Repository string
Created time.Time
InputName string
Names []string
Digest digest.Digest
isParent bool
}
// GetImages returns a slice of containerimages over a varlink connection
func (r *LocalRuntime) GetImages() ([]*ContainerImage, error) {
var newImages []*ContainerImage
images, err := iopodman.ListImages().Call(r.Conn)
if err != nil {
return nil, err
}
for _, i := range images {
name := i.Id
if len(i.RepoTags) > 1 {
name = i.RepoTags[0]
}
newImage, err := imageInListToContainerImage(i, name)
if err != nil {
return nil, err
}
newImages = append(newImages, newImage)
}
return newImages, nil
}
func imageInListToContainerImage(i iopodman.ImageInList, name string) (*ContainerImage, error) {
imageParts, err := image.DecomposeString(name)
if err != nil {
return nil, err
}
created, err := splitStringDate(i.Created)
if err != nil {
return nil, err
}
ri := remoteImage{
InputName: name,
ID: i.Id,
Labels: i.Labels,
RepoTags: i.RepoTags,
RepoDigests: i.RepoTags,
Parent: i.ParentId,
Size: i.Size,
Created: created,
Tag: imageParts.Tag,
Repository: imageParts.Registry,
Names: i.RepoTags,
isParent: i.IsParent,
}
return &ContainerImage{ri}, nil
}
// NewImageFromLocal returns a container image representation of a image over varlink
func (r *LocalRuntime) NewImageFromLocal(name string) (*ContainerImage, error) {
img, err := iopodman.GetImage().Call(r.Conn, name)
if err != nil {
return nil, err
}
return imageInListToContainerImage(img, name)
}
func splitStringDate(d string) (time.Time, error) {
fields := strings.Fields(d)
t := fmt.Sprintf("%sT%sZ", fields[0], fields[1])
return time.ParseInLocation(time.RFC3339Nano, t, time.UTC)
}
// IsParent goes through the layers in the store and checks if i.TopLayer is
// the parent of any other layer in store. Double check that image with that
// layer exists as well.
func (ci *ContainerImage) IsParent() (bool, error) {
return ci.remoteImage.isParent, nil
}
// ID returns the image ID as a string
func (ci *ContainerImage) ID() string {
return ci.remoteImage.ID
}
// Names returns a string array of names associated with the image
func (ci *ContainerImage) Names() []string {
return ci.remoteImage.Names
}
// Created returns the time the image was created
func (ci *ContainerImage) Created() time.Time {
return ci.remoteImage.Created
}
// Size returns the size of the image
func (ci *ContainerImage) Size(ctx context.Context) (*uint64, error) {
usize := uint64(ci.remoteImage.Size)
return &usize, nil
}
// Digest returns the image's digest
func (ci *ContainerImage) Digest() digest.Digest {
return ci.remoteImage.Digest
}
// Labels returns a map of the image's labels
func (ci *ContainerImage) Labels(ctx context.Context) (map[string]string, error) {
return ci.remoteImage.Labels, nil
}
// Dangling returns a bool if the image is "dangling"
func (ci *ContainerImage) Dangling() bool {
return len(ci.Names()) == 0
}

View File

@ -460,7 +460,7 @@ func normalizeTag(tag string) (string, error) {
} }
// If the input does not have a tag, we need to add one (latest) // If the input does not have a tag, we need to add one (latest)
if !decomposedTag.isTagged { if !decomposedTag.isTagged {
tag = fmt.Sprintf("%s:%s", tag, decomposedTag.tag) tag = fmt.Sprintf("%s:%s", tag, decomposedTag.Tag)
} }
// If the input doesn't specify a registry, set the registry to localhost // If the input doesn't specify a registry, set the registry to localhost
if !decomposedTag.hasRegistry { if !decomposedTag.hasRegistry {
@ -937,7 +937,7 @@ func (i *Image) MatchRepoTag(input string) (string, error) {
if err != nil { if err != nil {
return "", err return "", err
} }
if dcRepoName.registry == dcImage.registry && dcImage.registry != "" { if dcRepoName.Registry == dcImage.Registry && dcImage.Registry != "" {
count++ count++
} }
if dcRepoName.name == dcImage.name && dcImage.name != "" { if dcRepoName.name == dcImage.name && dcImage.name != "" {
@ -945,7 +945,7 @@ func (i *Image) MatchRepoTag(input string) (string, error) {
} else if splitString(dcRepoName.name) == splitString(dcImage.name) { } else if splitString(dcRepoName.name) == splitString(dcImage.name) {
count++ count++
} }
if dcRepoName.tag == dcImage.tag { if dcRepoName.Tag == dcImage.Tag {
count++ count++
} }
results[count] = append(results[count], repoName) results[count] = append(results[count], repoName)

View File

@ -7,12 +7,12 @@ import (
"github.com/containers/image/docker/reference" "github.com/containers/image/docker/reference"
) )
// imageParts describes the parts of an image's name // Parts describes the parts of an image's name
type imageParts struct { type Parts struct {
transport string transport string
registry string Registry string
name string name string
tag string Tag string
isTagged bool isTagged bool
hasRegistry bool hasRegistry bool
} }
@ -34,10 +34,16 @@ func GetImageBaseName(input string) (string, error) {
return splitImageName[len(splitImageName)-1], nil return splitImageName[len(splitImageName)-1], nil
} }
// DecomposeString decomposes a string name into imageParts description. This
// is a wrapper for decompose
func DecomposeString(input string) (Parts, error) {
return decompose(input)
}
// decompose breaks an input name into an imageParts description // decompose breaks an input name into an imageParts description
func decompose(input string) (imageParts, error) { func decompose(input string) (Parts, error) {
var ( var (
parts imageParts parts Parts
hasRegistry bool hasRegistry bool
tag string tag string
) )
@ -56,7 +62,7 @@ func decompose(input string) (imageParts, error) {
} }
registry := reference.Domain(imgRef.(reference.Named)) registry := reference.Domain(imgRef.(reference.Named))
imageName := reference.Path(imgRef.(reference.Named)) imageName := reference.Path(imgRef.(reference.Named))
// Is this a registry or a repo? // Is this a Registry or a repo?
if isRegistry(registry) { if isRegistry(registry) {
hasRegistry = true hasRegistry = true
} else { } else {
@ -65,27 +71,27 @@ func decompose(input string) (imageParts, error) {
registry = "" registry = ""
} }
} }
return imageParts{ return Parts{
registry: registry, Registry: registry,
hasRegistry: hasRegistry, hasRegistry: hasRegistry,
name: imageName, name: imageName,
tag: tag, Tag: tag,
isTagged: isTagged, isTagged: isTagged,
transport: DefaultTransport, transport: DefaultTransport,
}, nil }, nil
} }
// assemble concatenates an image's parts into a string // assemble concatenates an image's parts into a string
func (ip *imageParts) assemble() string { func (ip *Parts) assemble() string {
spec := fmt.Sprintf("%s:%s", ip.name, ip.tag) spec := fmt.Sprintf("%s:%s", ip.name, ip.Tag)
if ip.registry != "" { if ip.Registry != "" {
spec = fmt.Sprintf("%s/%s", ip.registry, spec) spec = fmt.Sprintf("%s/%s", ip.Registry, spec)
} }
return spec return spec
} }
// assemble concatenates an image's parts with transport into a string // assemble concatenates an image's parts with transport into a string
func (ip *imageParts) assembleWithTransport() string { func (ip *Parts) assembleWithTransport() string {
return fmt.Sprintf("%s%s", ip.transport, ip.assemble()) return fmt.Sprintf("%s%s", ip.transport, ip.assemble())
} }

View File

@ -55,9 +55,9 @@ func TestDecompose(t *testing.T) {
} else { } else {
assert.NoError(t, err, c.input) assert.NoError(t, err, c.input)
assert.Equal(t, c.transport, parts.transport, c.input) assert.Equal(t, c.transport, parts.transport, c.input)
assert.Equal(t, c.registry, parts.registry, c.input) assert.Equal(t, c.registry, parts.Registry, c.input)
assert.Equal(t, c.name, parts.name, c.input) assert.Equal(t, c.name, parts.name, c.input)
assert.Equal(t, c.tag, parts.tag, c.input) assert.Equal(t, c.tag, parts.Tag, c.input)
assert.Equal(t, c.isTagged, parts.isTagged, c.input) assert.Equal(t, c.isTagged, parts.isTagged, c.input)
assert.Equal(t, c.hasRegistry, parts.hasRegistry, c.input) assert.Equal(t, c.hasRegistry, parts.hasRegistry, c.input)
assert.Equal(t, c.assembled, parts.assemble(), c.input) assert.Equal(t, c.assembled, parts.assemble(), c.input)

View File

@ -76,7 +76,7 @@ func (ir *Runtime) getPullRefPair(srcRef types.ImageReference, destName string)
decomposedDest, err := decompose(destName) decomposedDest, err := decompose(destName)
if err == nil && !decomposedDest.hasRegistry { if err == nil && !decomposedDest.hasRegistry {
// If the image doesn't have a registry, set it as the default repo // If the image doesn't have a registry, set it as the default repo
decomposedDest.registry = DefaultLocalRegistry decomposedDest.Registry = DefaultLocalRegistry
decomposedDest.hasRegistry = true decomposedDest.hasRegistry = true
destName = decomposedDest.assemble() destName = decomposedDest.assemble()
} }
@ -317,7 +317,7 @@ func (ir *Runtime) pullGoalFromPossiblyUnqualifiedName(inputName string) (*pullG
} }
var refPairs []pullRefPair var refPairs []pullRefPair
for _, registry := range searchRegistries { for _, registry := range searchRegistries {
decomposedImage.registry = registry decomposedImage.Registry = registry
imageName := decomposedImage.assembleWithTransport() imageName := decomposedImage.assembleWithTransport()
if hasShaInInputName(inputName) { if hasShaInInputName(inputName) {
imageName = fmt.Sprintf("%s%s/%s", decomposedImage.transport, registry, inputName) imageName = fmt.Sprintf("%s%s/%s", decomposedImage.transport, registry, inputName)

View File

@ -16,7 +16,7 @@ import (
// findImageInRepotags takes an imageParts struct and searches images' repotags for // findImageInRepotags takes an imageParts struct and searches images' repotags for
// a match on name:tag // a match on name:tag
func findImageInRepotags(search imageParts, images []*Image) (*storage.Image, error) { func findImageInRepotags(search Parts, images []*Image) (*storage.Image, error) {
var results []*storage.Image var results []*storage.Image
for _, image := range images { for _, image := range images {
for _, name := range image.Names() { for _, name := range image.Names() {
@ -25,12 +25,12 @@ func findImageInRepotags(search imageParts, images []*Image) (*storage.Image, er
if err != nil { if err != nil {
continue continue
} }
if d.name == search.name && d.tag == search.tag { if d.name == search.name && d.Tag == search.Tag {
results = append(results, image.image) results = append(results, image.image)
continue continue
} }
// account for registry:/somedir/image // account for registry:/somedir/image
if strings.HasSuffix(d.name, search.name) && d.tag == search.tag { if strings.HasSuffix(d.name, search.name) && d.Tag == search.Tag {
results = append(results, image.image) results = append(results, image.image)
continue continue
} }

View File

@ -47,6 +47,10 @@ func (i *LibpodAPI) ListImages(call iopodman.VarlinkCall) error {
} }
size, _ := image.Size(getContext()) size, _ := image.Size(getContext())
isParent, err := image.IsParent()
if err != nil {
return call.ReplyErrorOccurred(err.Error())
}
i := iopodman.ImageInList{ i := iopodman.ImageInList{
Id: image.ID(), Id: image.ID(),
@ -58,6 +62,7 @@ func (i *LibpodAPI) ListImages(call iopodman.VarlinkCall) error {
VirtualSize: image.VirtualSize, VirtualSize: image.VirtualSize,
Containers: int64(len(containers)), Containers: int64(len(containers)),
Labels: labels, Labels: labels,
IsParent: isParent,
} }
imageList = append(imageList, i) imageList = append(imageList, i)
} }