Update kpod load and save for oci-archive

Signed-off-by: umohnani8 <umohnani@redhat.com>
This commit is contained in:
umohnani8 2017-08-02 16:32:44 -04:00
parent f9387aca28
commit 79c09d4343
8 changed files with 126 additions and 33 deletions

View file

@ -7,7 +7,6 @@ import (
"io/ioutil" "io/ioutil"
"github.com/containers/storage" "github.com/containers/storage"
"github.com/kubernetes-incubator/cri-o/libpod/common"
"github.com/kubernetes-incubator/cri-o/libpod/images" "github.com/kubernetes-incubator/cri-o/libpod/images"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -16,6 +15,7 @@ import (
type loadOptions struct { type loadOptions struct {
input string input string
quiet bool quiet bool
image string
} }
var ( var (
@ -54,12 +54,15 @@ func loadCmd(c *cli.Context) error {
} }
args := c.Args() args := c.Args()
if len(args) > 0 { var image string
if len(args) == 1 {
image = args[0]
}
if len(args) > 1 {
return errors.New("too many arguments. Requires exactly 1") return errors.New("too many arguments. Requires exactly 1")
} }
input := c.String("input") input := c.String("input")
quiet := c.Bool("quiet")
if input == "/dev/stdin" { if input == "/dev/stdin" {
fi, err := os.Stdin.Stat() fi, err := os.Stdin.Stat()
@ -92,7 +95,8 @@ func loadCmd(c *cli.Context) error {
opts := loadOptions{ opts := loadOptions{
input: input, input: input,
quiet: quiet, quiet: c.Bool("quiet"),
image: image,
} }
return loadImage(store, opts) return loadImage(store, opts)
@ -101,9 +105,21 @@ func loadCmd(c *cli.Context) error {
// loadImage loads the image from docker-archive or oci to containers-storage // loadImage loads the image from docker-archive or oci to containers-storage
// using the pullImage function // using the pullImage function
func loadImage(store storage.Store, opts loadOptions) error { func loadImage(store storage.Store, opts loadOptions) error {
systemContext := common.GetSystemContext("") loadOpts := images.CopyOptions{
Quiet: opts.quiet,
Store: store,
}
src := dockerArchive + opts.input src := images.DockerArchive + ":" + opts.input
if err := images.PullImage(src, false, loadOpts); err != nil {
return images.PullImage(store, src, false, opts.quiet, systemContext) src = images.OCIArchive + ":" + opts.input
// generate full src name with specified image:tag
if opts.image != "" {
src = src + ":" + opts.image
}
if err := images.PullImage(src, false, loadOpts); err != nil {
return errors.Wrapf(err, "error pulling from %q", opts.input)
}
}
return nil
} }

View file

@ -10,13 +10,10 @@ import (
"github.com/urfave/cli" "github.com/urfave/cli"
) )
const (
dockerArchive = "docker-archive:"
)
type saveOptions struct { type saveOptions struct {
output string output string
quiet bool quiet bool
format string
images []string images []string
} }
@ -31,9 +28,16 @@ var (
Name: "quiet, q", Name: "quiet, q",
Usage: "Suppress the output", Usage: "Suppress the output",
}, },
cli.StringFlag{
Name: "format",
Usage: "Save image to oci-archive",
},
} }
saveDescription = "Save an image to docker-archive on the local machine" saveDescription = `
saveCommand = cli.Command{ Save an image to docker-archive or oci-archive on the local machine.
Default is docker-archive`
saveCommand = cli.Command{
Name: "save", Name: "save",
Usage: "Save image to an archive", Usage: "Save image to an archive",
Description: saveDescription, Description: saveDescription,
@ -60,7 +64,6 @@ func saveCmd(c *cli.Context) error {
} }
output := c.String("output") output := c.String("output")
quiet := c.Bool("quiet")
if output == "/dev/stdout" { if output == "/dev/stdout" {
fi := os.Stdout fi := os.Stdout
@ -71,7 +74,8 @@ func saveCmd(c *cli.Context) error {
opts := saveOptions{ opts := saveOptions{
output: output, output: output,
quiet: quiet, quiet: c.Bool("quiet"),
format: c.String("format"),
images: args, images: args,
} }
@ -81,9 +85,19 @@ func saveCmd(c *cli.Context) error {
// saveImage pushes the image to docker-archive or oci by // saveImage pushes the image to docker-archive or oci by
// calling pushImage // calling pushImage
func saveImage(store storage.Store, opts saveOptions) error { func saveImage(store storage.Store, opts saveOptions) error {
dst := dockerArchive + opts.output var dst string
switch opts.format {
case images.OCIArchive:
dst = images.OCIArchive + ":" + opts.output
case images.DockerArchive:
fallthrough
case "":
dst = images.DockerArchive + ":" + opts.output
default:
return errors.Errorf("unknown format option %q", opts.format)
}
pushOpts := images.CopyOptions{ saveOpts := images.CopyOptions{
SignaturePolicyPath: "", SignaturePolicyPath: "",
Store: store, Store: store,
} }
@ -92,7 +106,7 @@ func saveImage(store storage.Store, opts saveOptions) error {
// future pull requests will fix this // future pull requests will fix this
for _, image := range opts.images { for _, image := range opts.images {
dest := dst + ":" + image dest := dst + ":" + image
if err := images.PushImage(image, dest, pushOpts); err != nil { if err := images.PushImage(image, dest, saveOpts); err != nil {
return errors.Wrapf(err, "unable to save %q", image) return errors.Wrapf(err, "unable to save %q", image)
} }
} }

View file

@ -332,6 +332,7 @@ _kpod_version() {
_kpod_save() { _kpod_save() {
local options_with_args=" local options_with_args="
--output -o --output -o
--format
" "
local boolean_options=" local boolean_options="
--quiet -q --quiet -q

View file

@ -11,8 +11,8 @@ kpod-load - Load an image from docker archive
[**--help**|**-h**] [**--help**|**-h**]
## DESCRIPTION ## DESCRIPTION
**kpod load** copies an image from **docker-archive** stored on the local machine. **kpod load** copies an image from either **docker-archive** or **oci-archive** stored
**kpod load** reads from stdin by default or a file if the **input** flag is set. on the local machine. **kpod load** reads from stdin by default or a file if the **input** flag is set.
The **quiet** flag suppresses the output when set. The **quiet** flag suppresses the output when set.
**kpod [GLOBAL OPTIONS]** **kpod [GLOBAL OPTIONS]**

View file

@ -3,7 +3,7 @@
# kpod-save "1" "July 2017" "kpod" # kpod-save "1" "July 2017" "kpod"
## NAME ## NAME
kpod-save - Save an image to docker-archive or oci kpod-save - Save an image to docker-archive or oci-archive
## SYNOPSIS ## SYNOPSIS
**kpod save** **kpod save**
@ -11,9 +11,10 @@ kpod-save - Save an image to docker-archive or oci
[**--help**|**-h**] [**--help**|**-h**]
## DESCRIPTION ## DESCRIPTION
**kpod save** saves an image to either **docker-archive** on the loacl machine. **kpod save** saves an image to either **docker-archive** or **oci-archive**
**kpod save** writes to STDOUT by default and can be redirected to a file on the local machine, default is **docker-archive**.
using the **output** flag. The **quiet** flag suppresses the output when set. **kpod save** writes to STDOUT by default and can be redirected to a file using the **output** flag.
The **quiet** flag suppresses the output when set.
**kpod [GLOBAL OPTIONS]** **kpod [GLOBAL OPTIONS]**
@ -26,6 +27,12 @@ using the **output** flag. The **quiet** flag suppresses the output when set.
**--output, -o** **--output, -o**
Write to a file, default is STDOUT Write to a file, default is STDOUT
**--format**
Save image to **oci-archive**
```
--format oci-archive
```
**--quiet, -q** **--quiet, -q**
Suppress the output Suppress the output
@ -44,6 +51,10 @@ Suppress the output
# kpod save > alpine-all.tar alpine # kpod save > alpine-all.tar alpine
``` ```
```
# kpod save -o oci-alpine.tar --format oci-archive alpine
```
## SEE ALSO ## SEE ALSO
kpod(1), kpod-load(1), crio(8), crio.conf(5) kpod(1), kpod-load(1), crio(8), crio.conf(5)

View file

@ -7,12 +7,13 @@ import (
"syscall" "syscall"
cp "github.com/containers/image/copy" cp "github.com/containers/image/copy"
dockerarchive "github.com/containers/image/docker/archive"
"github.com/containers/image/docker/tarfile" "github.com/containers/image/docker/tarfile"
"github.com/containers/image/manifest" "github.com/containers/image/manifest"
ociarchive "github.com/containers/image/oci/archive"
"github.com/containers/image/signature" "github.com/containers/image/signature"
is "github.com/containers/image/storage" is "github.com/containers/image/storage"
"github.com/containers/image/transports/alltransports" "github.com/containers/image/transports/alltransports"
"github.com/containers/image/types"
"github.com/containers/storage" "github.com/containers/storage"
"github.com/containers/storage/pkg/archive" "github.com/containers/storage/pkg/archive"
"github.com/kubernetes-incubator/cri-o/libpod/common" "github.com/kubernetes-incubator/cri-o/libpod/common"
@ -25,6 +26,15 @@ const (
DefaultRegistry = "docker://" DefaultRegistry = "docker://"
) )
var (
// DockerArchive is the transport we prepend to an image name
// when saving to docker-archive
DockerArchive = dockerarchive.Transport.Name()
// OCIArchive is the transport we prepend to an image name
// when saving to oci-archive
OCIArchive = ociarchive.Transport.Name()
)
// CopyOptions contains the options given when pushing or pulling images // CopyOptions contains the options given when pushing or pulling images
type CopyOptions struct { type CopyOptions struct {
// Compression specifies the type of compression which is applied to // Compression specifies the type of compression which is applied to
@ -50,6 +60,8 @@ type CopyOptions struct {
// strip or add signatures to the image when pushing (uploading) the // strip or add signatures to the image when pushing (uploading) the
// image to a registry. // image to a registry.
common.SigningOptions common.SigningOptions
// Quiet suppresses the output when a push or pull happens
Quiet bool
} }
// PushImage pushes the src image to the destination // PushImage pushes the src image to the destination
@ -101,13 +113,15 @@ func PushImage(srcName, destName string, options CopyOptions) error {
} }
// PullImage copies the image from the source to the destination // PullImage copies the image from the source to the destination
func PullImage(store storage.Store, imgName string, allTags, quiet bool, sc *types.SystemContext) error { func PullImage(imgName string, allTags bool, options CopyOptions) error {
var ( var (
images []string images []string
output io.Writer output io.Writer
) )
store := options.Store
sc := common.GetSystemContext(options.SignaturePolicyPath)
if quiet { if options.Quiet {
output = nil output = nil
} else { } else {
output = os.Stdout output = os.Stdout
@ -124,10 +138,11 @@ func PullImage(store storage.Store, imgName string, allTags, quiet bool, sc *typ
} }
splitArr := strings.Split(imgName, ":") splitArr := strings.Split(imgName, ":")
archFile := splitArr[len(splitArr)-1]
// supports pulling from docker-archive, oci, and registries // supports pulling from docker-archive, oci, and registries
if splitArr[0] == "docker-archive" { if srcRef.Transport().Name() == DockerArchive {
tarSource := tarfile.NewSource(splitArr[len(splitArr)-1]) tarSource := tarfile.NewSource(archFile)
manifest, err := tarSource.LoadTarManifest() manifest, err := tarSource.LoadTarManifest()
if err != nil { if err != nil {
return errors.Errorf("error retrieving manifest.json: %v", err) return errors.Errorf("error retrieving manifest.json: %v", err)
@ -152,9 +167,17 @@ func PullImage(store storage.Store, imgName string, allTags, quiet bool, sc *typ
} }
} }
} }
} else if splitArr[0] == "oci" { } else if srcRef.Transport().Name() == OCIArchive {
// needs to be implemented in future // retrieve the manifest from index.json to access the image name
return errors.Errorf("oci not supported") manifest, err := ociarchive.LoadManifestDescriptor(srcRef)
if err != nil {
return errors.Wrapf(err, "error loading manifest for %q", srcRef)
}
if manifest.Annotations == nil || manifest.Annotations["org.opencontainers.image.ref.name"] == "" {
return errors.Errorf("error, archive doesn't have a name annotation. Cannot store image with no name")
}
images = append(images, manifest.Annotations["org.opencontainers.image.ref.name"])
} else { } else {
images = append(images, imgName) images = append(images, imgName)
} }

View file

@ -27,6 +27,22 @@ function teardown() {
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
} }
@test "kpod load oci-archive image" {
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull $IMAGE
[ "$status" -eq 0 ]
run ${KPOD_BINARY} ${KPOD_OPTIONS} save -o alpine.tar --format oci-archive $IMAGE
[ "$status" -eq 0 ]
run ${KPOD_BINARY} $KPOD_OPTIONS rmi $IMAGE
[ "$status" -eq 0 ]
run ${KPOD_BINARY} ${KPOD_OPTIONS} load -i alpine.tar
echo "$output"
[ "$status" -eq 0 ]
rm -f alpine.tar
[ "$status" -eq 0 ]
run ${KPOD_BINARY} $KPOD_OPTIONS rmi $IMAGE
[ "$status" -eq 0 ]
}
@test "kpod load using quiet flag" { @test "kpod load using quiet flag" {
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull $IMAGE run ${KPOD_BINARY} ${KPOD_OPTIONS} pull $IMAGE
[ "$status" -eq 0 ] [ "$status" -eq 0 ]

View file

@ -23,6 +23,18 @@ function teardown() {
[ "$status" -eq 0 ] [ "$status" -eq 0 ]
} }
@test "kpod save oci flag" {
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull $IMAGE
[ "$status" -eq 0 ]
run ${KPOD_BINARY} ${KPOD_OPTIONS} save -o alpine.tar --format oci-archive $IMAGE
echo "$output"
[ "$status" -eq 0 ]
run ${KPOD_BINARY} ${KPOD_OPTIONS} rmi $IMAGE
[ "$status" -eq 0 ]
rm -f alpine.tar
[ "$status" -eq 0 ]
}
@test "kpod save using stdout" { @test "kpod save using stdout" {
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull $IMAGE run ${KPOD_BINARY} ${KPOD_OPTIONS} pull $IMAGE
[ "$status" -eq 0 ] [ "$status" -eq 0 ]