mirror of
https://github.com/containers/podman.git
synced 2025-06-24 19:42:56 +08:00
Merge pull request #2113 from baude/remoteimages
remote-client support for images
This commit is contained in:
@ -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 {
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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.
|
||||||
|
17
libpod/adapter/images_remote.go
Normal file
17
libpod/adapter/images_remote.go
Normal 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
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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())
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user