diff options
Diffstat (limited to 'libpod/image')
-rw-r--r-- | libpod/image/filters.go | 83 | ||||
-rw-r--r-- | libpod/image/image.go | 24 | ||||
-rw-r--r-- | libpod/image/image_test.go | 46 | ||||
-rw-r--r-- | libpod/image/parts.go | 48 | ||||
-rw-r--r-- | libpod/image/parts_test.go | 4 | ||||
-rw-r--r-- | libpod/image/pull.go | 4 | ||||
-rw-r--r-- | libpod/image/utils.go | 6 |
7 files changed, 104 insertions, 111 deletions
diff --git a/libpod/image/filters.go b/libpod/image/filters.go deleted file mode 100644 index d0c3adfb5..000000000 --- a/libpod/image/filters.go +++ /dev/null @@ -1,83 +0,0 @@ -package image - -import ( - "context" - "strings" - "time" - - "github.com/containers/libpod/pkg/inspect" -) - -// 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() ResultFilter { - return func(i *Image) bool { - return i.Dangling() - } -} - -// 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 - } -} - -// 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 -} diff --git a/libpod/image/image.go b/libpod/image/image.go index 3a6d0e305..dda753385 100644 --- a/libpod/image/image.go +++ b/libpod/image/image.go @@ -305,12 +305,24 @@ func (i *Image) Names() []string { } // RepoDigests returns a string array of repodigests associated with the image -func (i *Image) RepoDigests() []string { +func (i *Image) RepoDigests() ([]string, error) { var repoDigests []string + digest := i.Digest() + for _, name := range i.Names() { - repoDigests = append(repoDigests, strings.SplitN(name, ":", 2)[0]+"@"+i.Digest().String()) + named, err := reference.ParseNormalizedNamed(name) + if err != nil { + return nil, err + } + + canonical, err := reference.WithDigest(reference.TrimNamed(named), digest) + if err != nil { + return nil, err + } + + repoDigests = append(repoDigests, canonical.String()) } - return repoDigests + return repoDigests, nil } // Created returns the time the image was created @@ -448,7 +460,7 @@ func normalizeTag(tag string) (string, error) { } // If the input does not have a tag, we need to add one (latest) 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 !decomposedTag.hasRegistry { @@ -925,7 +937,7 @@ func (i *Image) MatchRepoTag(input string) (string, error) { if err != nil { return "", err } - if dcRepoName.registry == dcImage.registry && dcImage.registry != "" { + if dcRepoName.Registry == dcImage.Registry && dcImage.Registry != "" { count++ } if dcRepoName.name == dcImage.name && dcImage.name != "" { @@ -933,7 +945,7 @@ func (i *Image) MatchRepoTag(input string) (string, error) { } else if splitString(dcRepoName.name) == splitString(dcImage.name) { count++ } - if dcRepoName.tag == dcImage.tag { + if dcRepoName.Tag == dcImage.Tag { count++ } results[count] = append(results[count], repoName) diff --git a/libpod/image/image_test.go b/libpod/image/image_test.go index 91bb2411b..2a68fd273 100644 --- a/libpod/image/image_test.go +++ b/libpod/image/image_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/containers/storage" + "github.com/opencontainers/go-digest" "github.com/stretchr/testify/assert" ) @@ -192,6 +193,51 @@ func TestImage_MatchRepoTag(t *testing.T) { cleanup(workdir, ir) } +// TestImage_RepoDigests tests RepoDigest generation. +func TestImage_RepoDigests(t *testing.T) { + dgst, err := digest.Parse("sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc") + if err != nil { + t.Fatal(err) + } + + for _, test := range []struct { + name string + names []string + expected []string + }{ + { + name: "empty", + names: []string{}, + expected: nil, + }, + { + name: "tagged", + names: []string{"docker.io/library/busybox:latest"}, + expected: []string{"docker.io/library/busybox@sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc"}, + }, + { + name: "digest", + names: []string{"docker.io/library/busybox@sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc"}, + expected: []string{"docker.io/library/busybox@sha256:7173b809ca12ec5dee4506cd86be934c4596dd234ee82c0662eac04a8c2c71dc"}, + }, + } { + t.Run(test.name, func(t *testing.T) { + image := &Image{ + image: &storage.Image{ + Names: test.names, + Digest: dgst, + }, + } + actual, err := image.RepoDigests() + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, test.expected, actual) + }) + } +} + // Test_splitString tests the splitString function in image that // takes input and splits on / and returns the last array item func Test_splitString(t *testing.T) { diff --git a/libpod/image/parts.go b/libpod/image/parts.go index 1509005e5..b2a69f26c 100644 --- a/libpod/image/parts.go +++ b/libpod/image/parts.go @@ -7,12 +7,12 @@ import ( "github.com/containers/image/docker/reference" ) -// imageParts describes the parts of an image's name -type imageParts struct { +// Parts describes the parts of an image's name +type Parts struct { transport string - registry string + Registry string name string - tag string + Tag string isTagged bool hasRegistry bool } @@ -22,10 +22,28 @@ func isRegistry(name string) bool { return strings.ContainsAny(name, ".:") || name == "localhost" } +// GetImageBaseName uses decompose and string splits to obtain the base +// name of an image. Doing this here because it beats changing the +// imageParts struct names to be exported as well. +func GetImageBaseName(input string) (string, error) { + decomposedImage, err := decompose(input) + if err != nil { + return "", err + } + splitImageName := strings.Split(decomposedImage.name, "/") + 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 -func decompose(input string) (imageParts, error) { +func decompose(input string) (Parts, error) { var ( - parts imageParts + parts Parts hasRegistry bool tag string ) @@ -44,7 +62,7 @@ func decompose(input string) (imageParts, error) { } registry := reference.Domain(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) { hasRegistry = true } else { @@ -53,27 +71,27 @@ func decompose(input string) (imageParts, error) { registry = "" } } - return imageParts{ - registry: registry, + return Parts{ + Registry: registry, hasRegistry: hasRegistry, name: imageName, - tag: tag, + Tag: tag, isTagged: isTagged, transport: DefaultTransport, }, nil } // assemble concatenates an image's parts into a string -func (ip *imageParts) assemble() string { - spec := fmt.Sprintf("%s:%s", ip.name, ip.tag) +func (ip *Parts) assemble() string { + spec := fmt.Sprintf("%s:%s", ip.name, ip.Tag) - if ip.registry != "" { - spec = fmt.Sprintf("%s/%s", ip.registry, spec) + if ip.Registry != "" { + spec = fmt.Sprintf("%s/%s", ip.Registry, spec) } return spec } // 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()) } diff --git a/libpod/image/parts_test.go b/libpod/image/parts_test.go index 518538f0b..1e01c85d3 100644 --- a/libpod/image/parts_test.go +++ b/libpod/image/parts_test.go @@ -55,9 +55,9 @@ func TestDecompose(t *testing.T) { } else { assert.NoError(t, err, 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.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.hasRegistry, parts.hasRegistry, c.input) assert.Equal(t, c.assembled, parts.assemble(), c.input) diff --git a/libpod/image/pull.go b/libpod/image/pull.go index 09935fe7c..203e94310 100644 --- a/libpod/image/pull.go +++ b/libpod/image/pull.go @@ -76,7 +76,7 @@ func (ir *Runtime) getPullRefPair(srcRef types.ImageReference, destName string) decomposedDest, err := decompose(destName) if err == nil && !decomposedDest.hasRegistry { // If the image doesn't have a registry, set it as the default repo - decomposedDest.registry = DefaultLocalRegistry + decomposedDest.Registry = DefaultLocalRegistry decomposedDest.hasRegistry = true destName = decomposedDest.assemble() } @@ -317,7 +317,7 @@ func (ir *Runtime) pullGoalFromPossiblyUnqualifiedName(inputName string) (*pullG } var refPairs []pullRefPair for _, registry := range searchRegistries { - decomposedImage.registry = registry + decomposedImage.Registry = registry imageName := decomposedImage.assembleWithTransport() if hasShaInInputName(inputName) { imageName = fmt.Sprintf("%s%s/%s", decomposedImage.transport, registry, inputName) diff --git a/libpod/image/utils.go b/libpod/image/utils.go index b944de1bb..135b47008 100644 --- a/libpod/image/utils.go +++ b/libpod/image/utils.go @@ -16,7 +16,7 @@ import ( // findImageInRepotags takes an imageParts struct and searches images' repotags for // 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 for _, image := range images { for _, name := range image.Names() { @@ -25,12 +25,12 @@ func findImageInRepotags(search imageParts, images []*Image) (*storage.Image, er if err != nil { continue } - if d.name == search.name && d.tag == search.tag { + if d.name == search.name && d.Tag == search.Tag { results = append(results, image.image) continue } // 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) continue } |