Update kpod load and save for oci-archive
Signed-off-by: umohnani8 <umohnani@redhat.com>
This commit is contained in:
parent
f9387aca28
commit
79c09d4343
8 changed files with 126 additions and 33 deletions
|
@ -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,
|
||||||
src := dockerArchive + opts.input
|
Store: store,
|
||||||
|
}
|
||||||
return images.PullImage(store, src, false, opts.quiet, systemContext)
|
|
||||||
|
src := images.DockerArchive + ":" + opts.input
|
||||||
|
if err := images.PullImage(src, false, loadOpts); err != nil {
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,8 +28,15 @@ 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 = `
|
||||||
|
Save an image to docker-archive or oci-archive on the local machine.
|
||||||
|
Default is docker-archive`
|
||||||
|
|
||||||
saveCommand = cli.Command{
|
saveCommand = cli.Command{
|
||||||
Name: "save",
|
Name: "save",
|
||||||
Usage: "Save image to an archive",
|
Usage: "Save image to an archive",
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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]**
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ]
|
||||||
|
|
|
@ -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 ]
|
||||||
|
|
Loading…
Reference in a new issue