Skip to content

Commit

Permalink
Allow image to be saved without a reference name
Browse files Browse the repository at this point in the history
The docker-archive and oci-archive transport should allow the
destination of the image to be valid without the reference part also.
Format is transport:path[:reference] where reference is optional.
This is for the case where a user just wants to save/push an image with
the image ID only and not the name.
Creates archives with empty repotags.

Signed-off-by: umohnani8 <umohnani@redhat.com>
  • Loading branch information
umohnani8 committed May 15, 2018
1 parent 90bd556 commit a5264ad
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 28 deletions.
4 changes: 0 additions & 4 deletions docker/archive/dest.go
Expand Up @@ -17,10 +17,6 @@ type archiveImageDestination struct {
}

func newImageDestination(sys *types.SystemContext, ref archiveReference) (types.ImageDestination, error) {
if ref.destinationRef == nil {
return nil, errors.Errorf("docker-archive: destination reference not supplied (must be of form <path>:<reference:tag>)")
}

// ref.path can be either a pipe or a regular file
// in the case of a pipe, we require that we can open it for write
// in the case of a regular file, we don't want to overwrite any pre-existing file
Expand Down
4 changes: 3 additions & 1 deletion docker/archive/transport.go
Expand Up @@ -41,7 +41,9 @@ func (t archiveTransport) ValidatePolicyConfigurationScope(scope string) error {

// archiveReference is an ImageReference for Docker images.
type archiveReference struct {
destinationRef reference.NamedTagged // only used for destinations
// only used for destinations
// archiveReference.destinationRef is optional and can be nil for destinations as well.
destinationRef reference.NamedTagged
path string
}

Expand Down
3 changes: 2 additions & 1 deletion docker/archive/transport_test.go
Expand Up @@ -169,7 +169,8 @@ func TestReferenceNewImageDestination(t *testing.T) {
ref, err := ParseReference(filepath.Join(tmpDir, "no-reference"))
require.NoError(t, err)
dest, err := ref.NewImageDestination(context.Background(), nil)
assert.Error(t, err)
assert.NoError(t, err)
dest.Close()

ref, err = ParseReference(filepath.Join(tmpDir, "with-reference") + "busybox:latest")
require.NoError(t, err)
Expand Down
31 changes: 20 additions & 11 deletions docker/tarfile/dest.go
Expand Up @@ -23,23 +23,25 @@ import (

// Destination is a partial implementation of types.ImageDestination for writing to an io.Writer.
type Destination struct {
writer io.Writer
tar *tar.Writer
reference reference.NamedTagged
repoTags []reference.NamedTagged
writer io.Writer
tar *tar.Writer
repoTags []reference.NamedTagged
// Other state.
blobs map[digest.Digest]types.BlobInfo // list of already-sent blobs
config []byte
}

// NewDestination returns a tarfile.Destination for the specified io.Writer.
func NewDestination(dest io.Writer, ref reference.NamedTagged) *Destination {
repoTags := []reference.NamedTagged{}
if ref != nil {
repoTags = append(repoTags, ref)
}
return &Destination{
writer: dest,
tar: tar.NewWriter(dest),
reference: ref,
repoTags: []reference.NamedTagged{ref},
blobs: make(map[digest.Digest]types.BlobInfo),
writer: dest,
tar: tar.NewWriter(dest),
repoTags: repoTags,
blobs: make(map[digest.Digest]types.BlobInfo),
}
}

Expand Down Expand Up @@ -168,8 +170,15 @@ func (d *Destination) ReapplyBlob(ctx context.Context, info types.BlobInfo) (typ
}

func (d *Destination) createRepositoriesFile(rootLayerID string) error {
repositories := map[string]map[string]string{
d.reference.Name(): {d.reference.Tag(): rootLayerID}}
repositories := map[string]map[string]string{}
for _, repoTag := range d.repoTags {
if val, ok := repositories[repoTag.Name()]; ok {
val[repoTag.Tag()] = rootLayerID
} else {
repositories[repoTag.Name()] = map[string]string{repoTag.Tag(): rootLayerID}
}
}

b, err := json.Marshal(repositories)
if err != nil {
return errors.Wrap(err, "Error marshaling repositories")
Expand Down
14 changes: 4 additions & 10 deletions oci/layout/oci_dest.go
Expand Up @@ -25,10 +25,6 @@ type ociImageDestination struct {

// newImageDestination returns an ImageDestination for writing to an existing directory.
func newImageDestination(sys *types.SystemContext, ref ociReference) (types.ImageDestination, error) {
if ref.image == "" {
return nil, errors.Errorf("cannot save image with empty reference name (syntax must be of form <transport>:<path>:<reference>)")
}

var index *imgspecv1.Index
if indexExists(ref) {
var err error
Expand Down Expand Up @@ -217,13 +213,11 @@ func (d *ociImageDestination) PutManifest(ctx context.Context, m []byte) error {
return err
}

if d.ref.image == "" {
return errors.Errorf("cannot save image with empyt image.ref.name")
if d.ref.image != "" {
annotations := make(map[string]string)
annotations["org.opencontainers.image.ref.name"] = d.ref.image
desc.Annotations = annotations
}

annotations := make(map[string]string)
annotations["org.opencontainers.image.ref.name"] = d.ref.image
desc.Annotations = annotations
desc.Platform = &imgspecv1.Platform{
Architecture: runtime.GOARCH,
OS: runtime.GOOS,
Expand Down
4 changes: 3 additions & 1 deletion oci/layout/oci_transport.go
Expand Up @@ -55,7 +55,9 @@ type ociReference struct {
// (But in general, we make no attempt to be completely safe against concurrent hostile filesystem modifications.)
dir string // As specified by the user. May be relative, contain symlinks, etc.
resolvedDir string // Absolute path with no symlinks, at least at the time of its creation. Primarily used for policy namespaces.
image string // If image=="", it means the only image in the index.json is used
// If image=="", it means the "only image" in the index.json is used in the case it is a source
// for destinations, the image name annotation "image.ref.name" is not added to the index.json
image string
}

// ParseReference converts a string, which should not start with the ImageTransport.Name prefix, into an OCI ImageReference.
Expand Down

0 comments on commit a5264ad

Please sign in to comment.