Skip to content

Commit

Permalink
add Validate function
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewrobertson committed Apr 3, 2023
1 parent a6c0ed9 commit 935fe02
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 2 deletions.
4 changes: 4 additions & 0 deletions fakes/image.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,10 @@ func (i *Image) Found() bool {
return !i.deleted
}

func (i *Image) Valid() bool {
return !i.deleted
}

func (i *Image) AnnotateRefName(refName string) error {
i.refName = refName
return nil
Expand Down
2 changes: 2 additions & 0 deletions image.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ type Image interface {
Env(key string) (string, error)
// Found tells whether the image exists in the repository by `Name()`.
Found() bool
// Valid returns true if the image is well formed (e.g. all manifest layers exist on the registry).
Valid() bool
GetAnnotateRefName() (string, error)
// GetLayer retrieves layer by diff id. Returns a reader of the uncompressed contents of the layer.
GetLayer(diffID string) (io.ReadCloser, error)
Expand Down
4 changes: 4 additions & 0 deletions layout/layout.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ func (i *Image) Found() bool {
return ImageExists(i.path)
}

func (i *Image) Valid() bool {
return i.Found()
}

func ImageExists(path string) bool {
if !pathExists(path) {
return false
Expand Down
47 changes: 47 additions & 0 deletions layout/layout_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -912,6 +912,53 @@ func testImage(t *testing.T, when spec.G, it spec.S) {
})
})

when("#Valid", func() {
var image *layout.Image

it.Before(func() {
imagePath = filepath.Join(tmpDir, "found-image")
image, err = layout.NewImage(imagePath)
h.AssertNil(t, err)
})

it.After(func() {
os.RemoveAll(imagePath)
})

when("image doesn't exist on disk", func() {
it.Before(func() {
imagePath = filepath.Join(tmpDir, "non-exist-image")
image, err = layout.NewImage(imagePath)
h.AssertNil(t, err)
})

it("returns false", func() {
h.AssertTrue(t, func() bool {
return !image.Found()
})
})
})

when("image exists on disk", func() {
it.Before(func() {
imagePath = filepath.Join(testDataDir, "my-previous-image")
image, err = layout.NewImage(imagePath)
h.AssertNil(t, err)
})

it.After(func() {
// We don't want to delete testdata/my-previous-image
imagePath = ""
})

it("returns true", func() {
h.AssertTrue(t, func() bool {
return image.Found()
})
})
})
})

when("#Delete", func() {
var image *layout.Image

Expand Down
4 changes: 4 additions & 0 deletions local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ func (i *Image) Found() bool {
return i.inspect.ID != ""
}

func (i *Image) Valid() bool {
return i.Found()
}

func (i *Image) GetAnnotateRefName() (string, error) {
return "", nil
}
Expand Down
19 changes: 19 additions & 0 deletions remote/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
"github.com/google/go-containerregistry/pkg/v1/tarball"
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/google/go-containerregistry/pkg/v1/validate"
"github.com/pkg/errors"

"github.com/buildpacks/imgutil"
Expand Down Expand Up @@ -92,6 +93,7 @@ func (i *Image) Env(key string) (string, error) {

func (i *Image) Found() bool {
_, err := i.found()

return err == nil
}

Expand All @@ -104,6 +106,23 @@ func (i *Image) found() (*v1.Descriptor, error) {
return remote.Head(ref, remote.WithAuth(auth), remote.WithTransport(http.DefaultTransport))
}

func (i *Image) Valid() bool {
return i.valid() == nil
}

func (i *Image) valid() error {
reg := getRegistry(i.repoName, i.registrySettings)
ref, auth, err := referenceForRepoName(i.keychain, i.repoName, reg.insecure)
if err != nil {
return err
}
img, err := remote.Image(ref, remote.WithAuth(auth), remote.WithTransport(http.DefaultTransport))
if err != nil {
return err
}
return validate.Image(img, validate.Fast)
}

func (i *Image) GetAnnotateRefName() (string, error) {
// TODO issue https://github.com/buildpacks/imgutil/issues/178
return "", errors.New("not yet implemented")
Expand Down
47 changes: 47 additions & 0 deletions remote/remote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1595,6 +1595,53 @@ func testImage(t *testing.T, when spec.G, it spec.S) {
})
})

when("#Valid", func() {
when("it exists", func() {
it("returns true", func() {
origImage, err := remote.NewImage(repoName, authn.DefaultKeychain)
h.AssertNil(t, err)
h.AssertNil(t, origImage.Save())

image, err := remote.NewImage(repoName, authn.DefaultKeychain)
h.AssertNil(t, err)

h.AssertEq(t, image.Valid(), true)
})
})

when("it is corrupt", func() {
it("returns false", func() {
origImage, err := remote.NewImage(repoName, authn.DefaultKeychain)
h.AssertNil(t, err)
tarPath, _, _ := h.RandomLayer(t, t.TempDir())
defer os.Remove(tarPath)
h.AssertNil(t, origImage.AddLayer(tarPath))
h.AssertNil(t, origImage.Save())

// delete the top layer from the registry
layers, err := origImage.UnderlyingImage().Layers()
h.AssertNil(t, err)
digest, err := layers[0].Digest()
h.AssertNil(t, err)
h.DeleteRegistryBlob(t, repoName, digest, dockerRegistry.EncodedAuth())

image, err := remote.NewImage(repoName, authn.DefaultKeychain)
h.AssertNil(t, err)

h.AssertEq(t, image.Valid(), false)
})
})

when("it does not exist", func() {
it("returns false", func() {
image, err := remote.NewImage(repoName, authn.DefaultKeychain)
h.AssertNil(t, err)

h.AssertEq(t, image.Valid(), false)
})
})
})

when("#Delete", func() {
when("it exists", func() {
var img imgutil.Image
Expand Down
4 changes: 2 additions & 2 deletions testhelpers/docker_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func (r *DockerRegistry) Start(t *testing.T) {

if r.username != "" {
// Write Docker config and configure auth headers
writeDockerConfig(t, r.DockerDirectory, r.Host, r.Port, r.encodedAuth())
writeDockerConfig(t, r.DockerDirectory, r.Host, r.Port, r.EncodedAuth())
}
}

Expand Down Expand Up @@ -294,7 +294,7 @@ func DockerHostname(t *testing.T) string {
return "localhost"
}

func (r *DockerRegistry) encodedAuth() string {
func (r *DockerRegistry) EncodedAuth() string {
return base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", r.username, r.password)))
}

Expand Down
22 changes: 22 additions & 0 deletions testhelpers/testhelpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"io/ioutil"
"math/rand"
"net/http"
"net/url"
"os"
"path/filepath"
"regexp"
Expand Down Expand Up @@ -227,6 +228,27 @@ func PushImage(t *testing.T, dockerCli dockercli.CommonAPIClient, refStr string)
AssertNil(t, err)
}

// DeleteRegistryBlob deletes the blob with the given digest from the registry by issuing an HTTP DELETE request.
func DeleteRegistryBlob(t *testing.T, repoName string, digest v1.Hash, encodedAuth string) {
ref, err := name.ParseReference(repoName, name.WeakValidation)
AssertNil(t, err)
url := url.URL{
Scheme: ref.Context().Registry.Scheme(),
Host: ref.Context().RegistryStr(),
Path: fmt.Sprintf("/v2/%s/blobs/%s", ref.Context().RepositoryStr(), digest),
}
req, err := http.NewRequest(http.MethodDelete, url.String(), nil)
req.Header.Add("Authorization", "Basic "+encodedAuth)
client := &http.Client{}
resp, err := client.Do(req)
AssertNil(t, err)
defer resp.Body.Close()

_, err = io.ReadAll(resp.Body)
AssertNil(t, err)
AssertEq(t, resp.StatusCode, http.StatusAccepted)
}

func ImageID(t *testing.T, repoName string) string {
t.Helper()
inspect, _, err := DockerCli(t).ImageInspectWithRaw(context.Background(), repoName)
Expand Down

0 comments on commit 935fe02

Please sign in to comment.