Skip to content

Commit

Permalink
add FindManifests (#828)
Browse files Browse the repository at this point in the history
Signed-off-by: Avi Deitcher <avi@deitcher.net>
Co-authored-by: jonjohnsonjr <jonjohnson@google.com>
Signed-off-by: Avi Deitcher <avi@deitcher.net>

Co-authored-by: jonjohnsonjr <jonjohnson@google.com>
  • Loading branch information
deitch and jonjohnsonjr committed Nov 20, 2020
1 parent 3904ad8 commit 93b58dc
Show file tree
Hide file tree
Showing 4 changed files with 287 additions and 0 deletions.
85 changes: 85 additions & 0 deletions pkg/v1/partial/index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright 2020 Google LLC All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package partial

import (
"fmt"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/match"
)

// FindManifests given a v1.ImageIndex, find the manifests that fit the matcher.
func FindManifests(index v1.ImageIndex, matcher match.Matcher) ([]v1.Descriptor, error) {
// get the actual manifest list
indexManifest, err := index.IndexManifest()
if err != nil {
return nil, fmt.Errorf("unable to get raw index: %v", err)
}
manifests := []v1.Descriptor{}
// try to get the root of our image
for _, manifest := range indexManifest.Manifests {
if matcher(manifest) {
manifests = append(manifests, manifest)
}
}
return manifests, nil
}

// FindImages given a v1.ImageIndex, find the images that fit the matcher. If a Descriptor
// matches the provider Matcher, but the referenced item is not an Image, ignores it.
// Only returns those that match the Matcher and are images.
func FindImages(index v1.ImageIndex, matcher match.Matcher) ([]v1.Image, error) {
matches := []v1.Image{}
manifests, err := FindManifests(index, matcher)
if err != nil {
return nil, err
}
for _, desc := range manifests {
// if it is not an image, ignore it
if !desc.MediaType.IsImage() {
continue
}
img, err := index.Image(desc.Digest)
if err != nil {
return nil, err
}
matches = append(matches, img)
}
return matches, nil
}

// FindIndexes given a v1.ImageIndex, find the indexes that fit the matcher. If a Descriptor
// matches the provider Matcher, but the referenced item is not an Index, ignores it.
// Only returns those that match the Matcher and are indexes.
func FindIndexes(index v1.ImageIndex, matcher match.Matcher) ([]v1.ImageIndex, error) {
matches := []v1.ImageIndex{}
manifests, err := FindManifests(index, matcher)
if err != nil {
return nil, err
}
for _, desc := range manifests {
if !desc.MediaType.IsIndex() {
continue
}
// if it is not an index, ignore it
idx, err := index.ImageIndex(desc.Digest)
if err != nil {
return nil, err
}
matches = append(matches, idx)
}
return matches, nil
}
122 changes: 122 additions & 0 deletions pkg/v1/partial/index_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
// Copyright 2020 Google LLC All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package partial_test

import (
"testing"

v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/mutate"
"github.com/google/go-containerregistry/pkg/v1/partial"
"github.com/google/go-containerregistry/pkg/v1/random"
"github.com/google/go-containerregistry/pkg/v1/types"
)

func TestFindManifests(t *testing.T) {

ii, err := random.Index(100, 5, 6) // random image of 6 manifests, each having 5 layers of size 100
if err != nil {
t.Fatal("could not create random index:", err)
}
m, _ := ii.IndexManifest()
digest := m.Manifests[0].Digest

matcher := func(desc v1.Descriptor) bool {
return desc.Digest != digest
}

descriptors, err := partial.FindManifests(ii, matcher)
expected := len(m.Manifests) - 1
switch {
case err != nil:
t.Error("unexpected error:", err)
case len(descriptors) != expected:
t.Errorf("failed on manifests, actual %d, expected %d", len(descriptors), expected)
}
}

func TestFindImages(t *testing.T) {
// create our imageindex with which to work
ii, err := random.Index(100, 5, 6) // random image of 6 manifests, each having 5 layers of size 100
if err != nil {
t.Fatal("could not create random index:", err)
}
m, _ := ii.IndexManifest()
digest := m.Manifests[0].Digest

matcher := func(desc v1.Descriptor) bool {
return desc.Digest != digest
}
images, err := partial.FindImages(ii, matcher)
expected := len(m.Manifests) - 1
switch {
case err != nil:
t.Error("unexpected error:", err)
case len(images) != expected:
t.Errorf("failed on images, actual %d, expected %d", len(images), expected)
}
}

func TestFindIndexes(t *testing.T) {
// there is no utility to generate an index of indexes, so we need to create one
// base index
var (
indexCount = 5
imageCount = 7
)
base, err := random.Index(0, 0, 0)
if err != nil {
t.Fatal("error creating random index:", err)
}
// we now have 5 indexes and 5 images, so wrap them into a single index
adds := []mutate.IndexAddendum{}
for i := 0; i < indexCount; i++ {
ii, err := random.Index(100, 5, 6)
if err != nil {
t.Fatalf("%d: unable to create random index: %v", i, err)
}
adds = append(adds, mutate.IndexAddendum{
Add: ii,
Descriptor: v1.Descriptor{
MediaType: types.OCIImageIndex,
},
})
}
for i := 0; i < imageCount; i++ {
img, err := random.Image(100, 5)
if err != nil {
t.Fatalf("%d: unable to create random image: %v", i, err)
}
adds = append(adds, mutate.IndexAddendum{
Add: img,
Descriptor: v1.Descriptor{
MediaType: types.OCIManifestSchema1,
},
})
}

// just see if it finds all of the indexes
matcher := func(desc v1.Descriptor) bool {
return true
}
index := mutate.AppendManifests(base, adds...)
idxes, err := partial.FindIndexes(index, matcher)
switch {
case err != nil:
t.Error("unexpected error:", err)
case len(idxes) != indexCount:
t.Errorf("failed on index, actual %d, expected %d", len(idxes), indexCount)
}
}
18 changes: 18 additions & 0 deletions pkg/v1/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,21 @@ func (m MediaType) IsDistributable() bool {
}
return true
}

