move functions supporting images command to libkpod/image
Signed-off-by: Ryan Cole <rcyoalne@gmail.com>
This commit is contained in:
parent
df7536e3c0
commit
0f44ff1d3b
6 changed files with 281 additions and 235 deletions
|
@ -1,12 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
|
||||||
|
|
||||||
is "github.com/containers/image/storage"
|
is "github.com/containers/image/storage"
|
||||||
"github.com/containers/image/types"
|
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -35,18 +31,3 @@ func getStore(c *cli.Context) (storage.Store, error) {
|
||||||
is.Transport.SetStore(store)
|
is.Transport.SetStore(store)
|
||||||
return store, nil
|
return store, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseRegistryCreds(creds string) (*types.DockerAuthConfig, error) {
|
|
||||||
if creds == "" {
|
|
||||||
return nil, errors.New("no credentials supplied")
|
|
||||||
}
|
|
||||||
if strings.Index(creds, ":") < 0 {
|
|
||||||
return nil, errors.New("user name supplied, but no password supplied")
|
|
||||||
}
|
|
||||||
v := strings.SplitN(creds, ":", 2)
|
|
||||||
cfg := &types.DockerAuthConfig{
|
|
||||||
Username: v[0],
|
|
||||||
Password: v[1],
|
|
||||||
}
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,13 +3,11 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
is "github.com/containers/image/storage"
|
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/kubernetes-incubator/cri-o/libkpod/common"
|
|
||||||
"github.com/kubernetes-incubator/cri-o/libkpod/image"
|
"github.com/kubernetes-incubator/cri-o/libkpod/image"
|
||||||
|
libkpodimage "github.com/kubernetes-incubator/cri-o/libkpod/image"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
@ -22,15 +20,6 @@ type imageOutputParams struct {
|
||||||
Size string
|
Size string
|
||||||
}
|
}
|
||||||
|
|
||||||
type filterParams struct {
|
|
||||||
dangling string
|
|
||||||
label string
|
|
||||||
beforeImage string // Images are sorted by date, so we can just output until we see the image
|
|
||||||
sinceImage string // Images are sorted by date, so we can just output until we don't see the image
|
|
||||||
seenImage bool // Hence this boolean
|
|
||||||
referencePattern string
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
imagesFlags = []cli.Flag{
|
imagesFlags = []cli.Flag{
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
|
@ -106,14 +95,9 @@ func imagesCmd(c *cli.Context) error {
|
||||||
return errors.New("'buildah images' requires at most 1 argument")
|
return errors.New("'buildah images' requires at most 1 argument")
|
||||||
}
|
}
|
||||||
|
|
||||||
images, err := store.Images()
|
var params *libkpodimage.FilterParams
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "error reading images")
|
|
||||||
}
|
|
||||||
|
|
||||||
var params *filterParams
|
|
||||||
if c.IsSet("filter") {
|
if c.IsSet("filter") {
|
||||||
params, err = parseFilter(images, c.String("filter"))
|
params, err = libkpodimage.ParseFilter(store, c.String("filter"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error parsing filter")
|
return errors.Wrapf(err, "error parsing filter")
|
||||||
}
|
}
|
||||||
|
@ -121,60 +105,15 @@ func imagesCmd(c *cli.Context) error {
|
||||||
params = nil
|
params = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(images) > 0 && !noheading && !quiet && !hasTemplate {
|
imageList, err := libkpodimage.GetImagesMatchingFilter(store, params, name)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not get list of images matching filter")
|
||||||
|
}
|
||||||
|
if len(imageList) > 0 && !noheading && !quiet && !hasTemplate {
|
||||||
outputHeader(truncate, digests)
|
outputHeader(truncate, digests)
|
||||||
}
|
}
|
||||||
|
|
||||||
return outputImages(images, formatString, store, params, name, hasTemplate, truncate, digests, quiet)
|
return outputImages(store, imageList, formatString, hasTemplate, truncate, digests, quiet)
|
||||||
}
|
|
||||||
|
|
||||||
func parseFilter(images []storage.Image, filter string) (*filterParams, error) {
|
|
||||||
params := new(filterParams)
|
|
||||||
filterStrings := strings.Split(filter, ",")
|
|
||||||
for _, param := range filterStrings {
|
|
||||||
pair := strings.SplitN(param, "=", 2)
|
|
||||||
switch strings.TrimSpace(pair[0]) {
|
|
||||||
case "dangling":
|
|
||||||
if common.IsValidBool(pair[1]) {
|
|
||||||
params.dangling = pair[1]
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("invalid filter: '%s=[%s]'", pair[0], pair[1])
|
|
||||||
}
|
|
||||||
case "label":
|
|
||||||
params.label = pair[1]
|
|
||||||
case "before":
|
|
||||||
if imageExists(images, pair[1]) {
|
|
||||||
params.beforeImage = pair[1]
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("no such id: %s", pair[0])
|
|
||||||
}
|
|
||||||
case "since":
|
|
||||||
if imageExists(images, pair[1]) {
|
|
||||||
params.sinceImage = pair[1]
|
|
||||||
} else {
|
|
||||||
return nil, fmt.Errorf("no such id: %s``", pair[0])
|
|
||||||
}
|
|
||||||
case "reference":
|
|
||||||
params.referencePattern = pair[1]
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("invalid filter: '%s'", pair[0])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return params, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func imageExists(images []storage.Image, ref string) bool {
|
|
||||||
for _, image := range images {
|
|
||||||
if matchesID(image.ID, ref) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
for _, name := range image.Names {
|
|
||||||
if matchesReference(name, ref) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func outputHeader(truncate, digests bool) {
|
func outputHeader(truncate, digests bool) {
|
||||||
|
@ -191,7 +130,7 @@ func outputHeader(truncate, digests bool) {
|
||||||
fmt.Printf("%-22s %s\n", "CREATED AT", "SIZE")
|
fmt.Printf("%-22s %s\n", "CREATED AT", "SIZE")
|
||||||
}
|
}
|
||||||
|
|
||||||
func outputImages(images []storage.Image, format string, store storage.Store, filters *filterParams, argName string, hasTemplate, truncate, digests, quiet bool) error {
|
func outputImages(store storage.Store, images []storage.Image, format string, hasTemplate, truncate, digests, quiet bool) error {
|
||||||
for _, img := range images {
|
for _, img := range images {
|
||||||
imageMetadata, err := image.ParseMetadata(img)
|
imageMetadata, err := image.ParseMetadata(img)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -202,156 +141,33 @@ func outputImages(images []storage.Image, format string, store storage.Store, fi
|
||||||
if len(imageMetadata.Blobs) > 0 {
|
if len(imageMetadata.Blobs) > 0 {
|
||||||
digest = string(imageMetadata.Blobs[0].Digest)
|
digest = string(imageMetadata.Blobs[0].Digest)
|
||||||
}
|
}
|
||||||
size, _ := image.Size(store, img)
|
size, _ := libkpodimage.Size(store, img)
|
||||||
|
|
||||||
names := []string{""}
|
if quiet {
|
||||||
if len(img.Names) > 0 {
|
fmt.Printf("%-64s\n", img.ID)
|
||||||
names = img.Names
|
// We only want to print each id once
|
||||||
} else {
|
break
|
||||||
// images without names should be printed with "<none>" as the image name
|
|
||||||
names = append(names, "<none>")
|
|
||||||
}
|
}
|
||||||
for _, name := range names {
|
|
||||||
if !matchesFilter(img, store, name, filters) || !matchesReference(name, argName) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if quiet {
|
|
||||||
fmt.Printf("%-64s\n", img.ID)
|
|
||||||
// We only want to print each id once
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
params := imageOutputParams{
|
params := imageOutputParams{
|
||||||
ID: img.ID,
|
ID: img.ID,
|
||||||
Name: name,
|
Name: img.Names[0],
|
||||||
Digest: digest,
|
Digest: digest,
|
||||||
CreatedAt: createdTime,
|
CreatedAt: createdTime,
|
||||||
Size: formattedSize(size),
|
Size: libkpodimage.FormattedSize(size),
|
||||||
}
|
|
||||||
if hasTemplate {
|
|
||||||
err = outputUsingTemplate(format, params)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
outputUsingFormatString(truncate, digests, params)
|
|
||||||
}
|
}
|
||||||
|
if hasTemplate {
|
||||||
|
err = outputUsingTemplate(format, params)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
outputUsingFormatString(truncate, digests, params)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchesFilter(image storage.Image, store storage.Store, name string, params *filterParams) bool {
|
|
||||||
if params == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if params.dangling != "" && !matchesDangling(name, params.dangling) {
|
|
||||||
return false
|
|
||||||
} else if params.label != "" && !matchesLabel(image, store, params.label) {
|
|
||||||
return false
|
|
||||||
} else if params.beforeImage != "" && !matchesBeforeImage(image, name, params) {
|
|
||||||
return false
|
|
||||||
} else if params.sinceImage != "" && !matchesSinceImage(image, name, params) {
|
|
||||||
return false
|
|
||||||
} else if params.referencePattern != "" && !matchesReference(name, params.referencePattern) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func matchesDangling(name string, dangling string) bool {
|
|
||||||
if common.IsFalse(dangling) && name != "<none>" {
|
|
||||||
return true
|
|
||||||
} else if common.IsTrue(dangling) && name == "<none>" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func matchesLabel(image storage.Image, store storage.Store, label string) bool {
|
|
||||||
storeRef, err := is.Transport.ParseStoreReference(store, "@"+image.ID)
|
|
||||||
if err != nil {
|
|
||||||
|
|
||||||
}
|
|
||||||
img, err := storeRef.NewImage(nil)
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
info, err := img.Inspect()
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
pair := strings.SplitN(label, "=", 2)
|
|
||||||
for key, value := range info.Labels {
|
|
||||||
if key == pair[0] {
|
|
||||||
if len(pair) == 2 {
|
|
||||||
if value == pair[1] {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the image was created since the filter image. Returns
|
|
||||||
// false otherwise
|
|
||||||
func matchesBeforeImage(image storage.Image, name string, params *filterParams) bool {
|
|
||||||
if params.seenImage {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if matchesReference(name, params.beforeImage) || matchesID(image.ID, params.beforeImage) {
|
|
||||||
params.seenImage = true
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the image was created since the filter image. Returns
|
|
||||||
// false otherwise
|
|
||||||
func matchesSinceImage(image storage.Image, name string, params *filterParams) bool {
|
|
||||||
if params.seenImage {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
if matchesReference(name, params.sinceImage) || matchesID(image.ID, params.sinceImage) {
|
|
||||||
params.seenImage = true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func matchesID(id, argID string) bool {
|
|
||||||
return strings.HasPrefix(argID, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func matchesReference(name, argName string) bool {
|
|
||||||
if argName == "" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
splitName := strings.Split(name, ":")
|
|
||||||
// If the arg contains a tag, we handle it differently than if it does not
|
|
||||||
if strings.Contains(argName, ":") {
|
|
||||||
splitArg := strings.Split(argName, ":")
|
|
||||||
return strings.HasSuffix(splitName[0], splitArg[0]) && (splitName[1] == splitArg[1])
|
|
||||||
}
|
|
||||||
return strings.HasSuffix(splitName[0], argName)
|
|
||||||
}
|
|
||||||
|
|
||||||
func formattedSize(size int64) string {
|
|
||||||
suffixes := [5]string{"B", "KB", "MB", "GB", "TB"}
|
|
||||||
|
|
||||||
count := 0
|
|
||||||
formattedSize := float64(size)
|
|
||||||
for formattedSize >= 1024 && count < 4 {
|
|
||||||
formattedSize /= 1024
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
return fmt.Sprintf("%.4g %s", formattedSize, suffixes[count])
|
|
||||||
}
|
|
||||||
|
|
||||||
func outputUsingTemplate(format string, params imageOutputParams) error {
|
func outputUsingTemplate(format string, params imageOutputParams) error {
|
||||||
tmpl, err := template.New("image").Parse(format)
|
tmpl, err := template.New("image").Parse(format)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -86,7 +86,7 @@ func pushCmd(c *cli.Context) error {
|
||||||
signBy := c.String("sign-by")
|
signBy := c.String("sign-by")
|
||||||
|
|
||||||
if registryCredsString != "" {
|
if registryCredsString != "" {
|
||||||
creds, err := parseRegistryCreds(registryCredsString)
|
creds, err := common.ParseRegistryCreds(registryCredsString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"github.com/containers/image/transports/alltransports"
|
"github.com/containers/image/transports/alltransports"
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
|
libkpodimage "github.com/kubernetes-incubator/cri-o/libkpod/image"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
)
|
)
|
||||||
|
@ -67,7 +68,7 @@ func rmiCmd(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// If the user supplied an ID, we cannot delete the image if it is referred to by multiple tags
|
// If the user supplied an ID, we cannot delete the image if it is referred to by multiple tags
|
||||||
if matchesID(image.ID, id) {
|
if libkpodimage.MatchesID(image.ID, id) {
|
||||||
if len(image.Names) > 1 && !force {
|
if len(image.Names) > 1 && !force {
|
||||||
return fmt.Errorf("unable to delete %s (must force) - image is referred to in multiple tags", image.ID)
|
return fmt.Errorf("unable to delete %s (must force) - image is referred to in multiple tags", image.ID)
|
||||||
}
|
}
|
||||||
|
@ -130,7 +131,7 @@ func untagImage(imgArg string, image *storage.Image, store storage.Store) (strin
|
||||||
newNames := []string{}
|
newNames := []string{}
|
||||||
removedName := ""
|
removedName := ""
|
||||||
for _, name := range image.Names {
|
for _, name := range image.Names {
|
||||||
if matchesReference(name, imgArg) {
|
if libkpodimage.MatchesReference(name, imgArg) {
|
||||||
removedName = name
|
removedName = name
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
package common
|
package common
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
cp "github.com/containers/image/copy"
|
cp "github.com/containers/image/copy"
|
||||||
"github.com/containers/image/signature"
|
"github.com/containers/image/signature"
|
||||||
|
@ -68,3 +70,20 @@ func GetPolicyContext(path string) (*signature.PolicyContext, error) {
|
||||||
}
|
}
|
||||||
return signature.NewPolicyContext(policy)
|
return signature.NewPolicyContext(policy)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseRegistryCreds takes a credentials string in the form USERNAME:PASSWORD
|
||||||
|
// and returns a DockerAuthConfig
|
||||||
|
func ParseRegistryCreds(creds string) (*types.DockerAuthConfig, error) {
|
||||||
|
if creds == "" {
|
||||||
|
return nil, errors.New("no credentials supplied")
|
||||||
|
}
|
||||||
|
if strings.Index(creds, ":") < 0 {
|
||||||
|
return nil, errors.New("user name supplied, but no password supplied")
|
||||||
|
}
|
||||||
|
v := strings.SplitN(creds, ":", 2)
|
||||||
|
cfg := &types.DockerAuthConfig{
|
||||||
|
Username: v[0],
|
||||||
|
Password: v[1],
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
|
@ -1,11 +1,189 @@
|
||||||
package image
|
package image
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
is "github.com/containers/image/storage"
|
is "github.com/containers/image/storage"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
|
"github.com/kubernetes-incubator/cri-o/libkpod/common"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// FilterParams contains the filter options that may be given when outputting images
|
||||||
|
type FilterParams struct {
|
||||||
|
dangling string
|
||||||
|
label string
|
||||||
|
beforeImage time.Time
|
||||||
|
sinceImage time.Time
|
||||||
|
referencePattern string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseFilter takes a set of images and a filter string as input, and returns the
|
||||||
|
func ParseFilter(store storage.Store, filter string) (*FilterParams, error) {
|
||||||
|
images, err := store.Images()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
params := new(FilterParams)
|
||||||
|
filterStrings := strings.Split(filter, ",")
|
||||||
|
for _, param := range filterStrings {
|
||||||
|
pair := strings.SplitN(param, "=", 2)
|
||||||
|
switch strings.TrimSpace(pair[0]) {
|
||||||
|
case "dangling":
|
||||||
|
if common.IsValidBool(pair[1]) {
|
||||||
|
params.dangling = pair[1]
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("invalid filter: '%s=[%s]'", pair[0], pair[1])
|
||||||
|
}
|
||||||
|
case "label":
|
||||||
|
params.label = pair[1]
|
||||||
|
case "before":
|
||||||
|
if img, err := findImageInSlice(images, pair[1]); err == nil {
|
||||||
|
params.beforeImage, _ = getCreatedTime(img)
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("no such id: %s", pair[0])
|
||||||
|
}
|
||||||
|
case "since":
|
||||||
|
if img, err := findImageInSlice(images, pair[1]); err == nil {
|
||||||
|
params.sinceImage, _ = getCreatedTime(img)
|
||||||
|
} else {
|
||||||
|
return nil, fmt.Errorf("no such id: %s``", pair[0])
|
||||||
|
}
|
||||||
|
case "reference":
|
||||||
|
params.referencePattern = pair[1]
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid filter: '%s'", pair[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchesFilter(store storage.Store, image storage.Image, name string, params *FilterParams) bool {
|
||||||
|
if params == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if params.dangling != "" && !matchesDangling(name, params.dangling) {
|
||||||
|
return false
|
||||||
|
} else if params.label != "" && !matchesLabel(image, store, params.label) {
|
||||||
|
return false
|
||||||
|
} else if !params.beforeImage.IsZero() && !matchesBeforeImage(image, name, params) {
|
||||||
|
return false
|
||||||
|
} else if !params.sinceImage.IsZero() && !matchesSinceImage(image, name, params) {
|
||||||
|
return false
|
||||||
|
} else if params.referencePattern != "" && !MatchesReference(name, params.referencePattern) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchesDangling(name string, dangling string) bool {
|
||||||
|
if common.IsFalse(dangling) && name != "<none>" {
|
||||||
|
return true
|
||||||
|
} else if common.IsTrue(dangling) && name == "<none>" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchesLabel(image storage.Image, store storage.Store, label string) bool {
|
||||||
|
storeRef, err := is.Transport.ParseStoreReference(store, "@"+image.ID)
|
||||||
|
if err != nil {
|
||||||
|
|
||||||
|
}
|
||||||
|
img, err := storeRef.NewImage(nil)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
info, err := img.Inspect()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
pair := strings.SplitN(label, "=", 2)
|
||||||
|
for key, value := range info.Labels {
|
||||||
|
if key == pair[0] {
|
||||||
|
if len(pair) == 2 {
|
||||||
|
if value == pair[1] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the image was created since the filter image. Returns
|
||||||
|
// false otherwise
|
||||||
|
func matchesBeforeImage(image storage.Image, name string, params *FilterParams) bool {
|
||||||
|
if params.beforeImage.IsZero() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
createdTime, err := getCreatedTime(image)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if createdTime.Before(params.beforeImage) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if the image was created since the filter image. Returns
|
||||||
|
// false otherwise
|
||||||
|
func matchesSinceImage(image storage.Image, name string, params *FilterParams) bool {
|
||||||
|
if params.sinceImage.IsZero() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
createdTime, err := getCreatedTime(image)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if createdTime.After(params.beforeImage) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchesID returns true if argID is a full or partial match for id
|
||||||
|
func MatchesID(id, argID string) bool {
|
||||||
|
return strings.HasPrefix(id, argID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MatchesReference returns true if argName is a full or partial match for name
|
||||||
|
// Partial matches will register only if they match the most specific part of the name available
|
||||||
|
// For example, take the image docker.io/library/redis:latest
|
||||||
|
// redis, library,redis, docker.io/library/redis, redis:latest, etc. will match
|
||||||
|
// But redis:alpine, ry/redis, library, and io/library/redis will not
|
||||||
|
func MatchesReference(name, argName string) bool {
|
||||||
|
if argName == "" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
splitName := strings.Split(name, ":")
|
||||||
|
// If the arg contains a tag, we handle it differently than if it does not
|
||||||
|
if strings.Contains(argName, ":") {
|
||||||
|
splitArg := strings.Split(argName, ":")
|
||||||
|
return strings.HasSuffix(splitName[0], splitArg[0]) && (splitName[1] == splitArg[1])
|
||||||
|
}
|
||||||
|
return strings.HasSuffix(splitName[0], argName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormattedSize returns a human-readable formatted size for the image
|
||||||
|
func FormattedSize(size int64) string {
|
||||||
|
suffixes := [5]string{"B", "KB", "MB", "GB", "TB"}
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
formattedSize := float64(size)
|
||||||
|
for formattedSize >= 1024 && count < 4 {
|
||||||
|
formattedSize /= 1024
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%.4g %s", formattedSize, suffixes[count])
|
||||||
|
}
|
||||||
|
|
||||||
// FindImage searches for an image with a matching the given name or ID in the given store
|
// FindImage searches for an image with a matching the given name or ID in the given store
|
||||||
func FindImage(store storage.Store, image string) (*storage.Image, error) {
|
func FindImage(store storage.Store, image string) (*storage.Image, error) {
|
||||||
var img *storage.Image
|
var img *storage.Image
|
||||||
|
@ -26,6 +204,20 @@ func FindImage(store storage.Store, image string) (*storage.Image, error) {
|
||||||
return img, nil
|
return img, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findImageInSlice(images []storage.Image, ref string) (storage.Image, error) {
|
||||||
|
for _, image := range images {
|
||||||
|
if MatchesID(image.ID, ref) {
|
||||||
|
return image, nil
|
||||||
|
}
|
||||||
|
for _, name := range image.Names {
|
||||||
|
if MatchesReference(name, ref) {
|
||||||
|
return image, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return storage.Image{}, errors.New("could not find image")
|
||||||
|
}
|
||||||
|
|
||||||
// Size returns the size of the image in the given store
|
// Size returns the size of the image in the given store
|
||||||
func Size(store storage.Store, img storage.Image) (int64, error) {
|
func Size(store storage.Store, img storage.Image) (int64, error) {
|
||||||
is.Transport.SetStore(store)
|
is.Transport.SetStore(store)
|
||||||
|
@ -55,3 +247,40 @@ func GetTopLayerID(img storage.Image) (string, error) {
|
||||||
// Return the first layer associated with the given digest
|
// Return the first layer associated with the given digest
|
||||||
return metadata.Layers[digest][0], nil
|
return metadata.Layers[digest][0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getCreatedTime(image storage.Image) (time.Time, error) {
|
||||||
|
metadata, err := ParseMetadata(image)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
return metadata.CreatedTime, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetImagesMatchingFilter returns a slice of all images in the store that match the provided FilterParams.
|
||||||
|
// Images with more than one name matching the filter will be in the slice once for each name
|
||||||
|
func GetImagesMatchingFilter(store storage.Store, filter *FilterParams, argName string) ([]storage.Image, error) {
|
||||||
|
images, err := store.Images()
|
||||||
|
filteredImages := []storage.Image{}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if filter == nil {
|
||||||
|
return images, nil
|
||||||
|
}
|
||||||
|
for _, image := range images {
|
||||||
|
names := []string{}
|
||||||
|
if len(image.Names) > 0 {
|
||||||
|
names = image.Names
|
||||||
|
} else {
|
||||||
|
names = append(names, "<none>")
|
||||||
|
}
|
||||||
|
for _, name := range names {
|
||||||
|
if matchesFilter(store, image, name, filter) || MatchesReference(name, argName) {
|
||||||
|
newImage := image
|
||||||
|
newImage.Names = []string{name}
|
||||||
|
filteredImages = append(filteredImages, newImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filteredImages, nil
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue