Merge pull request #254 from runcom/enable-cli-userpass

use user/pass flags
This commit is contained in:
Antonio Murdaca 2016-11-30 17:29:48 +01:00 committed by GitHub
commit 7cca84ba57
10 changed files with 150 additions and 49 deletions

View File

@ -49,11 +49,12 @@ Copying images
- -
`skopeo` can copy container images between various storage mechanisms, `skopeo` can copy container images between various storage mechanisms,
e.g. Docker registries (including the Docker Hub), the Atomic Registry, e.g. Docker registries (including the Docker Hub), the Atomic Registry,
and local directories: local directories, and local OCI-layout directories:
```sh ```sh
$ skopeo copy docker://busybox:1-glibc atomic:myns/unsigned:streaming $ skopeo copy docker://busybox:1-glibc atomic:myns/unsigned:streaming
$ skopeo copy docker://busybox:latest dir:existingemptydirectory $ skopeo copy docker://busybox:latest dir:existingemptydirectory
$ skopeo copy docker://busybox:latest oci:busybox_ocilayout
``` ```
Deleting images Deleting images
@ -65,14 +66,10 @@ $ skopeo delete docker://localhost:5000/imagename:latest
Private registries with authentication Private registries with authentication
- -
When interacting with private registries, `skopeo` first looks for the Docker's cli config file (usually located at `$HOME/.docker/config.json`) to get the credentials needed to authenticate. When the file isn't available it falls back looking for `--username` and `--password` flags. The ultimate fallback, as Docker does, is to provide an empty authentication when interacting with those registries. When interacting with private registries, `skopeo` first looks for `--creds` (for `skopeo inspect|delete`) or `--src-creds|--dest-creds` (for `skopeo copy`) flags. If those aren't provided, it looks for the Docker's cli config file (usually located at `$HOME/.docker/config.json`) to get the credentials needed to authenticate. The ultimate fallback, as Docker does, is to provide an empty authentication when interacting with those registries.
Examples: Examples:
```sh ```sh
# on my system
$ skopeo --help | grep docker-cfg
--docker-cfg "/home/runcom/.docker" Docker's cli config for auth
$ cat /home/runcom/.docker/config.json $ cat /home/runcom/.docker/config.json
{ {
"auths": { "auths": {
@ -88,16 +85,22 @@ $ skopeo inspect docker://myregistrydomain.com:5000/busybox
{"Tag":"latest","Digest":"sha256:473bb2189d7b913ed7187a33d11e743fdc2f88931122a44d91a301b64419f092","RepoTags":["latest"],"Comment":"","Created":"2016-01-15T18:06:41.282540103Z","ContainerConfig":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","#(nop) CMD [\"sh\"]"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"DockerVersion":"1.8.3","Author":"","Config":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["sh"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"Architecture":"amd64","Os":"linux"} {"Tag":"latest","Digest":"sha256:473bb2189d7b913ed7187a33d11e743fdc2f88931122a44d91a301b64419f092","RepoTags":["latest"],"Comment":"","Created":"2016-01-15T18:06:41.282540103Z","ContainerConfig":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","#(nop) CMD [\"sh\"]"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"DockerVersion":"1.8.3","Author":"","Config":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["sh"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"Architecture":"amd64","Os":"linux"}
# let's try now to fake a non existent Docker's config file # let's try now to fake a non existent Docker's config file
$ skopeo --docker-cfg="" inspect docker://myregistrydomain.com:5000/busybox $ cat /home/runcom/.docker/config.json
FATA[0000] Get https://myregistrydomain.com:5000/v2/busybox/manifests/latest: no basic auth credentials {}
# passing --username and --password - we can see that everything goes fine $ skopeo inspect docker://myregistrydomain.com:5000/busybox
$ skopeo --docker-cfg="" --username=testuser --password=testpassword inspect docker://myregistrydomain.com:5000/busybox FATA[0000] unauthorized: authentication required
# passing --creds - we can see that everything goes fine
$ skopeo inspect --creds=testuser:testpassword docker://myregistrydomain.com:5000/busybox
{"Tag":"latest","Digest":"sha256:473bb2189d7b913ed7187a33d11e743fdc2f88931122a44d91a301b64419f092","RepoTags":["latest"],"Comment":"","Created":"2016-01-15T18:06:41.282540103Z","ContainerConfig":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","#(nop) CMD [\"sh\"]"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"DockerVersion":"1.8.3","Author":"","Config":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["sh"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"Architecture":"amd64","Os":"linux"} {"Tag":"latest","Digest":"sha256:473bb2189d7b913ed7187a33d11e743fdc2f88931122a44d91a301b64419f092","RepoTags":["latest"],"Comment":"","Created":"2016-01-15T18:06:41.282540103Z","ContainerConfig":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","#(nop) CMD [\"sh\"]"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"DockerVersion":"1.8.3","Author":"","Config":{"Hostname":"aded96b43f48","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["sh"],"Image":"9e77fef7a1c9f989988c06620dabc4020c607885b959a2cbd7c2283c91da3e33","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"Architecture":"amd64","Os":"linux"}
# skopeo copy example:
$ skopeo copy --src-creds=testuser:testpassword docker://myregistrydomain.com:5000/private oci:local_oci_image
``` ```
If your cli config is found but it doesn't contain the necessary credentials for the queried registry If your cli config is found but it doesn't contain the necessary credentials for the queried registry
you'll get an error. You can fix this by either logging in (via `docker login`) or providing `--username` you'll get an error. You can fix this by either logging in (via `docker login`) or providing `--creds` or `--src-creds|--dest-creds`.
and `--password`.
Building Building
- -
To build the manual you will need go-md2man. To build the manual you will need go-md2man.
@ -116,7 +119,7 @@ $ brew install gpgme
$ make binary-local $ make binary-local
``` ```
You may need to install additional development packages: gpgme-devel and libassuan-devel You may need to install additional development packages: `gpgme-devel` and `libassuan-devel`
```sh ```sh
$ dnf install gpgme-devel libassuan-devel $ dnf install gpgme-devel libassuan-devel
``` ```

View File

@ -7,9 +7,25 @@ import (
"github.com/containers/image/copy" "github.com/containers/image/copy"
"github.com/containers/image/transports" "github.com/containers/image/transports"
"github.com/containers/image/types"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
// contextsFromGlobalOptions returns source and destionation types.SystemContext depending on c.
func contextsFromGlobalOptions(c *cli.Context) (*types.SystemContext, *types.SystemContext, error) {
sourceCtx, err := contextFromGlobalOptions(c, "src-creds")
if err != nil {
return nil, nil, err
}
destinationCtx, err := contextFromGlobalOptions(c, "dest-creds")
if err != nil {
return nil, nil, err
}
return sourceCtx, destinationCtx, nil
}
func copyHandler(context *cli.Context) error { func copyHandler(context *cli.Context) error {
if len(context.Args()) != 2 { if len(context.Args()) != 2 {
return errors.New("Usage: copy source destination") return errors.New("Usage: copy source destination")
@ -32,10 +48,17 @@ func copyHandler(context *cli.Context) error {
signBy := context.String("sign-by") signBy := context.String("sign-by")
removeSignatures := context.Bool("remove-signatures") removeSignatures := context.Bool("remove-signatures")
return copy.Image(contextFromGlobalOptions(context), policyContext, destRef, srcRef, &copy.Options{ sourceCtx, destinationCtx, err := contextsFromGlobalOptions(context)
if err != nil {
return err
}
return copy.Image(policyContext, destRef, srcRef, &copy.Options{
RemoveSignatures: removeSignatures, RemoveSignatures: removeSignatures,
SignBy: signBy, SignBy: signBy,
ReportWriter: os.Stdout, ReportWriter: os.Stdout,
SourceCtx: sourceCtx,
DestinationCtx: destinationCtx,
}) })
} }
@ -54,5 +77,15 @@ var copyCmd = cli.Command{
Name: "sign-by", Name: "sign-by",
Usage: "Sign the image using a GPG key with the specified `FINGERPRINT`", Usage: "Sign the image using a GPG key with the specified `FINGERPRINT`",
}, },
cli.StringFlag{
Name: "src-creds, screds",
Value: "",
Usage: "Use `USERNAME[:PASSWORD]` for accessing the source registry",
},
cli.StringFlag{
Name: "dest-creds, dcreds",
Value: "",
Usage: "Use `USERNAME[:PASSWORD]` for accessing the destination registry",
},
}, },
} }

View File

@ -18,7 +18,11 @@ func deleteHandler(context *cli.Context) error {
return fmt.Errorf("Invalid source name %s: %v", context.Args()[0], err) return fmt.Errorf("Invalid source name %s: %v", context.Args()[0], err)
} }
if err := ref.DeleteImage(contextFromGlobalOptions(context)); err != nil { ctx, err := contextFromGlobalOptions(context, "creds")
if err != nil {
return err
}
if err := ref.DeleteImage(ctx); err != nil {
return err return err
} }
return nil return nil
@ -29,4 +33,11 @@ var deleteCmd = cli.Command{
Usage: "Delete image IMAGE-NAME", Usage: "Delete image IMAGE-NAME",
ArgsUsage: "IMAGE-NAME", ArgsUsage: "IMAGE-NAME",
Action: deleteHandler, Action: deleteHandler,
Flags: []cli.Flag{
cli.StringFlag{
Name: "creds",
Value: "",
Usage: "Use `USERNAME[:PASSWORD]` for accessing the registry",
},
},
} }

View File

@ -34,6 +34,11 @@ var inspectCmd = cli.Command{
Name: "raw", Name: "raw",
Usage: "output raw manifest", Usage: "output raw manifest",
}, },
cli.StringFlag{
Name: "creds",
Value: "",
Usage: "Use `USERNAME[:PASSWORD]` for accessing the registry",
},
}, },
Action: func(c *cli.Context) error { Action: func(c *cli.Context) error {
img, err := parseImage(c) img, err := parseImage(c)

View File

@ -25,23 +25,11 @@ func createApp() *cli.App {
app.Version = version.Version app.Version = version.Version
} }
app.Usage = "Various operations with container images and container image registries" app.Usage = "Various operations with container images and container image registries"
// TODO(runcom)
//app.EnableBashCompletion = true
app.Flags = []cli.Flag{ app.Flags = []cli.Flag{
cli.BoolFlag{ cli.BoolFlag{
Name: "debug", Name: "debug",
Usage: "enable debug output", Usage: "enable debug output",
}, },
cli.StringFlag{
Name: "username",
Value: "",
Usage: "use `USERNAME` for accessing the registry",
},
cli.StringFlag{
Name: "password",
Value: "",
Usage: "use `PASSWORD` for accessing the registry",
},
cli.StringFlag{ cli.StringFlag{
Name: "cert-path", Name: "cert-path",
Value: "", Value: "",

View File

@ -1,22 +1,56 @@
package main package main
import ( import (
"errors"
"strings"
"github.com/containers/image/transports" "github.com/containers/image/transports"
"github.com/containers/image/types" "github.com/containers/image/types"
"github.com/urfave/cli" "github.com/urfave/cli"
) )
// contextFromGlobalOptions returns a types.SystemContext depending on c. func contextFromGlobalOptions(c *cli.Context, credsFlag string) (*types.SystemContext, error) {
func contextFromGlobalOptions(c *cli.Context) *types.SystemContext { ctx := &types.SystemContext{
tlsVerify := c.GlobalBoolT("tls-verify")
return &types.SystemContext{
RegistriesDirPath: c.GlobalString("registries.d"), RegistriesDirPath: c.GlobalString("registries.d"),
DockerCertPath: c.GlobalString("cert-path"), DockerCertPath: c.GlobalString("cert-path"),
DockerInsecureSkipTLSVerify: !tlsVerify, DockerInsecureSkipTLSVerify: !c.GlobalBoolT("tls-verify"),
} }
if c.IsSet(credsFlag) {
var err error
ctx.DockerAuthConfig, err = getDockerAuth(c.String(credsFlag))
if err != nil {
return nil, err
}
}
return ctx, nil
} }
// ParseImage converts image URL-like string to an initialized handler for that image. func parseCreds(creds string) (string, string, error) {
if creds == "" {
return "", "", errors.New("credentials can't be empty")
}
up := strings.SplitN(creds, ":", 2)
if len(up) == 1 {
return up[0], "", nil
}
if up[0] == "" {
return "", "", errors.New("username can't be empty")
}
return up[0], up[1], nil
}
func getDockerAuth(creds string) (*types.DockerAuthConfig, error) {
username, password, err := parseCreds(creds)
if err != nil {
return nil, err
}
return &types.DockerAuthConfig{
Username: username,
Password: password,
}, nil
}
// parseImage converts image URL-like string to an initialized handler for that image.
// The caller must call .Close() on the returned Image. // The caller must call .Close() on the returned Image.
func parseImage(c *cli.Context) (types.Image, error) { func parseImage(c *cli.Context) (types.Image, error) {
imgName := c.Args().First() imgName := c.Args().First()
@ -24,7 +58,11 @@ func parseImage(c *cli.Context) (types.Image, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
return ref.NewImage(contextFromGlobalOptions(c)) ctx, err := contextFromGlobalOptions(c, "creds")
if err != nil {
return nil, err
}
return ref.NewImage(ctx)
} }
// parseImageSource converts image URL-like string to an ImageSource. // parseImageSource converts image URL-like string to an ImageSource.
@ -35,5 +73,9 @@ func parseImageSource(c *cli.Context, name string, requestedManifestMIMETypes []
if err != nil { if err != nil {
return nil, err return nil, err
} }
return ref.NewImageSource(contextFromGlobalOptions(c), requestedManifestMIMETypes) ctx, err := contextFromGlobalOptions(c, "creds")
if err != nil {
return nil, err
}
return ref.NewImageSource(ctx, requestedManifestMIMETypes)
} }

View File

@ -37,10 +37,6 @@ Most commands refer to container images, using a _transport_`:`_details_ format.
**--debug** enable debug output **--debug** enable debug output
**--username** _username_ for accessing the registry
**--password** _password_ for accessing the registry
**--cert-path** _path_ Use certificates at _path_ (cert.pem, key.pem) to connect to the registry **--cert-path** _path_ Use certificates at _path_ (cert.pem, key.pem) to connect to the registry
**--policy** _path-to-policy_ Path to a policy.json file to use for verifying signatures and deciding whether an image is trusted, overriding the default trust policy file. **--policy** _path-to-policy_ Path to a policy.json file to use for verifying signatures and deciding whether an image is trusted, overriding the default trust policy file.
@ -70,6 +66,10 @@ Uses the system's trust policy to validate images, rejects images not trusted by
**--sign-by=**_key-id_ add a signature using that key ID for an image name corresponding to _destination-image_ **--sign-by=**_key-id_ add a signature using that key ID for an image name corresponding to _destination-image_
**--src-creds** _username[:password]_ for accessing the source registry
**--dest-creds** _username[:password]_ for accessing the destination registry
Existing signatures, if any, are preserved as well. Existing signatures, if any, are preserved as well.
## skopeo delete ## skopeo delete
@ -81,6 +81,8 @@ Mark _image-name_ for deletion. To release the allocated disk space, you need t
$ docker exec -it registry bin/registry garbage-collect /etc/docker/registry/config.yml $ docker exec -it registry bin/registry garbage-collect /etc/docker/registry/config.yml
``` ```
**--creds** _username[:password]_ for accessing the registry
Additionally, the registry must allow deletions by setting `REGISTRY_STORAGE_DELETE_ENABLED=true` for the registry daemon. Additionally, the registry must allow deletions by setting `REGISTRY_STORAGE_DELETE_ENABLED=true` for the registry daemon.
## skopeo inspect ## skopeo inspect
@ -92,6 +94,8 @@ Return low-level information about _image-name_ in a registry
_image-name_ name of image to retrieve information about _image-name_ name of image to retrieve information about
**--creds** _username[:password]_ for accessing the registry
## skopeo layers ## skopeo layers
**skopeo layers** _image-name_ **skopeo layers** _image-name_

View File

@ -76,17 +76,13 @@ func (s *SkopeoSuite) TestVersion(c *check.C) {
} }
func (s *SkopeoSuite) TestCanAuthToPrivateRegistryV2WithoutDockerCfg(c *check.C) { func (s *SkopeoSuite) TestCanAuthToPrivateRegistryV2WithoutDockerCfg(c *check.C) {
// TODO(runcom) wanted := ".*manifest unknown: manifest unknown.*"
c.Skip("we need to restore --username --password flags!") assertSkopeoFails(c, wanted, "--tls-verify=false", "inspect", "--creds="+s.regV2WithAuth.username+":"+s.regV2WithAuth.password, fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
wanted := ".*unauthorized: authentication required.*"
assertSkopeoFails(c, wanted, "--docker-cfg=''", "--username="+s.regV2WithAuth.username, "--password="+s.regV2WithAuth.password, "inspect", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
} }
func (s *SkopeoSuite) TestNeedAuthToPrivateRegistryV2WithoutDockerCfg(c *check.C) { func (s *SkopeoSuite) TestNeedAuthToPrivateRegistryV2WithoutDockerCfg(c *check.C) {
// TODO(runcom): mock the empty docker-cfg by removing it in the test itself (?)
c.Skip("mock empty docker config")
wanted := ".*unauthorized: authentication required.*" wanted := ".*unauthorized: authentication required.*"
assertSkopeoFails(c, wanted, "--docker-cfg=''", "inspect", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url)) assertSkopeoFails(c, wanted, "--tls-verify=false", "inspect", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
} }
// TODO(runcom): as soon as we can push to registries ensure you can inspect here // TODO(runcom): as soon as we can push to registries ensure you can inspect here

View File

@ -397,3 +397,20 @@ func (s *CopySuite) TestCopyDockerSigstore(c *check.C) {
splitSigstoreReadServerHandler = http.FileServer(http.Dir(splitSigstoreStaging)) splitSigstoreReadServerHandler = http.FileServer(http.Dir(splitSigstoreStaging))
assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "--registries.d", registriesDir, "copy", ourRegistry+"public/busybox", dirDest) assertSkopeoSucceeds(c, "", "--tls-verify=false", "--policy", policy, "--registries.d", registriesDir, "copy", ourRegistry+"public/busybox", dirDest)
} }
func (s *SkopeoSuite) TestCopySrcWithAuth(c *check.C) {
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "--dest-creds=testuser:testpassword", "docker://busybox", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
dir1, err := ioutil.TempDir("", "copy-1")
c.Assert(err, check.IsNil)
defer os.RemoveAll(dir1)
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "--src-creds=testuser:testpassword", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url), "dir:"+dir1)
}
func (s *SkopeoSuite) TestCopyDestWithAuth(c *check.C) {
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "--dest-creds=testuser:testpassword", "docker://busybox", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
}
func (s *SkopeoSuite) TestCopySrcAndDestWithAuth(c *check.C) {
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "--dest-creds=testuser:testpassword", "docker://busybox", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url))
assertSkopeoSucceeds(c, "", "--tls-verify=false", "copy", "--src-creds=testuser:testpassword", "--dest-creds=testuser:testpassword", fmt.Sprintf("docker://%s/busybox:latest", s.regV2WithAuth.url), fmt.Sprintf("docker://%s/test:auth", s.regV2WithAuth.url))
}

