Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions directory/directory_dest.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,33 @@ import (
"os"

"github.com/containers/image/types"
"github.com/docker/docker/reference"
)

type dirImageDestination struct {
dir string
ref dirReference
}

// NewImageDestination returns an ImageDestination for writing to an existing directory.
func NewImageDestination(dir string) types.ImageDestination {
return &dirImageDestination{dir}
// newImageDestination returns an ImageDestination for writing to an existing directory.
func newImageDestination(ref dirReference) types.ImageDestination {
return &dirImageDestination{ref}
}

func (d *dirImageDestination) CanonicalDockerReference() reference.Named {
return nil
// Reference returns the reference used to set up this destination. Note that this should directly correspond to user's intent,
// e.g. it should use the public hostname instead of the result of resolving CNAMEs or following redirects.
func (d *dirImageDestination) Reference() types.ImageReference {
return d.ref
}

func (d *dirImageDestination) SupportedManifestMIMETypes() []string {
return nil
}

func (d *dirImageDestination) PutManifest(manifest []byte) error {
return ioutil.WriteFile(manifestPath(d.dir), manifest, 0644)
return ioutil.WriteFile(manifestPath(d.ref.path), manifest, 0644)
}

func (d *dirImageDestination) PutBlob(digest string, stream io.Reader) error {
layerFile, err := os.Create(layerPath(d.dir, digest))
layerFile, err := os.Create(layerPath(d.ref.path, digest))
if err != nil {
return err
}
Expand All @@ -47,7 +48,7 @@ func (d *dirImageDestination) PutBlob(digest string, stream io.Reader) error {

func (d *dirImageDestination) PutSignatures(signatures [][]byte) error {
for i, sig := range signatures {
if err := ioutil.WriteFile(signaturePath(d.dir, i), sig, 0644); err != nil {
if err := ioutil.WriteFile(signaturePath(d.ref.path, i), sig, 0644); err != nil {
return err
}
}
Expand Down
27 changes: 12 additions & 15 deletions directory/directory_src.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,41 +7,38 @@ import (
"os"

"github.com/containers/image/types"
"github.com/docker/docker/reference"
)

type dirImageSource struct {
dir string
ref dirReference
}

// NewImageSource returns an ImageSource reading from an existing directory.
func NewImageSource(dir string) types.ImageSource {
return &dirImageSource{dir}
// newImageSource returns an ImageSource reading from an existing directory.
func newImageSource(ref dirReference) types.ImageSource {
return &dirImageSource{ref}
}

// IntendedDockerReference returns the Docker reference for this image, _as specified by the user_
// (not as the image itself, or its underlying storage, claims). Should be fully expanded, i.e. !reference.IsNameOnly.
// This can be used e.g. to determine which public keys are trusted for this image.
// May be nil if unknown.
func (s *dirImageSource) IntendedDockerReference() reference.Named {
return nil
// Reference returns the reference used to set up this source, _as specified by the user_
// (not as the image itself, or its underlying storage, claims). This can be used e.g. to determine which public keys are trusted for this image.
func (s *dirImageSource) Reference() types.ImageReference {
return s.ref
}

// it's up to the caller to determine the MIME type of the returned manifest's bytes
func (s *dirImageSource) GetManifest(_ []string) ([]byte, string, error) {
m, err := ioutil.ReadFile(manifestPath(s.dir))
m, err := ioutil.ReadFile(manifestPath(s.ref.path))
if err != nil {
return nil, "", err
}
return m, "", err
}

func (s *dirImageSource) GetBlob(digest string) (io.ReadCloser, int64, error) {
r, err := os.Open(layerPath(s.dir, digest))
r, err := os.Open(layerPath(s.ref.path, digest))
if err != nil {
return nil, 0, nil
}
fi, err := os.Stat(layerPath(s.dir, digest))
fi, err := os.Stat(layerPath(s.ref.path, digest))
if err != nil {
return nil, 0, nil
}
Expand All @@ -51,7 +48,7 @@ func (s *dirImageSource) GetBlob(digest string) (io.ReadCloser, int64, error) {
func (s *dirImageSource) GetSignatures() ([][]byte, error) {
signatures := [][]byte{}
for i := 0; ; i++ {
signature, err := ioutil.ReadFile(signaturePath(s.dir, i))
signature, err := ioutil.ReadFile(signaturePath(s.ref.path, i))
if err != nil {
if os.IsNotExist(err) {
break
Expand Down
57 changes: 34 additions & 23 deletions directory/directory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,41 +10,47 @@ import (
"github.com/stretchr/testify/require"
)

func TestCanonicalDockerReference(t *testing.T) {
dest := NewImageDestination("/path/to/somewhere")
ref := dest.CanonicalDockerReference()
assert.Nil(t, ref)
func TestDestinationReference(t *testing.T) {
ref, tmpDir := refToTempDir(t)
defer os.RemoveAll(tmpDir)

dest, err := ref.NewImageDestination("", true)
require.NoError(t, err)
ref2 := dest.Reference()
assert.Equal(t, tmpDir, ref2.StringWithinTransport())
}

func TestGetPutManifest(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "put-manifest")
require.NoError(t, err)
ref, tmpDir := refToTempDir(t)
defer os.RemoveAll(tmpDir)

man := []byte("test-manifest")
dest := NewImageDestination(tmpDir)
dest, err := ref.NewImageDestination("", true)
require.NoError(t, err)
err = dest.PutManifest(man)
assert.NoError(t, err)

src := NewImageSource(tmpDir)
src, err := ref.NewImageSource("", true)
require.NoError(t, err)
m, mt, err := src.GetManifest(nil)
assert.NoError(t, err)
assert.Equal(t, man, m)
assert.Equal(t, "", mt)
}

func TestGetPutBlob(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "put-blob")
require.NoError(t, err)
ref, tmpDir := refToTempDir(t)
defer os.RemoveAll(tmpDir)

digest := "digest-test"
blob := []byte("test-blob")
dest := NewImageDestination(tmpDir)
dest, err := ref.NewImageDestination("", true)
require.NoError(t, err)
err = dest.PutBlob(digest, bytes.NewReader(blob))
assert.NoError(t, err)

src := NewImageSource(tmpDir)
src, err := ref.NewImageSource("", true)
require.NoError(t, err)
rc, size, err := src.GetBlob(digest)
assert.NoError(t, err)
defer rc.Close()
Expand All @@ -55,36 +61,41 @@ func TestGetPutBlob(t *testing.T) {
}

func TestGetPutSignatures(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "put-signatures")
require.NoError(t, err)
ref, tmpDir := refToTempDir(t)
defer os.RemoveAll(tmpDir)

dest := NewImageDestination(tmpDir)
dest, err := ref.NewImageDestination("", true)
require.NoError(t, err)
signatures := [][]byte{
[]byte("sig1"),
[]byte("sig2"),
}
err = dest.PutSignatures(signatures)
assert.NoError(t, err)

src := NewImageSource(tmpDir)
src, err := ref.NewImageSource("", true)
require.NoError(t, err)
sigs, err := src.GetSignatures()
assert.NoError(t, err)
assert.Equal(t, signatures, sigs)
}

func TestDelete(t *testing.T) {
tmpDir, err := ioutil.TempDir("", "delete")
require.NoError(t, err)
ref, tmpDir := refToTempDir(t)
defer os.RemoveAll(tmpDir)

src := NewImageSource(tmpDir)
src, err := ref.NewImageSource("", true)
require.NoError(t, err)
err = src.Delete()
assert.Error(t, err)
}

func TestIntendedDockerReference(t *testing.T) {
src := NewImageSource("/path/to/somewhere")
ref := src.IntendedDockerReference()
assert.Nil(t, ref)
func TestSourceReference(t *testing.T) {
ref, tmpDir := refToTempDir(t)
defer os.RemoveAll(tmpDir)

src, err := ref.NewImageSource("", true)
require.NoError(t, err)
ref2 := src.Reference()
assert.Equal(t, tmpDir, ref2.StringWithinTransport())
}
73 changes: 73 additions & 0 deletions directory/directory_transport.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package directory

import (
"github.com/containers/image/image"
"github.com/containers/image/types"
"github.com/docker/docker/reference"
)

// Transport is an ImageTransport for directory paths.
var Transport = dirTransport{}

type dirTransport struct{}

func (t dirTransport) Name() string {
return "dir"
}

// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an ImageReference.
func (t dirTransport) ParseReference(reference string) (types.ImageReference, error) {
return NewReference(reference), nil
}

// dirReference is an ImageReference for directory paths.
type dirReference struct {
// Note that the interpretation of paths below depends on the underlying filesystem state, which may change under us at any time!
path string // As specified by the user. May be relative, contain symlinks, etc.
}

// There is no directory.ParseReference because it is rather pointless.
// Callers who need a transport-independent interface will go through
// dirTransport.ParseReference; callers who intentionally deal with directories
// can use directory.NewReference.

// NewReference returns a directory reference for a specified path.
func NewReference(path string) types.ImageReference {
return dirReference{path: path}
}

func (ref dirReference) Transport() types.ImageTransport {
return Transport
}

// StringWithinTransport returns a string representation of the reference, which MUST be such that
// reference.Transport().ParseReference(reference.StringWithinTransport()) returns an equivalent reference.
// NOTE: The returned string is not promised to be equal to the original input to ParseReference;
// e.g. default attribute values omitted by the user may be filled in in the return value, or vice versa.
// WARNING: Do not use the return value in the UI to describe an image, it does not contain the Transport().Name() prefix.
func (ref dirReference) StringWithinTransport() string {
return ref.path
}

// DockerReference returns a Docker reference associated with this reference
// (fully explicit, i.e. !reference.IsNameOnly, but reflecting user intent,
// not e.g. after redirect or alias processing), or nil if unknown/not applicable.
func (ref dirReference) DockerReference() reference.Named {
return nil
}

// NewImage returns a types.Image for this reference.
func (ref dirReference) NewImage(certPath string, tlsVerify bool) (types.Image, error) {
src := newImageSource(ref)
return image.FromSource(src, nil), nil
}

// NewImageSource returns a types.ImageSource for this reference.
func (ref dirReference) NewImageSource(certPath string, tlsVerify bool) (types.ImageSource, error) {
return newImageSource(ref), nil
}

// NewImageDestination returns a types.ImageDestination for this reference.
func (ref dirReference) NewImageDestination(certPath string, tlsVerify bool) (types.ImageDestination, error) {
return newImageDestination(ref), nil
}
95 changes: 95 additions & 0 deletions directory/directory_transport_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package directory

import (
"io/ioutil"
"os"
"testing"

"github.com/containers/image/types"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

func TestTransportName(t *testing.T) {
assert.Equal(t, "dir", Transport.Name())
}

func TestTransportParseReference(t *testing.T) {
testNewReference(t, Transport.ParseReference)
}

func TestNewReference(t *testing.T) {
testNewReference(t, func(ref string) (types.ImageReference, error) {
return NewReference(ref), nil
})
}

// testNewReference is a test shared for Transport.ParseReference and NewReference.
func testNewReference(t *testing.T, fn func(string) (types.ImageReference, error)) {
tmpDir, err := ioutil.TempDir("", "dir-transport-test")
require.NoError(t, err)
defer os.RemoveAll(tmpDir)

for _, path := range []string{
"/",
"/etc",
tmpDir,
"relativepath",
tmpDir + "/thisdoesnotexist",
} {
ref, err := fn(path)
require.NoError(t, err, path)
dirRef, ok := ref.(dirReference)
require.True(t, ok)
assert.Equal(t, path, dirRef.path, path)
}
}

// refToTempDir creates a temporary directory and returns a reference to it.
// The caller should
// defer os.RemoveAll(tmpDir)
func refToTempDir(t *testing.T) (ref types.ImageReference, tmpDir string) {
tmpDir, err := ioutil.TempDir("", "dir-transport-test")
require.NoError(t, err)
ref = NewReference(tmpDir)
return ref, tmpDir
}

func TestReferenceTransport(t *testing.T) {
ref, tmpDir := refToTempDir(t)
defer os.RemoveAll(tmpDir)
assert.Equal(t, Transport, ref.Transport())
}

func TestReferenceStringWithinTransport(t *testing.T) {
ref, tmpDir := refToTempDir(t)
defer os.RemoveAll(tmpDir)
assert.Equal(t, tmpDir, ref.StringWithinTransport())
}

func TestReferenceDockerReference(t *testing.T) {
ref, tmpDir := refToTempDir(t)
defer os.RemoveAll(tmpDir)
assert.Nil(t, ref.DockerReference())
}

func TestReferenceNewImage(t *testing.T) {
ref, tmpDir := refToTempDir(t)
defer os.RemoveAll(tmpDir)
_, err := ref.NewImage("/this/doesn't/exist", true)
assert.NoError(t, err)
}

func TestReferenceNewImageSource(t *testing.T) {
ref, tmpDir := refToTempDir(t)
defer os.RemoveAll(tmpDir)
_, err := ref.NewImageSource("/this/doesn't/exist", true)
assert.NoError(t, err)
}

func TestReferenceNewImageDestination(t *testing.T) {
ref, tmpDir := refToTempDir(t)
defer os.RemoveAll(tmpDir)
_, err := ref.NewImageDestination("/this/doesn't/exist", true)
assert.NoError(t, err)
}
Loading