aboutsummaryrefslogtreecommitdiff
path: root/libpod/image/filters.go
blob: d545f1bfc1a9e2bb7b97b218ec4ebeb392372f74 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
package image

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

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

// ResultFilter is a mock function for image filtering
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(*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 *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 *Image) bool {
		return i.Created().After(createTime)
	}
}

// DanglingFilter allows you to filter images for dangling images
func DanglingFilter(danglingImages bool) ResultFilter {
	return func(i *Image) bool {
		if danglingImages {
			return i.Dangling()
		}
		return !i.Dangling()
	}
}

// ReadOnlyFilter allows you to filter images based on read/only and read/write
func ReadOnlyFilter(readOnly bool) ResultFilter {
	return func(i *Image) bool {
		if readOnly {
			return i.IsReadOnly()
		}
		return !i.IsReadOnly()
	}
}

// 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 *Image) bool {
		var value string
		splitFilter := strings.Split(labelfilter, "=")
		key := splitFilter[0]
		if len(splitFilter) > 1 {
			value = splitFilter[1]
		}
		labels, err := i.Labels(ctx)
		if err != nil {
			return false
		}
		if len(strings.TrimSpace(labels[key])) > 0 && len(strings.TrimSpace(value)) == 0 {
			return true
		}
		return labels[key] == value
	}
}

// ReferenceFilter allows you to filter by image name
// Replacing all '/' with '|' so that filepath.Match() can work
// '|' character is not valid in image name, so this is safe
func ReferenceFilter(ctx context.Context, referenceFilter string) ResultFilter {
	filter := fmt.Sprintf("*%s*", referenceFilter)
	filter = strings.Replace(filter, "/", "|", -1)
	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)
			if err != nil {
				logrus.Errorf("failed to match %s and %s, %q", name, referenceFilter, err)
			}
			if match {
				return true
			}
		}
		return false
	}
}

// OutputImageFilter allows you to filter by an a specific image name
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 []*Image, filters []ResultFilter) []*Image {
	var filteredImages []*Image
	for _, image := range images {
		include := true
		for _, filter := range filters {
			include = include && filter(image)
		}
		if include {
			filteredImages = append(filteredImages, image)
		}
	}
	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
}