Add authfile flag to pull and push
Push and pull can now access any cached registry credentials from the auth file Signed-off-by: umohnani8 <umohnani@redhat.com>
This commit is contained in:
parent
0914a7a667
commit
d855e2c8ad
13 changed files with 428 additions and 210 deletions
|
@ -95,14 +95,19 @@ func loadCmd(c *cli.Context) error {
|
||||||
output = os.Stdout
|
output = os.Stdout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
options := libpod.CopyOptions{
|
||||||
|
SignaturePolicyPath: c.String("signature-policy"),
|
||||||
|
Writer: output,
|
||||||
|
}
|
||||||
|
|
||||||
src := libpod.DockerArchive + ":" + input
|
src := libpod.DockerArchive + ":" + input
|
||||||
if err := runtime.PullImage(src, false, c.String("signature-policy"), output); err != nil {
|
if err := runtime.PullImage(src, options); err != nil {
|
||||||
src = libpod.OCIArchive + ":" + input
|
src = libpod.OCIArchive + ":" + input
|
||||||
// generate full src name with specified image:tag
|
// generate full src name with specified image:tag
|
||||||
if image != "" {
|
if image != "" {
|
||||||
src = src + ":" + image
|
src = src + ":" + image
|
||||||
}
|
}
|
||||||
if err := runtime.PullImage(src, false, "", output); err != nil {
|
if err := runtime.PullImage(src, options); err != nil {
|
||||||
return errors.Wrapf(err, "error pulling %q", src)
|
return errors.Wrapf(err, "error pulling %q", src)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,9 @@ func logoutCmd(c *cli.Context) error {
|
||||||
if len(args) > 1 {
|
if len(args) > 1 {
|
||||||
return errors.Errorf("too many arguments, logout takes only 1 argument")
|
return errors.Errorf("too many arguments, logout takes only 1 argument")
|
||||||
}
|
}
|
||||||
|
if len(args) == 0 {
|
||||||
|
return errors.Errorf("registry must be given")
|
||||||
|
}
|
||||||
var server string
|
var server string
|
||||||
if len(args) == 1 {
|
if len(args) == 1 {
|
||||||
server = args[0]
|
server = args[0]
|
||||||
|
|
152
cmd/kpod/pull.go
152
cmd/kpod/pull.go
|
@ -1,14 +1,14 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"fmt"
|
"golang.org/x/crypto/ssh/terminal"
|
||||||
|
|
||||||
"github.com/containers/image/docker/reference"
|
|
||||||
"github.com/containers/image/pkg/sysregistries"
|
|
||||||
"github.com/containers/image/transports/alltransports"
|
|
||||||
"github.com/containers/image/types"
|
"github.com/containers/image/types"
|
||||||
|
"github.com/kubernetes-incubator/cri-o/libpod"
|
||||||
|
"github.com/kubernetes-incubator/cri-o/libpod/common"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
|
@ -16,16 +16,18 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
pullFlags = []cli.Flag{
|
pullFlags = []cli.Flag{
|
||||||
cli.BoolFlag{
|
|
||||||
// all-tags is hidden since it has not been implemented yet
|
|
||||||
Name: "all-tags, a",
|
|
||||||
Hidden: true,
|
|
||||||
Usage: "Download all tagged images in the repository",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
cli.StringFlag{
|
||||||
Name: "signature-policy",
|
Name: "signature-policy",
|
||||||
Usage: "`pathname` of signature policy file (not usually used)",
|
Usage: "`pathname` of signature policy file (not usually used)",
|
||||||
},
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "authfile",
|
||||||
|
Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
|
||||||
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "creds",
|
||||||
|
Usage: "`credentials` (USERNAME:PASSWORD) to use for authenticating to a registry",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pullDescription = "Pulls an image from a registry and stores it locally.\n" +
|
pullDescription = "Pulls an image from a registry and stores it locally.\n" +
|
||||||
|
@ -41,83 +43,14 @@ var (
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// struct for when a user passes a short or incomplete
|
|
||||||
// image name
|
|
||||||
type imagePullStruct struct {
|
|
||||||
imageName string
|
|
||||||
tag string
|
|
||||||
registry string
|
|
||||||
hasRegistry bool
|
|
||||||
transport string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ips imagePullStruct) returnFQName() string {
|
|
||||||
return fmt.Sprintf("%s%s/%s:%s", ips.transport, ips.registry, ips.imageName, ips.tag)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRegistriesToTry(image string) ([]string, error) {
|
|
||||||
var registries []string
|
|
||||||
var imageError = fmt.Sprintf("unable to parse '%s'\n", image)
|
|
||||||
imgRef, err := reference.Parse(image)
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Wrapf(err, imageError)
|
|
||||||
}
|
|
||||||
tagged, isTagged := imgRef.(reference.NamedTagged)
|
|
||||||
tag := "latest"
|
|
||||||
if isTagged {
|
|
||||||
tag = tagged.Tag()
|
|
||||||
}
|
|
||||||
hasDomain := true
|
|
||||||
registry := reference.Domain(imgRef.(reference.Named))
|
|
||||||
if registry == "" {
|
|
||||||
hasDomain = false
|
|
||||||
}
|
|
||||||
imageName := reference.Path(imgRef.(reference.Named))
|
|
||||||
pImage := imagePullStruct{
|
|
||||||
imageName,
|
|
||||||
tag,
|
|
||||||
registry,
|
|
||||||
hasDomain,
|
|
||||||
"docker://",
|
|
||||||
}
|
|
||||||
if pImage.hasRegistry {
|
|
||||||
// If input has a registry, we have to assume they included an image
|
|
||||||
// name but maybe not a tag
|
|
||||||
pullRef, err := alltransports.ParseImageName(pImage.returnFQName())
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Errorf(imageError)
|
|
||||||
}
|
|
||||||
registries = append(registries, pullRef.DockerReference().String())
|
|
||||||
} else {
|
|
||||||
// No registry means we check the globals registries configuration file
|
|
||||||
// and assemble a list of candidate sources to try
|
|
||||||
registryConfigPath := ""
|
|
||||||
envOverride := os.Getenv("REGISTRIES_CONFIG_PATH")
|
|
||||||
if len(envOverride) > 0 {
|
|
||||||
registryConfigPath = envOverride
|
|
||||||
}
|
|
||||||
searchRegistries, err := sysregistries.GetRegistries(&types.SystemContext{SystemRegistriesConfPath: registryConfigPath})
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
return nil, errors.Errorf("unable to parse the registries.conf file and"+
|
|
||||||
" the image name '%s' is incomplete.", imageName)
|
|
||||||
}
|
|
||||||
for _, searchRegistry := range searchRegistries {
|
|
||||||
pImage.registry = searchRegistry
|
|
||||||
pullRef, err := alltransports.ParseImageName(pImage.returnFQName())
|
|
||||||
if err != nil {
|
|
||||||
return nil, errors.Errorf("unable to parse '%s'", pImage.returnFQName())
|
|
||||||
}
|
|
||||||
registries = append(registries, pullRef.DockerReference().String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return registries, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// pullCmd gets the data from the command line and calls pullImage
|
// pullCmd gets the data from the command line and calls pullImage
|
||||||
// to copy an image from a registry to a local machine
|
// to copy an image from a registry to a local machine
|
||||||
func pullCmd(c *cli.Context) error {
|
func pullCmd(c *cli.Context) error {
|
||||||
var fqRegistries []string
|
runtime, err := getRuntime(c)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "could not get runtime")
|
||||||
|
}
|
||||||
|
defer runtime.Shutdown(false)
|
||||||
|
|
||||||
args := c.Args()
|
args := c.Args()
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
|
@ -132,31 +65,34 @@ func pullCmd(c *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
image := args[0]
|
image := args[0]
|
||||||
srcRef, err := alltransports.ParseImageName(image)
|
|
||||||
if err != nil {
|
|
||||||
fqRegistries, err = getRegistriesToTry(image)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
fqRegistries = append(fqRegistries, srcRef.DockerReference().String())
|
|
||||||
}
|
|
||||||
runtime, err := getRuntime(c)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "could not get runtime")
|
|
||||||
}
|
|
||||||
defer runtime.Shutdown(false)
|
|
||||||
|
|
||||||
if err != nil {
|
var registryCreds *types.DockerAuthConfig
|
||||||
return errors.Wrapf(err, "could not create runtime")
|
if c.String("creds") != "" {
|
||||||
}
|
creds, err := common.ParseRegistryCreds(c.String("creds"))
|
||||||
for _, fqname := range fqRegistries {
|
if err != nil {
|
||||||
fmt.Printf("Trying to pull %s...", fqname)
|
if err == common.ErrNoPassword {
|
||||||
if err := runtime.PullImage(fqname, c.Bool("all-tags"), c.String("signature-policy"), os.Stdout); err != nil {
|
fmt.Print("Password: ")
|
||||||
fmt.Printf(" Failed\n")
|
password, err := terminal.ReadPassword(0)
|
||||||
} else {
|
if err != nil {
|
||||||
return nil
|
return errors.Wrapf(err, "could not read password from terminal")
|
||||||
|
}
|
||||||
|
creds.Password = string(password)
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
registryCreds = creds
|
||||||
}
|
}
|
||||||
return errors.Errorf("error pulling image from %q", image)
|
|
||||||
|
options := libpod.CopyOptions{
|
||||||
|
SignaturePolicyPath: c.String("signature-policy"),
|
||||||
|
AuthFile: c.String("authfile"),
|
||||||
|
DockerRegistryOptions: common.DockerRegistryOptions{
|
||||||
|
DockerRegistryCreds: registryCreds,
|
||||||
|
},
|
||||||
|
Writer: os.Stdout,
|
||||||
|
}
|
||||||
|
|
||||||
|
return runtime.PullImage(image, options)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,10 @@ var (
|
||||||
Name: "quiet, q",
|
Name: "quiet, q",
|
||||||
Usage: "don't output progress information when pushing images",
|
Usage: "don't output progress information when pushing images",
|
||||||
},
|
},
|
||||||
|
cli.StringFlag{
|
||||||
|
Name: "authfile",
|
||||||
|
Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
pushDescription = fmt.Sprintf(`
|
pushDescription = fmt.Sprintf(`
|
||||||
Pushes an image to a specified location.
|
Pushes an image to a specified location.
|
||||||
|
@ -120,7 +124,9 @@ func pushCmd(c *cli.Context) error {
|
||||||
RemoveSignatures: removeSignatures,
|
RemoveSignatures: removeSignatures,
|
||||||
SignBy: signBy,
|
SignBy: signBy,
|
||||||
},
|
},
|
||||||
|
AuthFile: c.String("authfile"),
|
||||||
|
Writer: writer,
|
||||||
}
|
}
|
||||||
|
|
||||||
return runtime.PushImage(srcName, destName, options, writer)
|
return runtime.PushImage(srcName, destName, options)
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,13 +83,14 @@ func saveCmd(c *cli.Context) error {
|
||||||
|
|
||||||
saveOpts := libpod.CopyOptions{
|
saveOpts := libpod.CopyOptions{
|
||||||
SignaturePolicyPath: "",
|
SignaturePolicyPath: "",
|
||||||
|
Writer: writer,
|
||||||
}
|
}
|
||||||
|
|
||||||
// only one image is supported for now
|
// only one image is supported for now
|
||||||
// future pull requests will fix this
|
// future pull requests will fix this
|
||||||
for _, image := range args {
|
for _, image := range args {
|
||||||
dest := dst + ":" + image
|
dest := dst + ":" + image
|
||||||
if err := runtime.PushImage(image, dest, saveOpts, writer); err != nil {
|
if err := runtime.PushImage(image, dest, saveOpts); err != nil {
|
||||||
return errors.Wrapf(err, "unable to save %q", image)
|
return errors.Wrapf(err, "unable to save %q", image)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -170,6 +170,8 @@ _kpod_logs() {
|
||||||
|
|
||||||
_kpod_pull() {
|
_kpod_pull() {
|
||||||
local options_with_args="
|
local options_with_args="
|
||||||
|
--authfile
|
||||||
|
--creds
|
||||||
--signature-policy
|
--signature-policy
|
||||||
"
|
"
|
||||||
local boolean_options="
|
local boolean_options="
|
||||||
|
@ -222,18 +224,20 @@ _kpod_mount() {
|
||||||
|
|
||||||
_kpod_push() {
|
_kpod_push() {
|
||||||
local boolean_options="
|
local boolean_options="
|
||||||
--disable-compression
|
--disable-compression
|
||||||
-D
|
-D
|
||||||
--quiet
|
--quiet
|
||||||
-q
|
-q
|
||||||
--signature-policy
|
--remove-signatures
|
||||||
--certs
|
--tls-verify
|
||||||
--tls-verify
|
|
||||||
--remove-signatures
|
|
||||||
--sign-by
|
|
||||||
"
|
"
|
||||||
|
|
||||||
local options_with_args="
|
local options_with_args="
|
||||||
|
--authfile
|
||||||
|
--cert-dir
|
||||||
|
--creds
|
||||||
|
--sign-by
|
||||||
|
--signature-policy
|
||||||
"
|
"
|
||||||
|
|
||||||
local all_options="$options_with_args $boolean_options"
|
local all_options="$options_with_args $boolean_options"
|
||||||
|
|
|
@ -21,10 +21,10 @@ images from archives and local storage using different transports.
|
||||||
## imageID
|
## imageID
|
||||||
Image stored in local container/storage
|
Image stored in local container/storage
|
||||||
|
|
||||||
## DESTINATION
|
## SOURCE
|
||||||
|
|
||||||
The DESTINATION is a location to store container images
|
The SOURCE is a location to get container images
|
||||||
The Image "DESTINATION" uses a "transport":"details" format.
|
The Image "SOURCE" uses a "transport":"details" format.
|
||||||
|
|
||||||
Multiple transports are supported:
|
Multiple transports are supported:
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ Image stored in local container/storage
|
||||||
An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This is a non-standardized format, primarily useful for debugging or noninvasive container inspection.
|
An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This is a non-standardized format, primarily useful for debugging or noninvasive container inspection.
|
||||||
|
|
||||||
**docker://**_docker-reference_
|
**docker://**_docker-reference_
|
||||||
An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$HOME/.docker/config.json`, which is set e.g. using `(docker login)`.
|
An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$XDG_RUNTIME_DIR/containers/auth.json`, which is set e.g. using `(kpod login)`.
|
||||||
|
|
||||||
**docker-archive:**_path_[**:**_docker-reference_]
|
**docker-archive:**_path_[**:**_docker-reference_]
|
||||||
An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a file, and it must not contain a digest.
|
An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a file, and it must not contain a digest.
|
||||||
|
@ -40,7 +40,7 @@ Image stored in local container/storage
|
||||||
**docker-daemon:**_docker-reference_
|
**docker-daemon:**_docker-reference_
|
||||||
An image _docker-reference_ stored in the docker daemon internal storage. _docker-reference_ must contain either a tag or a digest. Alternatively, when reading images, the format can also be docker-daemon:algo:digest (an image ID).
|
An image _docker-reference_ stored in the docker daemon internal storage. _docker-reference_ must contain either a tag or a digest. Alternatively, when reading images, the format can also be docker-daemon:algo:digest (an image ID).
|
||||||
|
|
||||||
**oci:**_path_**:**_tag_
|
**oci-archive:**_path_**:**_tag_
|
||||||
An image _tag_ in a directory compliant with "Open Container Image Layout Specification" at _path_.
|
An image _tag_ in a directory compliant with "Open Container Image Layout Specification" at _path_.
|
||||||
|
|
||||||
**ostree:**_image_[**@**_/absolute/repo/path_]
|
**ostree:**_image_[**@**_/absolute/repo/path_]
|
||||||
|
@ -54,6 +54,14 @@ Image stored in local container/storage
|
||||||
|
|
||||||
## OPTIONS
|
## OPTIONS
|
||||||
|
|
||||||
|
**--authfile**
|
||||||
|
|
||||||
|
Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json
|
||||||
|
|
||||||
|
**--creds**
|
||||||
|
|
||||||
|
Credentials (USERNAME:PASSWORD) to use for authenticating to a registry
|
||||||
|
|
||||||
**--signature-policy="PATHNAME"**
|
**--signature-policy="PATHNAME"**
|
||||||
|
|
||||||
Pathname of a signature policy file to use. It is not recommended that this
|
Pathname of a signature policy file to use. It is not recommended that this
|
||||||
|
@ -75,8 +83,30 @@ Writing manifest to image destination
|
||||||
Storing signatures
|
Storing signatures
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
# kpod pull --authfile temp-auths/myauths.json docker://docker.io/umohnani/finaltest
|
||||||
|
Trying to pull docker.io/umohnani/finaltest:latest...Getting image source signatures
|
||||||
|
Copying blob sha256:6d987f6f42797d81a318c40d442369ba3dc124883a0964d40b0c8f4f7561d913
|
||||||
|
1.90 MB / 1.90 MB [========================================================] 0s
|
||||||
|
Copying config sha256:ad4686094d8f0186ec8249fc4917b71faa2c1030d7b5a025c29f26e19d95c156
|
||||||
|
1.41 KB / 1.41 KB [========================================================] 0s
|
||||||
|
Writing manifest to image destination
|
||||||
|
Storing signatures
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
# kpod pull docker.io/umohnani/finaltest
|
||||||
|
Trying to pull docker.io/umohnani/finaltest:latest...Getting image source signatures
|
||||||
|
Copying blob sha256:6d987f6f42797d81a318c40d442369ba3dc124883a0964d40b0c8f4f7561d913
|
||||||
|
1.90 MB / 1.90 MB [========================================================] 0s
|
||||||
|
Copying config sha256:ad4686094d8f0186ec8249fc4917b71faa2c1030d7b5a025c29f26e19d95c156
|
||||||
|
1.41 KB / 1.41 KB [========================================================] 0s
|
||||||
|
Writing manifest to image destination
|
||||||
|
Storing signatures
|
||||||
|
```
|
||||||
|
|
||||||
## SEE ALSO
|
## SEE ALSO
|
||||||
kpod(1), crio(8), crio.conf(5)
|
kpod(1), kpod-push(1), crio(8), crio.conf(5)
|
||||||
|
|
||||||
## HISTORY
|
## HISTORY
|
||||||
July 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
|
July 2017, Originally compiled by Urvashi Mohnani <umohnani@redhat.com>
|
||||||
|
|
|
@ -26,7 +26,7 @@ Image stored in local container/storage
|
||||||
An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This is a non-standardized format, primarily useful for debugging or noninvasive container inspection.
|
An existing local directory _path_ storing the manifest, layer tarballs and signatures as individual files. This is a non-standardized format, primarily useful for debugging or noninvasive container inspection.
|
||||||
|
|
||||||
**docker://**_docker-reference_
|
**docker://**_docker-reference_
|
||||||
An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$HOME/.docker/config.json`, which is set e.g. using `(docker login)`.
|
An image in a registry implementing the "Docker Registry HTTP API V2". By default, uses the authorization state in `$XDG_RUNTIME_DIR/containers/auth.json`, which is set e.g. using `(kpod login)`.
|
||||||
|
|
||||||
**docker-archive:**_path_[**:**_docker-reference_]
|
**docker-archive:**_path_[**:**_docker-reference_]
|
||||||
An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a file, and it must not contain a digest.
|
An image is stored in the `docker save` formatted file. _docker-reference_ is only used when creating such a file, and it must not contain a digest.
|
||||||
|
@ -34,7 +34,7 @@ Image stored in local container/storage
|
||||||
**docker-daemon:**_docker-reference_
|
**docker-daemon:**_docker-reference_
|
||||||
An image _docker-reference_ stored in the docker daemon internal storage. _docker-reference_ must contain either a tag or a digest. Alternatively, when reading images, the format can also be docker-daemon:algo:digest (an image ID).
|
An image _docker-reference_ stored in the docker daemon internal storage. _docker-reference_ must contain either a tag or a digest. Alternatively, when reading images, the format can also be docker-daemon:algo:digest (an image ID).
|
||||||
|
|
||||||
**oci:**_path_**:**_tag_
|
**oci-archive:**_path_**:**_tag_
|
||||||
An image _tag_ in a directory compliant with "Open Container Image Layout Specification" at _path_.
|
An image _tag_ in a directory compliant with "Open Container Image Layout Specification" at _path_.
|
||||||
|
|
||||||
**ostree:**_image_[**@**_/absolute/repo/path_]
|
**ostree:**_image_[**@**_/absolute/repo/path_]
|
||||||
|
@ -42,6 +42,10 @@ Image stored in local container/storage
|
||||||
|
|
||||||
## OPTIONS
|
## OPTIONS
|
||||||
|
|
||||||
|
**--authfile**
|
||||||
|
|
||||||
|
Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json
|
||||||
|
|
||||||
**--creds="CREDENTIALS"**
|
**--creds="CREDENTIALS"**
|
||||||
|
|
||||||
Credentials (USERNAME:PASSWORD) to use for authenticating to a registry
|
Credentials (USERNAME:PASSWORD) to use for authenticating to a registry
|
||||||
|
@ -84,7 +88,7 @@ This example extracts the imageID image to a local directory in docker format.
|
||||||
|
|
||||||
This example extracts the imageID image to a local directory in oci format.
|
This example extracts the imageID image to a local directory in oci format.
|
||||||
|
|
||||||
`# kpod push imageID oci:/path/to/layout`
|
`# kpod push imageID oci-archive:/path/to/layout:image:tag`
|
||||||
|
|
||||||
This example extracts the imageID image to a container registry named registry.example.com
|
This example extracts the imageID image to a container registry named registry.example.com
|
||||||
|
|
||||||
|
@ -94,5 +98,19 @@ This example extracts the imageID image and puts into the local docker container
|
||||||
|
|
||||||
`# kpod push imageID docker-daemon:image:tag`
|
`# kpod push imageID docker-daemon:image:tag`
|
||||||
|
|
||||||
|
This example pushes the alpine image to umohnani/alpine on dockerhub and reads the creds from
|
||||||
|
the path given to --authfile
|
||||||
|
|
||||||
|
```
|
||||||
|
# kpod push --authfile temp-auths/myauths.json alpine docker://docker.io/umohnani/alpine
|
||||||
|
Getting image source signatures
|
||||||
|
Copying blob sha256:5bef08742407efd622d243692b79ba0055383bbce12900324f75e56f589aedb0
|
||||||
|
4.03 MB / 4.03 MB [========================================================] 1s
|
||||||
|
Copying config sha256:ad4686094d8f0186ec8249fc4917b71faa2c1030d7b5a025c29f26e19d95c156
|
||||||
|
1.41 KB / 1.41 KB [========================================================] 1s
|
||||||
|
Writing manifest to image destination
|
||||||
|
Storing signatures
|
||||||
|
```
|
||||||
|
|
||||||
## SEE ALSO
|
## SEE ALSO
|
||||||
kpod(1)
|
kpod(1), kpod-pull(1), crio(8), crio.conf(5)
|
||||||
|
|
|
@ -17,15 +17,15 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetCopyOptions constructs a new containers/image/copy.Options{} struct from the given parameters
|
// GetCopyOptions constructs a new containers/image/copy.Options{} struct from the given parameters
|
||||||
func GetCopyOptions(reportWriter io.Writer, signaturePolicyPath string, srcDockerRegistry, destDockerRegistry *DockerRegistryOptions, signing SigningOptions) *cp.Options {
|
func GetCopyOptions(reportWriter io.Writer, signaturePolicyPath string, srcDockerRegistry, destDockerRegistry *DockerRegistryOptions, signing SigningOptions, authFile string) *cp.Options {
|
||||||
if srcDockerRegistry == nil {
|
if srcDockerRegistry == nil {
|
||||||
srcDockerRegistry = &DockerRegistryOptions{}
|
srcDockerRegistry = &DockerRegistryOptions{}
|
||||||
}
|
}
|
||||||
if destDockerRegistry == nil {
|
if destDockerRegistry == nil {
|
||||||
destDockerRegistry = &DockerRegistryOptions{}
|
destDockerRegistry = &DockerRegistryOptions{}
|
||||||
}
|
}
|
||||||
srcContext := srcDockerRegistry.GetSystemContext(signaturePolicyPath)
|
srcContext := srcDockerRegistry.GetSystemContext(signaturePolicyPath, authFile)
|
||||||
destContext := destDockerRegistry.GetSystemContext(signaturePolicyPath)
|
destContext := destDockerRegistry.GetSystemContext(signaturePolicyPath, authFile)
|
||||||
return &cp.Options{
|
return &cp.Options{
|
||||||
RemoveSignatures: signing.RemoveSignatures,
|
RemoveSignatures: signing.RemoveSignatures,
|
||||||
SignBy: signing.SignBy,
|
SignBy: signing.SignBy,
|
||||||
|
|
|
@ -22,12 +22,13 @@ type DockerRegistryOptions struct {
|
||||||
|
|
||||||
// GetSystemContext constructs a new system context from the given signaturePolicy path and the
|
// GetSystemContext constructs a new system context from the given signaturePolicy path and the
|
||||||
// values in the DockerRegistryOptions
|
// values in the DockerRegistryOptions
|
||||||
func (o DockerRegistryOptions) GetSystemContext(signaturePolicyPath string) *types.SystemContext {
|
func (o DockerRegistryOptions) GetSystemContext(signaturePolicyPath, authFile string) *types.SystemContext {
|
||||||
sc := &types.SystemContext{
|
sc := &types.SystemContext{
|
||||||
SignaturePolicyPath: signaturePolicyPath,
|
SignaturePolicyPath: signaturePolicyPath,
|
||||||
DockerAuthConfig: o.DockerRegistryCreds,
|
DockerAuthConfig: o.DockerRegistryCreds,
|
||||||
DockerCertPath: o.DockerCertPath,
|
DockerCertPath: o.DockerCertPath,
|
||||||
DockerInsecureSkipTLSVerify: o.DockerInsecureSkipTLSVerify,
|
DockerInsecureSkipTLSVerify: o.DockerInsecureSkipTLSVerify,
|
||||||
|
AuthFilePath: authFile,
|
||||||
}
|
}
|
||||||
return sc
|
return sc
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
@ -14,6 +15,7 @@ import (
|
||||||
"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"
|
ociarchive "github.com/containers/image/oci/archive"
|
||||||
|
"github.com/containers/image/pkg/sysregistries"
|
||||||
"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"
|
"github.com/containers/image/transports"
|
||||||
|
@ -42,6 +44,9 @@ var (
|
||||||
// OCIArchive is the transport we prepend to an image name
|
// OCIArchive is the transport we prepend to an image name
|
||||||
// when saving to oci-archive
|
// when saving to oci-archive
|
||||||
OCIArchive = ociarchive.Transport.Name()
|
OCIArchive = ociarchive.Transport.Name()
|
||||||
|
// DirTransport is the transport for pushing and pulling
|
||||||
|
// images to and from a directory
|
||||||
|
DirTransport = "dir"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CopyOptions contains the options given when pushing or pulling images
|
// CopyOptions contains the options given when pushing or pulling images
|
||||||
|
@ -61,6 +66,10 @@ type CopyOptions struct {
|
||||||
|
|
||||||
// SigningPolicyPath this points to a alternative signature policy file, used mainly for testing
|
// SigningPolicyPath this points to a alternative signature policy file, used mainly for testing
|
||||||
SignaturePolicyPath string
|
SignaturePolicyPath string
|
||||||
|
// AuthFile is the path of the cached credentials file defined by the user
|
||||||
|
AuthFile string
|
||||||
|
// Writer is the reportWriter for the output
|
||||||
|
Writer io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Image API
|
// Image API
|
||||||
|
@ -76,45 +85,124 @@ type ImageFilterParams struct {
|
||||||
ImageInput string
|
ImageInput string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// struct for when a user passes a short or incomplete
|
||||||
|
// image name
|
||||||
|
type imageDecomposeStruct struct {
|
||||||
|
imageName string
|
||||||
|
tag string
|
||||||
|
registry string
|
||||||
|
hasRegistry bool
|
||||||
|
transport string
|
||||||
|
}
|
||||||
|
|
||||||
// ImageFilter is a function to determine whether an image is included in
|
// ImageFilter is a function to determine whether an image is included in
|
||||||
// command output. Images to be outputted are tested using the function. A true
|
// command output. Images to be outputted are tested using the function. A true
|
||||||
// return will include the image, a false return will exclude it.
|
// return will include the image, a false return will exclude it.
|
||||||
type ImageFilter func(*storage.Image, *types.ImageInspectInfo) bool
|
type ImageFilter func(*storage.Image, *types.ImageInspectInfo) bool
|
||||||
|
|
||||||
// PullImage pulls an image from configured registries
|
func (ips imageDecomposeStruct) returnFQName() string {
|
||||||
// By default, only the latest tag (or a specific tag if requested) will be
|
return fmt.Sprintf("%s%s/%s:%s", ips.transport, ips.registry, ips.imageName, ips.tag)
|
||||||
// pulled. If allTags is true, all tags for the requested image will be pulled.
|
}
|
||||||
// Signature validation will be performed if the Runtime has been appropriately
|
|
||||||
// configured
|
|
||||||
func (r *Runtime) PullImage(imgName string, allTags bool, signaturePolicyPath string, reportWriter io.Writer) error {
|
|
||||||
r.lock.Lock()
|
|
||||||
defer r.lock.Unlock()
|
|
||||||
|
|
||||||
if !r.valid {
|
func getRegistriesToTry(image string, store storage.Store) ([]*pullStruct, error) {
|
||||||
return ErrRuntimeStopped
|
var pStructs []*pullStruct
|
||||||
}
|
var imageError = fmt.Sprintf("unable to parse '%s'\n", image)
|
||||||
|
imgRef, err := reference.Parse(image)
|
||||||
// PullImage copies the image from the source to the destination
|
|
||||||
var (
|
|
||||||
images []string
|
|
||||||
)
|
|
||||||
|
|
||||||
if signaturePolicyPath == "" {
|
|
||||||
signaturePolicyPath = r.config.SignaturePolicyPath
|
|
||||||
}
|
|
||||||
|
|
||||||
sc := common.GetSystemContext(signaturePolicyPath, "")
|
|
||||||
|
|
||||||
srcRef, err := alltransports.ParseImageName(imgName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
defaultName := DefaultRegistry + imgName
|
return nil, errors.Wrapf(err, imageError)
|
||||||
srcRef2, err2 := alltransports.ParseImageName(defaultName)
|
}
|
||||||
if err2 != nil {
|
tagged, isTagged := imgRef.(reference.NamedTagged)
|
||||||
return errors.Errorf("error parsing image name %q: %v", defaultName, err2)
|
tag := "latest"
|
||||||
|
if isTagged {
|
||||||
|
tag = tagged.Tag()
|
||||||
|
}
|
||||||
|
hasDomain := true
|
||||||
|
registry := reference.Domain(imgRef.(reference.Named))
|
||||||
|
if registry == "" {
|
||||||
|
hasDomain = false
|
||||||
|
}
|
||||||
|
imageName := reference.Path(imgRef.(reference.Named))
|
||||||
|
pImage := imageDecomposeStruct{
|
||||||
|
imageName,
|
||||||
|
tag,
|
||||||
|
registry,
|
||||||
|
hasDomain,
|
||||||
|
"docker://",
|
||||||
|
}
|
||||||
|
if pImage.hasRegistry {
|
||||||
|
// If input has a registry, we have to assume they included an image
|
||||||
|
// name but maybe not a tag
|
||||||
|
srcRef, err := alltransports.ParseImageName(pImage.returnFQName())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf(imageError)
|
||||||
|
}
|
||||||
|
pStruct := &pullStruct{
|
||||||
|
image: srcRef.DockerReference().String(),
|
||||||
|
srcRef: srcRef,
|
||||||
|
}
|
||||||
|
pStructs = append(pStructs, pStruct)
|
||||||
|
} else {
|
||||||
|
// No registry means we check the globals registries configuration file
|
||||||
|
// and assemble a list of candidate sources to try
|
||||||
|
registryConfigPath := ""
|
||||||
|
envOverride := os.Getenv("REGISTRIES_CONFIG_PATH")
|
||||||
|
if len(envOverride) > 0 {
|
||||||
|
registryConfigPath = envOverride
|
||||||
|
}
|
||||||
|
searchRegistries, err := sysregistries.GetRegistries(&types.SystemContext{SystemRegistriesConfPath: registryConfigPath})
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return nil, errors.Errorf("unable to parse the registries.conf file and"+
|
||||||
|
" the image name '%s' is incomplete.", imageName)
|
||||||
|
}
|
||||||
|
for _, searchRegistry := range searchRegistries {
|
||||||
|
pImage.registry = searchRegistry
|
||||||
|
srcRef, err := alltransports.ParseImageName(pImage.returnFQName())
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf("unable to parse '%s'", pImage.returnFQName())
|
||||||
|
}
|
||||||
|
pStruct := &pullStruct{
|
||||||
|
image: srcRef.DockerReference().String(),
|
||||||
|
srcRef: srcRef,
|
||||||
|
}
|
||||||
|
pStructs = append(pStructs, pStruct)
|
||||||
}
|
}
|
||||||
srcRef = srcRef2
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, pStruct := range pStructs {
|
||||||
|
destRef, err := is.Transport.ParseStoreReference(store, pStruct.image)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf("error parsing dest reference name: %v", err)
|
||||||
|
}
|
||||||
|
pStruct.dstRef = destRef
|
||||||
|
}
|
||||||
|
return pStructs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type pullStruct struct {
|
||||||
|
image string
|
||||||
|
srcRef types.ImageReference
|
||||||
|
dstRef types.ImageReference
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Runtime) getPullStruct(srcRef types.ImageReference, destName string) (*pullStruct, error) {
|
||||||
|
reference := destName
|
||||||
|
if srcRef.DockerReference() != nil {
|
||||||
|
reference = srcRef.DockerReference().String()
|
||||||
|
}
|
||||||
|
destRef, err := is.Transport.ParseStoreReference(r.store, reference)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Errorf("error parsing dest reference name: %v", err)
|
||||||
|
}
|
||||||
|
return &pullStruct{
|
||||||
|
image: destName,
|
||||||
|
srcRef: srcRef,
|
||||||
|
dstRef: destRef,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Runtime) getPullListFromRef(srcRef types.ImageReference, imgName string, sc *types.SystemContext) ([]*pullStruct, error) {
|
||||||
|
var pullStructs []*pullStruct
|
||||||
splitArr := strings.Split(imgName, ":")
|
splitArr := strings.Split(imgName, ":")
|
||||||
archFile := splitArr[len(splitArr)-1]
|
archFile := splitArr[len(splitArr)-1]
|
||||||
|
|
||||||
|
@ -123,41 +211,107 @@ func (r *Runtime) PullImage(imgName string, allTags bool, signaturePolicyPath st
|
||||||
tarSource := tarfile.NewSource(archFile)
|
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 nil, errors.Errorf("error retrieving manifest.json: %v", err)
|
||||||
}
|
}
|
||||||
// to pull all the images stored in one tar file
|
// to pull the first image stored in the tar file
|
||||||
for i := range manifest {
|
if len(manifest) == 0 {
|
||||||
if manifest[i].RepoTags != nil {
|
// create an image object and use the hex value of the digest as the image ID
|
||||||
images = append(images, manifest[i].RepoTags[0])
|
// for parsing the store reference
|
||||||
} else {
|
newImg, err := srcRef.NewImage(sc)
|
||||||
// create an image object and use the hex value of the digest as the image ID
|
if err != nil {
|
||||||
// for parsing the store reference
|
return nil, err
|
||||||
newImg, err := srcRef.NewImage(sc)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer newImg.Close()
|
|
||||||
digest := newImg.ConfigInfo().Digest
|
|
||||||
if err := digest.Validate(); err == nil {
|
|
||||||
images = append(images, "@"+digest.Hex())
|
|
||||||
} else {
|
|
||||||
return errors.Wrapf(err, "error getting config info")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
defer newImg.Close()
|
||||||
|
digest := newImg.ConfigInfo().Digest
|
||||||
|
if err := digest.Validate(); err == nil {
|
||||||
|
pullInfo, err := r.getPullStruct(srcRef, "@"+digest.Hex())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pullStructs = append(pullStructs, pullInfo)
|
||||||
|
} else {
|
||||||
|
return nil, errors.Wrapf(err, "error getting config info")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pullInfo, err := r.getPullStruct(srcRef, manifest[0].RepoTags[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pullStructs = append(pullStructs, pullInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if srcRef.Transport().Name() == OCIArchive {
|
} else if srcRef.Transport().Name() == OCIArchive {
|
||||||
// retrieve the manifest from index.json to access the image name
|
// retrieve the manifest from index.json to access the image name
|
||||||
manifest, err := ociarchive.LoadManifestDescriptor(srcRef)
|
manifest, err := ociarchive.LoadManifestDescriptor(srcRef)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "error loading manifest for %q", srcRef)
|
return nil, errors.Wrapf(err, "error loading manifest for %q", srcRef)
|
||||||
}
|
}
|
||||||
|
|
||||||
if manifest.Annotations == nil || manifest.Annotations["org.opencontainers.image.ref.name"] == "" {
|
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")
|
return nil, 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"])
|
pullInfo, err := r.getPullStruct(srcRef, manifest.Annotations["org.opencontainers.image.ref.name"])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pullStructs = append(pullStructs, pullInfo)
|
||||||
|
} else if srcRef.Transport().Name() == DirTransport {
|
||||||
|
// supports pull from a directory
|
||||||
|
image := splitArr[1]
|
||||||
|
// remove leading "/"
|
||||||
|
if image[:1] == "/" {
|
||||||
|
image = image[1:]
|
||||||
|
}
|
||||||
|
pullInfo, err := r.getPullStruct(srcRef, image)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pullStructs = append(pullStructs, pullInfo)
|
||||||
} else {
|
} else {
|
||||||
images = append(images, imgName)
|
pullInfo, err := r.getPullStruct(srcRef, imgName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
pullStructs = append(pullStructs, pullInfo)
|
||||||
|
}
|
||||||
|
return pullStructs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullImage pulls an image from configured registries
|
||||||
|
// By default, only the latest tag (or a specific tag if requested) will be
|
||||||
|
// pulled. If allTags is true, all tags for the requested image will be pulled.
|
||||||
|
// Signature validation will be performed if the Runtime has been appropriately
|
||||||
|
// configured
|
||||||
|
func (r *Runtime) PullImage(imgName string, options CopyOptions) error {
|
||||||
|
r.lock.Lock()
|
||||||
|
defer r.lock.Unlock()
|
||||||
|
|
||||||
|
if !r.valid {
|
||||||
|
return ErrRuntimeStopped
|
||||||
|
}
|
||||||
|
|
||||||
|
// PullImage copies the image from the source to the destination
|
||||||
|
var pullStructs []*pullStruct
|
||||||
|
|
||||||
|
signaturePolicyPath := r.config.SignaturePolicyPath
|
||||||
|
if options.SignaturePolicyPath != "" {
|
||||||
|
signaturePolicyPath = options.SignaturePolicyPath
|
||||||
|
}
|
||||||
|
|
||||||
|
sc := common.GetSystemContext(signaturePolicyPath, options.AuthFile)
|
||||||
|
|
||||||
|
srcRef, err := alltransports.ParseImageName(imgName)
|
||||||
|
if err != nil {
|
||||||
|
// could be trying to pull from registry with short name
|
||||||
|
pullStructs, err = getRegistriesToTry(imgName, r.store)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "error getting default registries to try")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pullStructs, err = r.getPullListFromRef(srcRef, imgName, sc)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "error getting pullStruct info to pull image %q", imgName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
policy, err := signature.DefaultPolicy(sc)
|
policy, err := signature.DefaultPolicy(sc)
|
||||||
|
@ -171,25 +325,21 @@ func (r *Runtime) PullImage(imgName string, allTags bool, signaturePolicyPath st
|
||||||
}
|
}
|
||||||
defer policyContext.Destroy()
|
defer policyContext.Destroy()
|
||||||
|
|
||||||
copyOptions := common.GetCopyOptions(reportWriter, signaturePolicyPath, nil, nil, common.SigningOptions{})
|
copyOptions := common.GetCopyOptions(options.Writer, signaturePolicyPath, &options.DockerRegistryOptions, nil, options.SigningOptions, options.AuthFile)
|
||||||
for _, image := range images {
|
|
||||||
reference := image
|
for _, imageInfo := range pullStructs {
|
||||||
if srcRef.DockerReference() != nil {
|
fmt.Printf("Trying to pull %s...\n", imageInfo.image)
|
||||||
reference = srcRef.DockerReference().String()
|
if err = cp.Image(policyContext, imageInfo.dstRef, imageInfo.srcRef, copyOptions); err != nil {
|
||||||
}
|
fmt.Println("Failed")
|
||||||
destRef, err := is.Transport.ParseStoreReference(r.store, reference)
|
} else {
|
||||||
if err != nil {
|
return nil
|
||||||
return errors.Errorf("error parsing dest reference name: %v", err)
|
|
||||||
}
|
|
||||||
if err = cp.Image(policyContext, destRef, srcRef, copyOptions); err != nil {
|
|
||||||
return errors.Errorf("error loading image %q: %v", image, err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return errors.Wrapf(err, "error pulling image from %q", imgName)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushImage pushes the given image to a location described by the given path
|
// PushImage pushes the given image to a location described by the given path
|
||||||
func (r *Runtime) PushImage(source string, destination string, options CopyOptions, reportWriter io.Writer) error {
|
func (r *Runtime) PushImage(source string, destination string, options CopyOptions) error {
|
||||||
r.lock.Lock()
|
r.lock.Lock()
|
||||||
defer r.lock.Unlock()
|
defer r.lock.Unlock()
|
||||||
|
|
||||||
|
@ -214,9 +364,16 @@ func (r *Runtime) PushImage(source string, destination string, options CopyOptio
|
||||||
signaturePolicyPath = options.SignaturePolicyPath
|
signaturePolicyPath = options.SignaturePolicyPath
|
||||||
}
|
}
|
||||||
|
|
||||||
policyContext, err := common.GetPolicyContext(signaturePolicyPath)
|
sc := common.GetSystemContext(signaturePolicyPath, options.AuthFile)
|
||||||
|
|
||||||
|
policy, err := signature.DefaultPolicy(sc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Could not get default policy context for signature policy path %q", signaturePolicyPath)
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
policyContext, err := signature.NewPolicyContext(policy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
defer policyContext.Destroy()
|
defer policyContext.Destroy()
|
||||||
// Look up the image name and its layer, then build the imagePushData from
|
// Look up the image name and its layer, then build the imagePushData from
|
||||||
|
@ -239,7 +396,7 @@ func (r *Runtime) PushImage(source string, destination string, options CopyOptio
|
||||||
return errors.Wrapf(err, "error copying layers and metadata")
|
return errors.Wrapf(err, "error copying layers and metadata")
|
||||||
}
|
}
|
||||||
|
|
||||||
copyOptions := common.GetCopyOptions(reportWriter, signaturePolicyPath, nil, &options.DockerRegistryOptions, options.SigningOptions)
|
copyOptions := common.GetCopyOptions(options.Writer, signaturePolicyPath, nil, &options.DockerRegistryOptions, options.SigningOptions, options.AuthFile)
|
||||||
|
|
||||||
// Copy the image to the remote destination
|
// Copy the image to the remote destination
|
||||||
err = cp.Image(policyContext, dest, src, copyOptions)
|
err = cp.Image(policyContext, dest, src, copyOptions)
|
||||||
|
|
|
@ -76,3 +76,63 @@ function teardown() {
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@test "kpod pull from docker-archive" {
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull alpine
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} save -o alp.tar alpine
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} rmi alpine
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull docker-archive:alp.tar
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} rmi alpine
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
rm -f alp.tar
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "kpod pull from oci-archive" {
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull alpine
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} save --format oci-archive -o oci-alp.tar alpine
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} rmi alpine
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull oci-archive:oci-alp.tar
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} rmi alpine
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
rm -f oci-alp.tar
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "kpod pull from local directory" {
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull alpine
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run mkdir test_pull_dir
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} push alpine dir:test_pull_dir
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} rmi alpine
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} pull dir:test_pull_dir
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
run ${KPOD_BINARY} ${KPOD_OPTIONS} rmi test_pull_dir
|
||||||
|
echo "$output"
|
||||||
|
[ "$status" -eq 0 ]
|
||||||
|
rm -rf test_pull_dir
|
||||||
|
}
|
||||||
|
|
|
@ -49,17 +49,14 @@ function teardown() {
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@test "kpod push to oci without compression" {
|
@test "kpod push to oci-archive without compression" {
|
||||||
run ${KPOD_BINARY} $KPOD_OPTIONS pull "$IMAGE"
|
run ${KPOD_BINARY} $KPOD_OPTIONS pull "$IMAGE"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
run mkdir /tmp/oci-busybox
|
run ${KPOD_BINARY} $KPOD_OPTIONS push "$IMAGE" oci-archive:/tmp/oci-busybox.tar:alpine
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
run ${KPOD_BINARY} $KPOD_OPTIONS push "$IMAGE" oci:/tmp/oci-busybox:busybox
|
rm -f /tmp/oci-busybox.tar
|
||||||
echo "$output"
|
|
||||||
[ "$status" -eq 0 ]
|
|
||||||
rm -rf /tmp/oci-busybox
|
|
||||||
run ${KPOD_BINARY} $KPOD_OPTIONS rmi "$IMAGE"
|
run ${KPOD_BINARY} $KPOD_OPTIONS rmi "$IMAGE"
|
||||||
echo "$output"
|
echo "$output"
|
||||||
[ "$status" -eq 0 ]
|
[ "$status" -eq 0 ]
|
||||||
|
|
Loading…
Reference in a new issue