Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move image filters under libpod/images #4664

Merged
merged 1 commit into from
Dec 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in

[func ListImages() Image](#ListImages)

[func ListImagesWithFilters(filters: []string) Image](#ListImagesWithFilters)

[func ListPods() ListPodData](#ListPods)

[func LoadImage(name: string, inputFile: string, quiet: bool, deleteFile: bool) MoreResponse](#LoadImage)
Expand Down Expand Up @@ -892,6 +894,13 @@ See also [GetContainer](#GetContainer).
method ListImages() [Image](#Image)</div>
ListImages returns information about the images that are currently in storage.
See also [InspectImage](#InspectImage).
### <a name="ListImagesWithFilters"></a>func ListImagesWithFilters
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">

method ListImagesWithFilters(filters: [[]string](#[]string)) [Image](#Image)</div>
ListImagesWithFilters returns information about the images that are currently in storage
after one or more filters has been applied.
See also [InspectImage](#InspectImage).
### <a name="ListPods"></a>func ListPods
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">

Expand Down
81 changes: 7 additions & 74 deletions cmd/podman/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,12 @@ import (
"fmt"
"reflect"
"sort"
"strconv"
"strings"
"time"
"unicode"

"github.com/containers/buildah/pkg/formats"
"github.com/containers/libpod/cmd/podman/cliconfig"
"github.com/containers/libpod/cmd/podman/imagefilters"
"github.com/containers/libpod/libpod/image"
"github.com/containers/libpod/pkg/adapter"
"github.com/docker/go-units"
Expand Down Expand Up @@ -138,10 +136,10 @@ func init() {

func imagesCmd(c *cliconfig.ImagesValues) error {
var (
filterFuncs []imagefilters.ResultFilter
image string
image string
)

ctx := getContext()
runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
if err != nil {
return errors.Wrapf(err, "Could not get runtime")
Expand All @@ -156,15 +154,9 @@ func imagesCmd(c *cliconfig.ImagesValues) error {
if len(c.Filter) > 0 && image != "" {
return errors.New("can not specify an image and a filter")
}
ctx := getContext()

if len(c.Filter) > 0 {
filterFuncs, err = CreateFilterFuncs(ctx, runtime, c.Filter, nil)
} else {
filterFuncs, err = CreateFilterFuncs(ctx, runtime, []string{fmt.Sprintf("reference=%s", image)}, nil)
}
if err != nil {
return err
filters := c.Filter
if len(filters) < 1 {
filters = append(filters, fmt.Sprintf("reference=%s", image))
}

opts := imagesOptions{
Expand All @@ -179,26 +171,17 @@ func imagesCmd(c *cliconfig.ImagesValues) error {
}

opts.outputformat = opts.setOutputFormat()
images, err := runtime.GetImages()
filteredImages, err := runtime.GetFilteredImages(filters, false)
if err != nil {
return errors.Wrapf(err, "unable to get images")
}

for _, image := range images {
for _, image := range filteredImages {
if image.IsReadOnly() {
opts.outputformat += "{{.ReadOnly}}\t"
break
}
}

var filteredImages []*adapter.ContainerImage
//filter the images
if len(c.Filter) > 0 || len(c.InputArgs) == 1 {
filteredImages = imagefilters.FilterImages(images, filterFuncs)
} else {
filteredImages = images
}

return generateImagesOutput(ctx, filteredImages, opts)
}

Expand Down Expand Up @@ -392,53 +375,3 @@ func GenImageOutputMap() map[string]string {
}
return values
}

// CreateFilterFuncs returns an array of filter functions based on the user inputs
// and is later used to filter images for output
func CreateFilterFuncs(ctx context.Context, r *adapter.LocalRuntime, filters []string, img *adapter.ContainerImage) ([]imagefilters.ResultFilter, error) {
var filterFuncs []imagefilters.ResultFilter
for _, filter := range filters {
splitFilter := strings.Split(filter, "=")
if len(splitFilter) < 2 {
return nil, errors.Errorf("invalid filter syntax %s", filter)
}
switch splitFilter[0] {
case "before":
before, err := r.NewImageFromLocal(splitFilter[1])
if err != nil {
return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
}
filterFuncs = append(filterFuncs, imagefilters.CreatedBeforeFilter(before.Created()))
case "after":
after, err := r.NewImageFromLocal(splitFilter[1])
if err != nil {
return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
}
filterFuncs = append(filterFuncs, imagefilters.CreatedAfterFilter(after.Created()))
case "readonly":
readonly, err := strconv.ParseBool(splitFilter[1])
if err != nil {
return nil, errors.Wrapf(err, "invalid filter readonly=%s", splitFilter[1])
}
filterFuncs = append(filterFuncs, imagefilters.ReadOnlyFilter(readonly))
case "dangling":
danglingImages, err := strconv.ParseBool(splitFilter[1])
if err != nil {
return nil, errors.Wrapf(err, "invalid filter dangling=%s", splitFilter[1])
}
filterFuncs = append(filterFuncs, imagefilters.DanglingFilter(danglingImages))
case "label":
labelFilter := strings.Join(splitFilter[1:], "=")
filterFuncs = append(filterFuncs, imagefilters.LabelFilter(ctx, labelFilter))
case "reference":
referenceFilter := strings.Join(splitFilter[1:], "=")
filterFuncs = append(filterFuncs, imagefilters.ReferenceFilter(ctx, referenceFilter))
default:
return nil, errors.Errorf("invalid filter %s ", splitFilter[0])
}
}
if img != nil {
filterFuncs = append(filterFuncs, imagefilters.OutputImageFilter(img))
}
return filterFuncs, nil
}
5 changes: 5 additions & 0 deletions cmd/podman/varlink/io.podman.varlink
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,11 @@ method DeleteStoppedContainers() -> (containers: []string)
# See also [InspectImage](#InspectImage).
method ListImages() -> (images: []Image)

# ListImagesWithFilters returns information about the images that are currently in storage
# after one or more filters has been applied.
# See also [InspectImage](#InspectImage).
method ListImagesWithFilters(filters: []string) -> (images: []Image)

# GetImage returns information about a single image in storage.
# If the image caGetImage returns be found, [ImageNotFound](#ImageNotFound) will be returned.
method GetImage(id: string) -> (image: Image)
Expand Down
83 changes: 69 additions & 14 deletions cmd/podman/imagefilters/filters.go → libpod/image/filters.go
Original file line number Diff line number Diff line change
@@ -1,44 +1,45 @@
package imagefilters
package image

import (
"context"
"fmt"
"github.com/pkg/errors"
"path/filepath"
"strconv"
"strings"
"time"

"github.com/containers/libpod/pkg/adapter"
"github.com/containers/libpod/pkg/inspect"
"github.com/sirupsen/logrus"
)

// ResultFilter is a mock function for image filtering
type ResultFilter func(*adapter.ContainerImage) bool
type ResultFilter func(*Image) bool

// 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
// return will include the image, a false return will exclude it.
type Filter func(*adapter.ContainerImage, *inspect.ImageData) bool
type Filter func(*Image, *inspect.ImageData) bool

// CreatedBeforeFilter allows you to filter on images created before
// the given time.Time
func CreatedBeforeFilter(createTime time.Time) ResultFilter {
return func(i *adapter.ContainerImage) bool {
return func(i *Image) bool {
return i.Created().Before(createTime)
}
}

// CreatedAfterFilter allows you to filter on images created after
// the given time.Time
func CreatedAfterFilter(createTime time.Time) ResultFilter {
return func(i *adapter.ContainerImage) bool {
return func(i *Image) bool {
return i.Created().After(createTime)
}
}

// DanglingFilter allows you to filter images for dangling images
func DanglingFilter(danglingImages bool) ResultFilter {
return func(i *adapter.ContainerImage) bool {
return func(i *Image) bool {
if danglingImages {
return i.Dangling()
}
Expand All @@ -48,7 +49,7 @@ func DanglingFilter(danglingImages bool) ResultFilter {

// ReadOnlyFilter allows you to filter images based on read/only and read/write
func ReadOnlyFilter(readOnly bool) ResultFilter {
return func(i *adapter.ContainerImage) bool {
return func(i *Image) bool {
if readOnly {
return i.IsReadOnly()
}
Expand All @@ -59,7 +60,7 @@ func ReadOnlyFilter(readOnly bool) ResultFilter {
// LabelFilter allows you to filter by images labels key and/or value
func LabelFilter(ctx context.Context, labelfilter string) ResultFilter {
// We need to handle both label=key and label=key=value
return func(i *adapter.ContainerImage) bool {
return func(i *Image) bool {
var value string
splitFilter := strings.Split(labelfilter, "=")
key := splitFilter[0]
Expand All @@ -83,7 +84,10 @@ func LabelFilter(ctx context.Context, labelfilter string) ResultFilter {
func ReferenceFilter(ctx context.Context, referenceFilter string) ResultFilter {
filter := fmt.Sprintf("*%s*", referenceFilter)
filter = strings.Replace(filter, "/", "|", -1)
return func(i *adapter.ContainerImage) bool {
return func(i *Image) bool {
if len(referenceFilter) < 1 {
return true
}
for _, name := range i.Names() {
newName := strings.Replace(name, "/", "|", -1)
match, err := filepath.Match(filter, newName)
Expand All @@ -99,15 +103,15 @@ func ReferenceFilter(ctx context.Context, referenceFilter string) ResultFilter {
}

// OutputImageFilter allows you to filter by an a specific image name
func OutputImageFilter(userImage *adapter.ContainerImage) ResultFilter {
return func(i *adapter.ContainerImage) bool {
func OutputImageFilter(userImage *Image) ResultFilter {
return func(i *Image) bool {
return userImage.ID() == i.ID()
}
}

// FilterImages filters images using a set of predefined filter funcs
func FilterImages(images []*adapter.ContainerImage, filters []ResultFilter) []*adapter.ContainerImage {
var filteredImages []*adapter.ContainerImage
func FilterImages(images []*Image, filters []ResultFilter) []*Image {
var filteredImages []*Image
for _, image := range images {
include := true
for _, filter := range filters {
Expand All @@ -119,3 +123,54 @@ func FilterImages(images []*adapter.ContainerImage, filters []ResultFilter) []*a
}
return filteredImages
}

// createFilterFuncs returns an array of filter functions based on the user inputs
// and is later used to filter images for output
func (ir *Runtime) createFilterFuncs(filters []string, img *Image) ([]ResultFilter, error) {
var filterFuncs []ResultFilter
ctx := context.Background()
for _, filter := range filters {
splitFilter := strings.Split(filter, "=")
if len(splitFilter) < 2 {
return nil, errors.Errorf("invalid filter syntax %s", filter)
}
switch splitFilter[0] {
case "before":
before, err := ir.NewFromLocal(splitFilter[1])
if err != nil {
return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
}
filterFuncs = append(filterFuncs, CreatedBeforeFilter(before.Created()))
case "after":
after, err := ir.NewFromLocal(splitFilter[1])
if err != nil {
return nil, errors.Wrapf(err, "unable to find image %s in local stores", splitFilter[1])
}
filterFuncs = append(filterFuncs, CreatedAfterFilter(after.Created()))
case "readonly":
readonly, err := strconv.ParseBool(splitFilter[1])
if err != nil {
return nil, errors.Wrapf(err, "invalid filter readonly=%s", splitFilter[1])
}
filterFuncs = append(filterFuncs, ReadOnlyFilter(readonly))
case "dangling":
danglingImages, err := strconv.ParseBool(splitFilter[1])
if err != nil {
return nil, errors.Wrapf(err, "invalid filter dangling=%s", splitFilter[1])
}
filterFuncs = append(filterFuncs, DanglingFilter(danglingImages))
case "label":
labelFilter := strings.Join(splitFilter[1:], "=")
filterFuncs = append(filterFuncs, LabelFilter(ctx, labelFilter))
case "reference":
referenceFilter := strings.Join(splitFilter[1:], "=")
filterFuncs = append(filterFuncs, ReferenceFilter(ctx, referenceFilter))
default:
return nil, errors.Errorf("invalid filter %s ", splitFilter[0])
}
}
if img != nil {
filterFuncs = append(filterFuncs, OutputImageFilter(img))
}
return filterFuncs, nil
}
13 changes: 13 additions & 0 deletions libpod/image/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,19 @@ func (ir *Runtime) Shutdown(force bool) error {
return err
}

// GetImagesWithFilters gets images with a series of filters applied
func (ir *Runtime) GetImagesWithFilters(filters []string) ([]*Image, error) {
filterFuncs, err := ir.createFilterFuncs(filters, nil)
if err != nil {
return nil, err
}
images, err := ir.GetImages()
if err != nil {
return nil, err
}
return FilterImages(images, filterFuncs), nil
}

func (i *Image) reloadImage() error {
newImage, err := i.imageruntime.getImage(i.ID())
if err != nil {
Expand Down
15 changes: 14 additions & 1 deletion pkg/adapter/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,15 @@ func getRuntime(runtime *libpod.Runtime) (*LocalRuntime, error) {
}, nil
}

// GetFilterImages returns a slice of images in containerimages that are "filtered"
func (r *LocalRuntime) GetFilteredImages(filters []string, rwOnly bool) ([]*ContainerImage, error) {
images, err := r.ImageRuntime().GetImagesWithFilters(filters)
if err != nil {
return nil, err
}
return r.ImagestoContainerImages(images, rwOnly)
}

// GetImages returns a slice of images in containerimages
func (r *LocalRuntime) GetImages() ([]*ContainerImage, error) {
return r.getImages(false)
Expand All @@ -95,11 +104,15 @@ func (r *LocalRuntime) GetRWImages() ([]*ContainerImage, error) {
}

func (r *LocalRuntime) getImages(rwOnly bool) ([]*ContainerImage, error) {
var containerImages []*ContainerImage
images, err := r.Runtime.ImageRuntime().GetImages()
if err != nil {
return nil, err
}
return r.ImagestoContainerImages(images, rwOnly)
}

func (r *LocalRuntime) ImagestoContainerImages(images []*image.Image, rwOnly bool) ([]*ContainerImage, error) {
var containerImages []*ContainerImage
for _, i := range images {
if rwOnly && i.IsReadOnly() {
continue
Expand Down
Loading