View File

@ -75,10 +75,12 @@ type Options struct {
RemoveSignatures bool // Remove any pre-existing signatures. SignBy will still add a new signature. RemoveSignatures bool // Remove any pre-existing signatures. SignBy will still add a new signature.
SignBy string // If non-empty, asks for a signature to be added during the copy, and specifies a key ID, as accepted by signature.NewGPGSigningMechanism().SignDockerManifest(), SignBy string // If non-empty, asks for a signature to be added during the copy, and specifies a key ID, as accepted by signature.NewGPGSigningMechanism().SignDockerManifest(),
ReportWriter io.Writer ReportWriter io.Writer
SourceCtx *types.SystemContext
DestinationCtx *types.SystemContext
} }
// Image copies image from srcRef to destRef, using policyContext to validate source image admissibility. // Image copies image from srcRef to destRef, using policyContext to validate source image admissibility.
func Image(ctx *types.SystemContext, policyContext *signature.PolicyContext, destRef, srcRef types.ImageReference, options *Options) error { func Image(policyContext *signature.PolicyContext, destRef, srcRef types.ImageReference, options *Options) error {
reportWriter := ioutil.Discard reportWriter := ioutil.Discard
if options != nil && options.ReportWriter != nil { if options != nil && options.ReportWriter != nil {
reportWriter = options.ReportWriter reportWriter = options.ReportWriter
@ -87,14 +89,14 @@ func Image(ctx *types.SystemContext, policyContext *signature.PolicyContext, des
fmt.Fprintf(reportWriter, f, a...) fmt.Fprintf(reportWriter, f, a...)
} }
dest, err := destRef.NewImageDestination(ctx) dest, err := destRef.NewImageDestination(options.DestinationCtx)
if err != nil { if err != nil {
return fmt.Errorf("Error initializing destination %s: %v", transports.ImageName(destRef), err) return fmt.Errorf("Error initializing destination %s: %v", transports.ImageName(destRef), err)
} }
defer dest.Close() defer dest.Close()
destSupportedManifestMIMETypes := dest.SupportedManifestMIMETypes() destSupportedManifestMIMETypes := dest.SupportedManifestMIMETypes()
rawSource, err := srcRef.NewImageSource(ctx, destSupportedManifestMIMETypes) rawSource, err := srcRef.NewImageSource(options.SourceCtx, destSupportedManifestMIMETypes)
if err != nil { if err != nil {
return fmt.Errorf("Error initializing source %s: %v", transports.ImageName(srcRef), err) return fmt.Errorf("Error initializing source %s: %v", transports.ImageName(srcRef), err)
} }