// IsImage returns true if the mediaType represents an image manifest, as opposed to something else, like an index.
func (m MediaType) IsImage() bool {
switch m {
case OCIManifestSchema1, DockerManifestSchema2:
return true
}
return false
}

// IsIndex returns true if the mediaType represents an index, as opposed to something else, like an image.
func (m MediaType) IsIndex() bool {
switch m {
case OCIImageIndex, DockerManifestList:
return true
}
return false
}
62 changes: 62 additions & 0 deletions pkg/v1/types/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,65 @@ func TestIsDistributable(t *testing.T) {
}
}
}

func TestIsImage(t *testing.T) {
for _, mt := range []MediaType{
OCIManifestSchema1, DockerManifestSchema2,
} {
if !mt.IsImage() {
t.Errorf("%s: should be image", mt)
}
}

for _, mt := range []MediaType{
OCIContentDescriptor,
OCIImageIndex,
OCIConfigJSON,
OCILayer,
OCIRestrictedLayer,
OCIUncompressedLayer,
OCIUncompressedRestrictedLayer,

DockerManifestList,
DockerLayer,
DockerConfigJSON,
DockerPluginConfig,
DockerForeignLayer,
DockerUncompressedLayer,
} {
if mt.IsImage() {
t.Errorf("%s: should not be image", mt)
}
}
}

func TestIsIndex(t *testing.T) {
for _, mt := range []MediaType{
OCIImageIndex, DockerManifestList,
} {
if !mt.IsIndex() {
t.Errorf("%s: should be index", mt)
}
}

for _, mt := range []MediaType{
OCIContentDescriptor,
OCIConfigJSON,
OCILayer,
OCIRestrictedLayer,
OCIUncompressedLayer,
OCIUncompressedRestrictedLayer,
OCIManifestSchema1,

DockerManifestSchema2,
DockerLayer,
DockerConfigJSON,
DockerPluginConfig,
DockerForeignLayer,
DockerUncompressedLayer,
} {
if mt.IsIndex() {
t.Errorf("%s: should not be index", mt)
}
}
}

0 comments on commit 93b58dc

Please sign in to comment.