Add a "copy" command for copying images

This copies an image from ImageSource to ImageDestination, e.g.

skopeo copy atomic:mitr/busybox:latest dir:t-down # pull
skopeo copy dir:t-up atomic:mitr/busybox:latest # push
This commit is contained in:
Miloslav Trmač 2016-04-21 20:33:05 +02:00
parent 56f9c987a2
commit 2e48975b8b
3 changed files with 92 additions and 0 deletions

80
copy.go Normal file
View File

@ -0,0 +1,80 @@
package main
import (
"encoding/json"
"fmt"
"github.com/Sirupsen/logrus"
"github.com/codegangsta/cli"
)
// FIXME: Also handle schema2, and put this elsewhere:
// docker.go contains similar code, more sophisticated
// (at the very least the deduplication should be reused from there).
func manifestLayers(manifest []byte) ([]string, error) {
man := manifestSchema1{}
if err := json.Unmarshal(manifest, &man); err != nil {
return nil, err
}
layers := []string{}
for _, layer := range man.FSLayers {
layers = append(layers, layer.BlobSum)
}
return layers, nil
}
func copyHandler(context *cli.Context) {
if len(context.Args()) != 2 {
logrus.Fatal("Usage: copy source destination")
}
src, err := parseImageSource(context, context.Args()[0])
if err != nil {
logrus.Fatalf("Error initializing %s: %s", context.Args()[0], err.Error())
}
dest, err := parseImageDestination(context, context.Args()[1])
if err != nil {
logrus.Fatalf("Error initializing %s: %s", context.Args()[1], err.Error())
}
manifest, digest, err := src.GetManifest()
if err != nil {
logrus.Fatalf("Error reading manifest: %s", err.Error())
}
fmt.Printf("Canonical manifest digest: %s\n", digest)
layers, err := manifestLayers(manifest)
if err != nil {
logrus.Fatalf("Error parsing manifest: %s", err.Error())
}
for _, layer := range layers {
stream, err := src.GetLayer(layer)
if err != nil {
logrus.Fatalf("Error reading layer %s: %s", layer, err.Error())
}
defer stream.Close()
if err := dest.PutLayer(layer, stream); err != nil {
logrus.Fatalf("Error writing layer: %s", err.Error())
}
}
sigs, err := src.GetSignatures()
if err != nil {
logrus.Fatalf("Error reading signatures: %s", err.Error())
}
if err := dest.PutSignatures(sigs); err != nil {
logrus.Fatalf("Error writing signatures: %s", err.Error())
}
// FIXME: We need to call PutManifest after PutLayer and PutSignatures. This seems ugly; move to a "set properties" + "commit" model?
if err := dest.PutManifest(manifest); err != nil {
logrus.Fatalf("Error writing manifest: %s", err.Error())
}
}
var copyCmd = cli.Command{
Name: "copy",
Action: copyHandler,
}

View File

@ -60,6 +60,7 @@ func main() {
return nil
}
app.Commands = []cli.Command{
copyCmd,
inspectCmd,
layersCmd,
standaloneSignCmd,

View File

@ -8,6 +8,8 @@
.SH NAME
skopeo \(em Inspect Docker images and repositories on registries
.SH SYNOPSIS
\fBskopeo copy\fR source-location destination-location
.PP
\fBskopeo inspect\fR image-name [\fB--raw\fR]
.PP
\fBskopeo layers\fR image-name
@ -45,6 +47,15 @@ Show help
.B "--version, -v"
print the version number
.SH COMMANDS
.TP
.B copy
Copy an image (manifest, filesystem layers, signatures) from one location to another.
.sp
.B source-location
and
.B destination-location
can be \fBdocker://\fRdocker-reference, \fBdir:\fRlocal-path, or \fBatomic:\fRimagestream-name\fB:\fRtag .
.TP
.B inspect
Return low-level information on images in a registry