Repository: google/go-containerregistry
Pull request #645 (2019-12-20 22:50:58)

Add tests to pkg/v1/{partial,remote,transport}
This also drops a bunch of test code from legacy/tarball that the `compare` package takes care of.

codecov-io: # [Codecov](https://codecov.io/gh/google/go-containerregistry/pull/645?src=pr&el=h1) Report
> Merging [#645](https://codecov.io/gh/google/go-containerregistry/pull/645?src=pr&el=desc) into [master](https://codecov.io/gh/google/go-containerregistry/commit/34fb8ff33bed0fab41a607d21da5860b924ff694?src=pr&el=desc) will **increase** coverage by `0.95%`.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/google/go-containerregistry/pull/645/graphs/tree.svg?width=650&token=wG7iiDCdZ4&height=150&src=pr)](https://codecov.io/gh/google/go-containerregistry/pull/645?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #645      +/-   ##
==========================================
+ Coverage   76.53%   77.49%   +0.95%     
==========================================
  Files         101      101              
  Lines        4483     4483              
==========================================
+ Hits         3431     3474      +43     
+ Misses        636      588      -48     
- Partials      416      421       +5
```


| [Impacted Files](https://codecov.io/gh/google/go-containerregistry/pull/645?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pkg/v1/partial/uncompressed.go](https://codecov.io/gh/google/go-containerregistry/pull/645/diff?src=pr&el=tree#diff-cGtnL3YxL3BhcnRpYWwvdW5jb21wcmVzc2VkLmdv) | `71.42% <ø> (+24.99%)` | :arrow_up: |
| [pkg/v1/remote/index.go](https://codecov.io/gh/google/go-containerregistry/pull/645/diff?src=pr&el=tree#diff-cGtnL3YxL3JlbW90ZS9pbmRleC5nbw==) | `89.32% <0%> (+0.97%)` | :arrow_up: |
| [pkg/v1/remote/write.go](https://codecov.io/gh/google/go-containerregistry/pull/645/diff?src=pr&el=tree#diff-cGtnL3YxL3JlbW90ZS93cml0ZS5nbw==) | `67.49% <0%> (+2.47%)` | :arrow_up: |
| [pkg/v1/partial/with.go](https://codecov.io/gh/google/go-containerregistry/pull/645/diff?src=pr&el=tree#diff-cGtnL3YxL3BhcnRpYWwvd2l0aC5nbw==) | `66.66% <0%> (+2.63%)` | :arrow_up: |
| [pkg/v1/partial/compressed.go](https://codecov.io/gh/google/go-containerregistry/pull/645/diff?src=pr&el=tree#diff-cGtnL3YxL3BhcnRpYWwvY29tcHJlc3NlZC5nbw==) | `80.39% <0%> (+7.84%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/google/go-containerregistry/pull/645?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/google/go-containerregistry/pull/645?src=pr&el=footer). Last update [34fb8ff...f758456](https://codecov.io/gh/google/go-containerregistry/pull/645?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/cmd/crane/cmd/append.go b/cmd/crane/cmd/append.go
index cbbc9be3..29b18785 100644
--- a/cmd/crane/cmd/append.go
+++ b/cmd/crane/cmd/append.go
@@ -18,7 +18,6 @@ import (
 	"log"
 
 	"github.com/google/go-containerregistry/pkg/crane"
-	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/spf13/cobra"
 )
 
@@ -27,17 +26,12 @@ func init() { Root.AddCommand(NewCmdAppend()) }
 // NewCmdAppend creates a new cobra.Command for the append subcommand.
 func NewCmdAppend() *cobra.Command {
 	var baseRef, newTag, newLayer, outFile string
-	var insecure bool
 
 	appendCmd := &cobra.Command{
 		Use:   "append",
 		Short: "Append contents of a tarball to a remote image",
 		Args:  cobra.NoArgs,
 		Run: func(_ *cobra.Command, args []string) {
-			options := []name.Option{}
-			if insecure {
-				options = append(options, name.Insecure)
-			}
 			base, err := crane.Pull(baseRef, options...)
 			if err != nil {
 				log.Fatalf("pulling %s: %v", baseRef, err)
@@ -63,7 +57,6 @@ func NewCmdAppend() *cobra.Command {
 	appendCmd.Flags().StringVarP(&newTag, "new_tag", "t", "", "Tag to apply to resulting image")
 	appendCmd.Flags().StringVarP(&newLayer, "new_layer", "f", "", "Path to tarball to append to image")
 	appendCmd.Flags().StringVarP(&outFile, "output", "o", "", "Path to new tarball of resulting image")
-	appendCmd.Flags().BoolVarP(&insecure, "insecure", "i", false, "Allow image references to be fetched without TLS")
 
 	appendCmd.MarkFlagRequired("base")
 	appendCmd.MarkFlagRequired("new_tag")
diff --git a/cmd/crane/cmd/config.go b/cmd/crane/cmd/config.go
index 778b55dd..c678e134 100644
--- a/cmd/crane/cmd/config.go
+++ b/cmd/crane/cmd/config.go
@@ -31,7 +31,7 @@ func NewCmdConfig() *cobra.Command {
 		Short: "Get the config of an image",
 		Args:  cobra.ExactArgs(1),
 		Run: func(_ *cobra.Command, args []string) {
-			cfg, err := crane.Config(args[0])
+			cfg, err := crane.Config(args[0], options...)
 			if err != nil {
 				log.Fatalf("fetching config: %v", err)
 			}
diff --git a/cmd/crane/cmd/copy.go b/cmd/crane/cmd/copy.go
index b248aa8c..17d07679 100644
--- a/cmd/crane/cmd/copy.go
+++ b/cmd/crane/cmd/copy.go
@@ -32,7 +32,7 @@ func NewCmdCopy() *cobra.Command {
 		Args:    cobra.ExactArgs(2),
 		Run: func(_ *cobra.Command, args []string) {
 			src, dst := args[0], args[1]
-			if err := crane.Copy(src, dst); err != nil {
+			if err := crane.Copy(src, dst, options...); err != nil {
 				log.Fatal(err)
 			}
 		},
diff --git a/cmd/crane/cmd/delete.go b/cmd/crane/cmd/delete.go
index 705b7459..ba535f06 100644
--- a/cmd/crane/cmd/delete.go
+++ b/cmd/crane/cmd/delete.go
@@ -31,7 +31,7 @@ func NewCmdDelete() *cobra.Command {
 		Args:  cobra.ExactArgs(1),
 		Run: func(_ *cobra.Command, args []string) {
 			ref := args[0]
-			if err := crane.Delete(ref); err != nil {
+			if err := crane.Delete(ref, options...); err != nil {
 				log.Fatalf("deleting %s: %v", ref, err)
 			}
 		},
diff --git a/cmd/crane/cmd/digest.go b/cmd/crane/cmd/digest.go
index 02c1d7e1..3076c82a 100644
--- a/cmd/crane/cmd/digest.go
+++ b/cmd/crane/cmd/digest.go
@@ -31,7 +31,7 @@ func NewCmdDigest() *cobra.Command {
 		Short: "Get the digest of an image",
 		Args:  cobra.ExactArgs(1),
 		Run: func(_ *cobra.Command, args []string) {
-			digest, err := crane.Digest(args[0])
+			digest, err := crane.Digest(args[0], options...)
 			if err != nil {
 				log.Fatalf("computing digest: %v", err)
 			}
diff --git a/cmd/crane/cmd/export.go b/cmd/crane/cmd/export.go
index 3551c73c..b8ba3fbe 100644
--- a/cmd/crane/cmd/export.go
+++ b/cmd/crane/cmd/export.go
@@ -19,7 +19,6 @@ import (
 	"os"
 
 	"github.com/google/go-containerregistry/pkg/crane"
-	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/spf13/cobra"
 )
 
@@ -27,9 +26,7 @@ func init() { Root.AddCommand(NewCmdExport()) }
 
 // NewCmdExport creates a new cobra.Command for the export subcommand.
 func NewCmdExport() *cobra.Command {
-	var insecure bool
-
-	exportCmd := &cobra.Command{
+	return &cobra.Command{
 		Use:   "export IMAGE TARBALL",
 		Short: "Export contents of a remote image as a tarball",
 		Example: `  # Write tarball to stdout
@@ -39,10 +36,6 @@ func NewCmdExport() *cobra.Command {
   crane export ubuntu ubuntu.tar`,
 		Args: cobra.ExactArgs(2),
 		Run: func(_ *cobra.Command, args []string) {
-			options := []name.Option{}
-			if insecure {
-				options = append(options, name.Insecure)
-			}
 			src, dst := args[0], args[1]
 
 			f, err := openFile(dst)
@@ -61,8 +54,6 @@ func NewCmdExport() *cobra.Command {
 			}
 		},
 	}
-	exportCmd.Flags().BoolVarP(&insecure, "insecure", "i", false, "Allow image references to be fetched without TLS")
-	return exportCmd
 }
 
 func openFile(s string) (*os.File, error) {
diff --git a/cmd/crane/cmd/list.go b/cmd/crane/cmd/list.go
index 1cfbf72c..00d356a4 100644
--- a/cmd/crane/cmd/list.go
+++ b/cmd/crane/cmd/list.go
@@ -32,7 +32,7 @@ func NewCmdList() *cobra.Command {
 		Args:  cobra.ExactArgs(1),
 		Run: func(_ *cobra.Command, args []string) {
 			repo := args[0]
-			tags, err := crane.ListTags(repo)
+			tags, err := crane.ListTags(repo, options...)
 			if err != nil {
 				log.Fatalf("reading tags for %s: %v", repo, err)
 			}
diff --git a/cmd/crane/cmd/manifest.go b/cmd/crane/cmd/manifest.go
index 64be9bf6..1929e7b6 100644
--- a/cmd/crane/cmd/manifest.go
+++ b/cmd/crane/cmd/manifest.go
@@ -32,7 +32,7 @@ func NewCmdManifest() *cobra.Command {
 		Args:  cobra.ExactArgs(1),
 		Run: func(_ *cobra.Command, args []string) {
 			src := args[0]
-			manifest, err := crane.Manifest(src)
+			manifest, err := crane.Manifest(src, options...)
 			if err != nil {
 				log.Fatalf("fetching manifest %s: %v", src, err)
 			}
diff --git a/cmd/crane/cmd/pull.go b/cmd/crane/cmd/pull.go
index 751fa3d3..bb5cc767 100644
--- a/cmd/crane/cmd/pull.go
+++ b/cmd/crane/cmd/pull.go
@@ -18,7 +18,6 @@ import (
 	"log"
 
 	"github.com/google/go-containerregistry/pkg/crane"
-	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/google/go-containerregistry/pkg/v1/cache"
 	"github.com/spf13/cobra"
 )
@@ -28,17 +27,12 @@ func init() { Root.AddCommand(NewCmdPull()) }
 // NewCmdPull creates a new cobra.Command for the pull subcommand.
 func NewCmdPull() *cobra.Command {
 	var cachePath string
-	var insecure bool
 
-	pull := &cobra.Command{
+	cmd := &cobra.Command{
 		Use:   "pull IMAGE TARBALL",
 		Short: "Pull a remote image by reference and store its contents in a tarball",
 		Args:  cobra.ExactArgs(2),
 		Run: func(_ *cobra.Command, args []string) {
-			options := []name.Option{}
-			if insecure {
-				options = append(options, name.Insecure)
-			}
 			src, path := args[0], args[1]
 			img, err := crane.Pull(src, options...)
 			if err != nil {
@@ -52,7 +46,7 @@ func NewCmdPull() *cobra.Command {
 			}
 		},
 	}
-	pull.Flags().StringVarP(&cachePath, "cache_path", "c", "", "Path to cache image layers")
-	pull.Flags().BoolVarP(&insecure, "insecure", "i", false, "Allow image references to be fetched without TLS")
-	return pull
+	cmd.Flags().StringVarP(&cachePath, "cache_path", "c", "", "Path to cache image layers")
+
+	return cmd
 }
diff --git a/cmd/crane/cmd/push.go b/cmd/crane/cmd/push.go
index 06ac02ba..29546503 100644
--- a/cmd/crane/cmd/push.go
+++ b/cmd/crane/cmd/push.go
@@ -18,7 +18,6 @@ import (
 	"log"
 
 	"github.com/google/go-containerregistry/pkg/crane"
-	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/spf13/cobra"
 )
 
@@ -26,17 +25,11 @@ func init() { Root.AddCommand(NewCmdPush()) }
 
 // NewCmdPush creates a new cobra.Command for the push subcommand.
 func NewCmdPush() *cobra.Command {
-	var insecure bool
-
-	pushCmd := &cobra.Command{
+	return &cobra.Command{
 		Use:   "push TARBALL IMAGE",
 		Short: "Push image contents as a tarball to a remote registry",
 		Args:  cobra.ExactArgs(2),
 		Run: func(_ *cobra.Command, args []string) {
-			options := []name.Option{}
-			if insecure {
-				options = append(options, name.Insecure)
-			}
 			path, tag := args[0], args[1]
 			img, err := crane.Load(path)
 			if err != nil {
@@ -48,8 +41,4 @@ func NewCmdPush() *cobra.Command {
 			}
 		},
 	}
-
-	pushCmd.Flags().BoolVarP(&insecure, "insecure", "i", false, "Allow image references to be pushed without TLS")
-
-	return pushCmd
 }
diff --git a/cmd/crane/cmd/rebase.go b/cmd/crane/cmd/rebase.go
index 0c903cae..d4fdfc2f 100644
--- a/cmd/crane/cmd/rebase.go
+++ b/cmd/crane/cmd/rebase.go
@@ -19,7 +19,6 @@ import (
 	"log"
 
 	"github.com/google/go-containerregistry/pkg/crane"
-	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/google/go-containerregistry/pkg/v1/mutate"
 	"github.com/spf13/cobra"
 )
@@ -29,17 +28,12 @@ func init() { Root.AddCommand(NewCmdRebase()) }
 // NewCmdRebase creates a new cobra.Command for the rebase subcommand.
 func NewCmdRebase() *cobra.Command {
 	var orig, oldBase, newBase, rebased string
-	var insecure bool
 
 	rebaseCmd := &cobra.Command{
 		Use:   "rebase",
 		Short: "Rebase an image onto a new base image",
 		Args:  cobra.NoArgs,
 		Run: func(*cobra.Command, []string) {
-			options := []name.Option{}
-			if insecure {
-				options = append(options, name.Insecure)
-			}
 			origImg, err := crane.Pull(orig, options...)
 			if err != nil {
 				log.Fatalf("pulling %s: %v", orig, err)
@@ -75,7 +69,6 @@ func NewCmdRebase() *cobra.Command {
 	rebaseCmd.Flags().StringVarP(&oldBase, "old_base", "", "", "Old base image to remove")
 	rebaseCmd.Flags().StringVarP(&newBase, "new_base", "", "", "New base image to insert")
 	rebaseCmd.Flags().StringVarP(&rebased, "rebased", "", "", "Tag to apply to rebased image")
-	rebaseCmd.Flags().BoolVarP(&insecure, "insecure", "i", false, "Allow image references to be fetched without TLS")
 
 	rebaseCmd.MarkFlagRequired("original")
 	rebaseCmd.MarkFlagRequired("old_base")
diff --git a/cmd/crane/cmd/root.go b/cmd/crane/cmd/root.go
index 734e79dd..ae022405 100644
--- a/cmd/crane/cmd/root.go
+++ b/cmd/crane/cmd/root.go
@@ -15,16 +15,22 @@ package cmd
 import (
 	"os"
 
+	"github.com/google/go-containerregistry/pkg/crane"
 	"github.com/google/go-containerregistry/pkg/logs"
 	"github.com/spf13/cobra"
 )
 
 func init() {
 	Root.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable debug logs")
+	Root.PersistentFlags().BoolVar(&insecure, "insecure", false, "Allow image references to be fetched without TLS")
 }
 
 var (
-	verbose = false
+	verbose  = false
+	insecure = false
+
+	// Crane options for this invocation.
+	options = []crane.Option{}
 
 	// Root is the top-level cobra.Command for crane.
 	Root = &cobra.Command{
@@ -36,6 +42,9 @@ var (
 			if verbose {
 				logs.Debug.SetOutput(os.Stderr)
 			}
+			if insecure {
+				options = append(options, crane.Insecure)
+			}
 		},
 	}
 )
diff --git a/cmd/crane/cmd/tag.go b/cmd/crane/cmd/tag.go
index cd0a14a0..0b2411fa 100644
--- a/cmd/crane/cmd/tag.go
+++ b/cmd/crane/cmd/tag.go
@@ -42,7 +42,7 @@ crane tag ubuntu v1`,
 		Args: cobra.ExactArgs(2),
 		Run: func(_ *cobra.Command, args []string) {
 			img, tag := args[0], args[1]
-			if err := crane.Tag(img, tag); err != nil {
+			if err := crane.Tag(img, tag, options...); err != nil {
 				log.Fatal(err)
 			}
 		},
diff --git a/cmd/crane/cmd/validate.go b/cmd/crane/cmd/validate.go
index 7a3e4856..3edcdade 100644
--- a/cmd/crane/cmd/validate.go
+++ b/cmd/crane/cmd/validate.go
@@ -19,7 +19,6 @@ import (
 	"log"
 
 	"github.com/google/go-containerregistry/pkg/crane"
-	"github.com/google/go-containerregistry/pkg/name"
 	v1 "github.com/google/go-containerregistry/pkg/v1"
 	"github.com/google/go-containerregistry/pkg/v1/tarball"
 	"github.com/google/go-containerregistry/pkg/v1/validate"
@@ -31,18 +30,13 @@ func init() { Root.AddCommand(NewCmdValidate()) }
 // NewCmdValidate creates a new cobra.Command for the validate subcommand.
 func NewCmdValidate() *cobra.Command {
 	var tarballPath, remoteRef string
-	var insecure bool
 
 	validateCmd := &cobra.Command{
 		Use:   "validate",
 		Short: "Validate that an image is well-formed",
 		Args:  cobra.ExactArgs(0),
 		Run: func(_ *cobra.Command, args []string) {
-			options := []name.Option{}
-			if insecure {
-				options = append(options, name.Insecure)
-			}
-			for flag, maker := range map[string]func(string, ...name.Option) (v1.Image, error){
+			for flag, maker := range map[string]func(string, ...crane.Option) (v1.Image, error){
 				tarballPath: makeTarball,
 				remoteRef:   crane.Pull,
 			} {
@@ -64,11 +58,10 @@ func NewCmdValidate() *cobra.Command {
 	}
 	validateCmd.Flags().StringVar(&tarballPath, "tarball", "", "Path to tarball to validate")
 	validateCmd.Flags().StringVar(&remoteRef, "remote", "", "Name of remote image to validate")
-	validateCmd.Flags().BoolVarP(&insecure, "insecure", "i", false, "Allow image references to be fetched without TLS")
 
 	return validateCmd
 }
 
-func makeTarball(path string, opts ...name.Option) (v1.Image, error) {
+func makeTarball(path string, opts ...crane.Option) (v1.Image, error) {
 	return tarball.ImageFromPath(path, nil)
 }
diff --git a/pkg/crane/catalog.go b/pkg/crane/catalog.go
index c08cb094..528722f6 100644
--- a/pkg/crane/catalog.go
+++ b/pkg/crane/catalog.go
@@ -17,17 +17,17 @@ package crane
 import (
 	"context"
 
-	"github.com/google/go-containerregistry/pkg/authn"
 	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/google/go-containerregistry/pkg/v1/remote"
 )
 
 // Catalog returns the repositories in a registry's catalog.
-func Catalog(src string) (res []string, err error) {
-	reg, err := name.NewRegistry(src)
+func Catalog(src string, opt ...Option) (res []string, err error) {
+	o := makeOptions(opt...)
+	reg, err := name.NewRegistry(src, o.name...)
 	if err != nil {
 		return nil, err
 	}
 
-	return remote.Catalog(context.TODO(), reg, remote.WithAuthFromKeychain(authn.DefaultKeychain))
+	return remote.Catalog(context.TODO(), reg, o.remote...)
 }
diff --git a/pkg/crane/config.go b/pkg/crane/config.go
index fe51a13f..3e55cc93 100644
--- a/pkg/crane/config.go
+++ b/pkg/crane/config.go
@@ -15,8 +15,8 @@
 package crane
 
 // Config returns the config file for the remote image ref.
-func Config(ref string) ([]byte, error) {
-	i, _, err := getImage(ref)
+func Config(ref string, opt ...Option) ([]byte, error) {
+	i, _, err := getImage(ref, opt...)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/crane/copy.go b/pkg/crane/copy.go
index 03cfac41..9db128fe 100644
--- a/pkg/crane/copy.go
+++ b/pkg/crane/copy.go
@@ -26,19 +26,20 @@ import (
 )
 
 // Copy copies a remote image or index from src to dst.
-func Copy(src, dst string) error {
-	srcRef, err := name.ParseReference(src)
+func Copy(src, dst string, opt ...Option) error {
+	o := makeOptions(opt...)
+	srcRef, err := name.ParseReference(src, o.name...)
 	if err != nil {
 		return fmt.Errorf("parsing reference %q: %v", src, err)
 	}
 
-	dstRef, err := name.ParseReference(dst)
+	dstRef, err := name.ParseReference(dst, o.name...)
 	if err != nil {
 		return fmt.Errorf("parsing reference for %q: %v", dst, err)
 	}
 
 	logs.Progress.Printf("Copying from %v to %v", srcRef, dstRef)
-	desc, err := remote.Get(srcRef, remote.WithAuthFromKeychain(authn.DefaultKeychain))
+	desc, err := remote.Get(srcRef, o.remote...)
 	if err != nil {
 		return fmt.Errorf("fetching %q: %v", src, err)
 	}
@@ -46,7 +47,7 @@ func Copy(src, dst string) error {
 	switch desc.MediaType {
 	case types.OCIImageIndex, types.DockerManifestList:
 		// Handle indexes separately.
-		if err := copyIndex(desc, dstRef); err != nil {
+		if err := copyIndex(desc, dstRef, o); err != nil {
 			return fmt.Errorf("failed to copy index: %v", err)
 		}
 	case types.DockerManifestSchema1, types.DockerManifestSchema1Signed:
@@ -56,7 +57,7 @@ func Copy(src, dst string) error {
 		}
 	default:
 		// Assume anything else is an image, since some registries don't set mediaTypes properly.
-		if err := copyImage(desc, dstRef); err != nil {
+		if err := copyImage(desc, dstRef, o); err != nil {
 			return fmt.Errorf("failed to copy image: %v", err)
 		}
 	}
@@ -64,20 +65,20 @@ func Copy(src, dst string) error {
 	return nil
 }
 
-func copyImage(desc *remote.Descriptor, dstRef name.Reference) error {
+func copyImage(desc *remote.Descriptor, dstRef name.Reference, o options) error {
 	img, err := desc.Image()
 	if err != nil {
 		return err
 	}
-	return remote.Write(dstRef, img, remote.WithAuthFromKeychain(authn.DefaultKeychain))
+	return remote.Write(dstRef, img, o.remote...)
 }
 
-func copyIndex(desc *remote.Descriptor, dstRef name.Reference) error {
+func copyIndex(desc *remote.Descriptor, dstRef name.Reference, o options) error {
 	idx, err := desc.ImageIndex()
 	if err != nil {
 		return err
 	}
-	return remote.WriteIndex(dstRef, idx, remote.WithAuthFromKeychain(authn.DefaultKeychain))
+	return remote.WriteIndex(dstRef, idx, o.remote...)
 }
 
 func copySchema1(desc *remote.Descriptor, srcRef, dstRef name.Reference) error {
diff --git a/pkg/crane/delete.go b/pkg/crane/delete.go
index 8a86330f..487c888c 100644
--- a/pkg/crane/delete.go
+++ b/pkg/crane/delete.go
@@ -17,17 +17,17 @@ package crane
 import (
 	"fmt"
 
-	"github.com/google/go-containerregistry/pkg/authn"
 	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/google/go-containerregistry/pkg/v1/remote"
 )
 
 // Delete deletes the remote reference at src.
-func Delete(src string) error {
-	ref, err := name.ParseReference(src)
+func Delete(src string, opt ...Option) error {
+	o := makeOptions(opt...)
+	ref, err := name.ParseReference(src, o.name...)
 	if err != nil {
 		return fmt.Errorf("parsing reference %q: %v", src, err)
 	}
 
-	return remote.Delete(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
+	return remote.Delete(ref, o.remote...)
 }
diff --git a/pkg/crane/digest.go b/pkg/crane/digest.go
index 4900fe92..1b794fc1 100644
--- a/pkg/crane/digest.go
+++ b/pkg/crane/digest.go
@@ -15,8 +15,8 @@
 package crane
 
 // Digest returns the sha256 hash of the remote image at ref.
-func Digest(ref string) (string, error) {
-	desc, err := getManifest(ref)
+func Digest(ref string, opt ...Option) (string, error) {
+	desc, err := getManifest(ref, opt...)
 	if err != nil {
 		return "", err
 	}
diff --git a/pkg/crane/get.go b/pkg/crane/get.go
index 200a1ccd..6b70ba6b 100644
--- a/pkg/crane/get.go
+++ b/pkg/crane/get.go
@@ -17,28 +17,29 @@ package crane
 import (
 	"fmt"
 
-	"github.com/google/go-containerregistry/pkg/authn"
 	"github.com/google/go-containerregistry/pkg/name"
 	v1 "github.com/google/go-containerregistry/pkg/v1"
 	"github.com/google/go-containerregistry/pkg/v1/remote"
 )
 
-func getImage(r string) (v1.Image, name.Reference, error) {
-	ref, err := name.ParseReference(r)
+func getImage(r string, opt ...Option) (v1.Image, name.Reference, error) {
+	o := makeOptions(opt...)
+	ref, err := name.ParseReference(r, o.name...)
 	if err != nil {
 		return nil, nil, fmt.Errorf("parsing reference %q: %v", r, err)
 	}
-	img, err := remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
+	img, err := remote.Image(ref, o.remote...)
 	if err != nil {
 		return nil, nil, fmt.Errorf("reading image %q: %v", ref, err)
 	}
 	return img, ref, nil
 }
 
-func getManifest(r string) (*remote.Descriptor, error) {
-	ref, err := name.ParseReference(r)
+func getManifest(r string, opt ...Option) (*remote.Descriptor, error) {
+	o := makeOptions(opt...)
+	ref, err := name.ParseReference(r, o.name...)
 	if err != nil {
 		return nil, fmt.Errorf("parsing reference %q: %v", r, err)
 	}
-	return remote.Get(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
+	return remote.Get(ref, o.remote...)
 }
diff --git a/pkg/crane/list.go b/pkg/crane/list.go
index 76a0f076..5e3a40df 100644
--- a/pkg/crane/list.go
+++ b/pkg/crane/list.go
@@ -17,17 +17,17 @@ package crane
 import (
 	"fmt"
 
-	"github.com/google/go-containerregistry/pkg/authn"
 	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/google/go-containerregistry/pkg/v1/remote"
 )
 
 // ListTags returns the tags in repository src.
-func ListTags(src string) ([]string, error) {
-	repo, err := name.NewRepository(src)
+func ListTags(src string, opt ...Option) ([]string, error) {
+	o := makeOptions(opt...)
+	repo, err := name.NewRepository(src, o.name...)
 	if err != nil {
 		return nil, fmt.Errorf("parsing repo %q: %v", src, err)
 	}
 
-	return remote.List(repo, remote.WithAuthFromKeychain(authn.DefaultKeychain))
+	return remote.List(repo, o.remote...)
 }
diff --git a/pkg/crane/manifest.go b/pkg/crane/manifest.go
index 34aa3682..bbf59dbe 100644
--- a/pkg/crane/manifest.go
+++ b/pkg/crane/manifest.go
@@ -15,8 +15,8 @@
 package crane
 
 // Manifest returns the manifest for the remote image or index ref.
-func Manifest(ref string) ([]byte, error) {
-	desc, err := getManifest(ref)
+func Manifest(ref string, opt ...Option) ([]byte, error) {
+	desc, err := getManifest(ref, opt...)
 	if err != nil {
 		return nil, err
 	}
diff --git a/pkg/crane/options.go b/pkg/crane/options.go
new file mode 100644
index 00000000..95954f2c
--- /dev/null
+++ b/pkg/crane/options.go
@@ -0,0 +1,56 @@
+// Copyright 2019 Google LLC All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package crane
+
+import (
+	"net/http"
+
+	"github.com/google/go-containerregistry/pkg/authn"
+	"github.com/google/go-containerregistry/pkg/name"
+	"github.com/google/go-containerregistry/pkg/v1/remote"
+)
+
+type options struct {
+	name   []name.Option
+	remote []remote.Option
+}
+
+func makeOptions(opts ...Option) options {
+	opt := options{
+		remote: []remote.Option{
+			remote.WithAuthFromKeychain(authn.DefaultKeychain),
+		},
+	}
+	for _, o := range opts {
+		o(&opt)
+	}
+	return opt
+}
+
+// Option is a functional option for crane.
+type Option func(*options)
+
+// WithTransport is a functional option for overriding the default transport
+// for remote operations.
+func WithTransport(t http.RoundTripper) Option {
+	return func(o *options) {
+		o.remote = append(o.remote, remote.WithTransport(t))
+	}
+}
+
+// Insecure is an Option that allows image references to be fetched without TLS.
+func Insecure(o *options) {
+	o.name = append(o.name, name.Insecure)
+}
diff --git a/pkg/crane/pull.go b/pkg/crane/pull.go
index 9f18003f..85877daa 100644
--- a/pkg/crane/pull.go
+++ b/pkg/crane/pull.go
@@ -17,7 +17,6 @@ package crane
 import (
 	"fmt"
 
-	"github.com/google/go-containerregistry/pkg/authn"
 	"github.com/google/go-containerregistry/pkg/name"
 	v1 "github.com/google/go-containerregistry/pkg/v1"
 	"github.com/google/go-containerregistry/pkg/v1/remote"
@@ -30,13 +29,14 @@ import (
 const iWasADigestTag = "i-was-a-digest"
 
 // Pull returns a v1.Image of the remote image src.
-func Pull(src string, opts ...name.Option) (v1.Image, error) {
-	ref, err := name.ParseReference(src, opts...)
+func Pull(src string, opt ...Option) (v1.Image, error) {
+	o := makeOptions(opt...)
+	ref, err := name.ParseReference(src, o.name...)
 	if err != nil {
 		return nil, fmt.Errorf("parsing tag %q: %v", src, err)
 	}
 
-	return remote.Image(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
+	return remote.Image(ref, o.remote...)
 }
 
 // Save writes the v1.Image img as a tarball at path with tag src.
diff --git a/pkg/crane/push.go b/pkg/crane/push.go
index 5c1b19f7..52f4fc48 100644
--- a/pkg/crane/push.go
+++ b/pkg/crane/push.go
@@ -17,7 +17,6 @@ package crane
 import (
 	"fmt"
 
-	"github.com/google/go-containerregistry/pkg/authn"
 	"github.com/google/go-containerregistry/pkg/name"
 	v1 "github.com/google/go-containerregistry/pkg/v1"
 	"github.com/google/go-containerregistry/pkg/v1/remote"
@@ -31,11 +30,11 @@ func Load(path string) (v1.Image, error) {
 }
 
 // Push pushes the v1.Image img to a registry as dst.
-func Push(img v1.Image, dst string, options ...name.Option) error {
-	tag, err := name.NewTag(dst, options...)
+func Push(img v1.Image, dst string, opt ...Option) error {
+	o := makeOptions(opt...)
+	tag, err := name.NewTag(dst, o.name...)
 	if err != nil {
 		return fmt.Errorf("parsing tag %q: %v", dst, err)
 	}
-
-	return remote.Write(tag, img, remote.WithAuthFromKeychain(authn.DefaultKeychain))
+	return remote.Write(tag, img, o.remote...)
 }
diff --git a/pkg/crane/tag.go b/pkg/crane/tag.go
index 3f3d819d..d3e9abb3 100644
--- a/pkg/crane/tag.go
+++ b/pkg/crane/tag.go
@@ -17,23 +17,23 @@ package crane
 import (
 	"fmt"
 
-	"github.com/google/go-containerregistry/pkg/authn"
 	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/google/go-containerregistry/pkg/v1/remote"
 )
 
 // Tag adds tag to the remote img.
-func Tag(img, tag string) error {
-	ref, err := name.ParseReference(img)
+func Tag(img, tag string, opt ...Option) error {
+	o := makeOptions(opt...)
+	ref, err := name.ParseReference(img, o.name...)
 	if err != nil {
 		return fmt.Errorf("parsing reference %q: %v", img, err)
 	}
-	desc, err := remote.Get(ref, remote.WithAuthFromKeychain(authn.DefaultKeychain))
+	desc, err := remote.Get(ref, o.remote...)
 	if err != nil {
 		return fmt.Errorf("fetching %q: %v", img, err)
 	}
 
 	dst := ref.Context().Tag(tag)
 
-	return remote.Tag(dst, desc, remote.WithAuthFromKeychain(authn.DefaultKeychain))
+	return remote.Tag(dst, desc, o.remote...)
 }
diff --git a/pkg/v1/partial/uncompressed.go b/pkg/v1/partial/uncompressed.go
index ab82b28e..ef1704c9 100644
--- a/pkg/v1/partial/uncompressed.go
+++ b/pkg/v1/partial/uncompressed.go
@@ -188,6 +188,7 @@ func (i *uncompressedImageExtender) Manifest() (*v1.Manifest, error) {
 			m.Layers[i] = *desc
 			continue
 		}
+		// TODO: This is unreachable.
 		sz, err := l.Size()
 		if err != nil {
 			return nil, err
diff --git a/pkg/v1/remote/transport/bearer.go b/pkg/v1/remote/transport/bearer.go
index 9c404df5..c31a609c 100644
--- a/pkg/v1/remote/transport/bearer.go
+++ b/pkg/v1/remote/transport/bearer.go
@@ -33,7 +33,7 @@ type bearerTransport struct {
 	// Basic credentials that we exchange for bearer tokens.
 	basic authn.Authenticator
 	// Holds the bearer response from the token service.
-	bearer *authn.Bearer
+	bearer authn.AuthConfig
 	// Registry to which we send bearer tokens.
 	registry name.Registry
 	// See https://tools.ietf.org/html/rfc6750#section-3
@@ -61,11 +61,7 @@ func (bt *bearerTransport) RoundTrip(in *http.Request) (*http.Response, error) {
 		// the registry with which we are interacting.
 		// In case of redirect http.Client can use an empty Host, check URL too.
 		if matchesHost(bt.registry, in, bt.scheme) {
-			auth, err := bt.bearer.Authorization()
-			if err != nil {
-				return nil, err
-			}
-			hdr := fmt.Sprintf("Bearer %s", auth.RegistryToken)
+			hdr := fmt.Sprintf("Bearer %s", bt.bearer.RegistryToken)
 			in.Header.Set("Authorization", hdr)
 		}
 		return bt.inner.RoundTrip(in)
@@ -134,9 +130,8 @@ func (bt *bearerTransport) refresh() error {
 	}
 
 	// Find a token to turn into a Bearer authenticator
-	var bearer authn.Bearer
 	if response.Token != "" {
-		bearer = authn.Bearer{Token: response.Token}
+		bt.bearer.RegistryToken = response.Token
 	} else {
 		return fmt.Errorf("no token in bearer response:\n%s", content)
 	}
@@ -148,8 +143,6 @@ func (bt *bearerTransport) refresh() error {
 		})
 	}
 
-	// Replace our old bearer authenticator (if we had one) with our newly refreshed authenticator.
-	bt.bearer = &bearer
 	return nil
 }
 


Test patch:
diff --git a/pkg/crane/crane_test.go b/pkg/crane/crane_test.go
index 1e61f9e7..b9f4d2a4 100644
--- a/pkg/crane/crane_test.go
+++ b/pkg/crane/crane_test.go
@@ -109,7 +109,7 @@ func TestCraneRegistry(t *testing.T) {
 	}
 
 	// Make sure what we copied is equivalent.
-	copied, err := crane.Pull(dst)
+	copied, err := crane.Pull(dst, crane.Insecure, crane.WithTransport(http.DefaultTransport))
 	if err != nil {
 		t.Fatal(err)
 	}
diff --git a/pkg/legacy/tarball/write_test.go b/pkg/legacy/tarball/write_test.go
index d261415b..3b9ce495 100644
--- a/pkg/legacy/tarball/write_test.go
+++ b/pkg/legacy/tarball/write_test.go
@@ -20,7 +20,7 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-containerregistry/pkg/internal/compare"
 	"github.com/google/go-containerregistry/pkg/name"
 	v1 "github.com/google/go-containerregistry/pkg/v1"
 	"github.com/google/go-containerregistry/pkg/v1/mutate"
@@ -65,24 +65,12 @@ func TestWrite(t *testing.T) {
 		if err != nil {
 			t.Fatalf("Unexpected error reading tarball: %v", err)
 		}
-
-		tarManifest, err := tarImage.Manifest()
-		if err != nil {
-			t.Fatalf("Unexpected error reading tarball: %v", err)
-		}
-		randManifest, err := randImage.Manifest()
-		if err != nil {
-			t.Fatalf("Unexpected error reading tarball: %v", err)
-		}
-
-		if diff := cmp.Diff(randManifest, tarManifest); diff != "" {
-			t.Errorf("Manifests not equal. (-rand +tar) %s", diff)
-		}
-
 		if err := validate.Image(tarImage); err != nil {
 			t.Errorf("validate.Image: %v", err)
 		}
-		assertLayersAreIdentical(t, randImage, tarImage)
+		if err := compare.Images(randImage, tarImage); err != nil {
+			t.Errorf("compare.Images: %v", err)
+		}
 	}
 
 	// Try loading a different tag, it should error.
@@ -149,25 +137,12 @@ func TestMultiWriteSameImage(t *testing.T) {
 		if err != nil {
 			t.Fatalf("Unexpected error reading tarball: %v", err)
 		}
-
-		tarManifest, err := tarImage.Manifest()
-		if err != nil {
-			t.Fatalf("Unexpected error reading tarball: %v", err)
-		}
-		randManifest, err := randImage.Manifest()
-		if err != nil {
-			t.Fatalf("Unexpected error reading tarball: %v", err)
-		}
-
-		if diff := cmp.Diff(randManifest, tarManifest); diff != "" {
-			t.Errorf("Manifests not equal. (-rand +tar) %s", diff)
-		}
-
 		if err := validate.Image(tarImage); err != nil {
 			t.Errorf("validate.Image: %v", err)
 		}
-
-		assertLayersAreIdentical(t, randImage, tarImage)
+		if err := compare.Images(randImage, tarImage); err != nil {
+			t.Errorf("compare.Images: %v", err)
+		}
 	}
 }
 
@@ -237,24 +212,12 @@ func TestMultiWriteDifferentImages(t *testing.T) {
 		if err != nil {
 			t.Fatalf("Unexpected error reading tarball: %v", err)
 		}
-
-		tarManifest, err := tarImage.Manifest()
-		if err != nil {
-			t.Fatalf("Unexpected error reading tarball: %v", err)
-		}
-		randManifest, err := img.Manifest()
-		if err != nil {
-			t.Fatalf("Unexpected error reading tarball: %v", err)
-		}
-
-		if diff := cmp.Diff(randManifest, tarManifest); diff != "" {
-			t.Errorf("Manifests not equal. (-rand +tar) %s", diff)
-		}
-
 		if err := validate.Image(tarImage); err != nil {
 			t.Errorf("validate.Image: %v", err)
 		}
-		assertLayersAreIdentical(t, img, tarImage)
+		if err := compare.Images(img, tarImage); err != nil {
+			t.Errorf("compare.Images: %v", err)
+		}
 	}
 }
 
@@ -438,55 +401,3 @@ func TestMultiWriteMismatchedHistory(t *testing.T) {
 		t.Errorf("Got unexpected error when writing image with mismatched history & layer, got %v, want substring %q", err, want)
 	}
 }
-
-func assertLayersAreIdentical(t *testing.T, a, b v1.Image) {
-	t.Helper()
-
-	aLayers, err := a.Layers()
-	if err != nil {
-		t.Fatalf("error getting layers to compare: %v", err)
-	}
-
-	bLayers, err := b.Layers()
-	if err != nil {
-		t.Fatalf("error getting layers to compare: %v", err)
-	}
-
-	if diff := cmp.Diff(getDigests(t, aLayers), getDigests(t, bLayers)); diff != "" {
-		t.Fatalf("layers digests are not identical (-rand +tar) %s", diff)
-	}
-
-	if diff := cmp.Diff(getDiffIDs(t, aLayers), getDiffIDs(t, bLayers)); diff != "" {
-		t.Fatalf("layers digests are not identical (-rand +tar) %s", diff)
-	}
-}
-
-func getDigests(t *testing.T, layers []v1.Layer) []v1.Hash {
-	t.Helper()
-
-	digests := make([]v1.Hash, 0, len(layers))
-	for _, layer := range layers {
-		digest, err := layer.Digest()
-		if err != nil {
-			t.Fatalf("error getting digests: %s", err)
-		}
-		digests = append(digests, digest)
-	}
-
-	return digests
-}
-
-func getDiffIDs(t *testing.T, layers []v1.Layer) []v1.Hash {
-	t.Helper()
-
-	diffIDs := make([]v1.Hash, 0, len(layers))
-	for _, layer := range layers {
-		diffID, err := layer.DiffID()
-		if err != nil {
-			t.Fatalf("error getting diffID: %s", err)
-		}
-		diffIDs = append(diffIDs, diffID)
-	}
-
-	return diffIDs
-}
diff --git a/pkg/v1/partial/compressed_test.go b/pkg/v1/partial/compressed_test.go
index 42e5cf98..005f50cd 100644
--- a/pkg/v1/partial/compressed_test.go
+++ b/pkg/v1/partial/compressed_test.go
@@ -15,13 +15,18 @@
 package partial_test
 
 import (
+	"io"
 	"testing"
 
 	"github.com/google/go-cmp/cmp"
+	"github.com/google/go-containerregistry/pkg/internal/compare"
 	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/google/go-containerregistry/pkg/registry"
+	v1 "github.com/google/go-containerregistry/pkg/v1"
+	"github.com/google/go-containerregistry/pkg/v1/partial"
 	"github.com/google/go-containerregistry/pkg/v1/random"
 	"github.com/google/go-containerregistry/pkg/v1/remote"
+	"github.com/google/go-containerregistry/pkg/v1/types"
 	"github.com/google/go-containerregistry/pkg/v1/validate"
 )
 
@@ -75,3 +80,35 @@ func TestRemote(t *testing.T) {
 		t.Errorf("mismatched digest: %v", diff)
 	}
 }
+
+type noDiffID struct {
+	l v1.Layer
+}
+
+func (l *noDiffID) Digest() (v1.Hash, error) {
+	return l.l.Digest()
+}
+func (l *noDiffID) Compressed() (io.ReadCloser, error) {
+	return l.l.Compressed()
+}
+func (l *noDiffID) Size() (int64, error) {
+	return l.l.Size()
+}
+func (l *noDiffID) MediaType() (types.MediaType, error) {
+	return l.l.MediaType()
+}
+
+func TestCompressedLayerExtender(t *testing.T) {
+	rnd, err := random.Layer(1000, types.OCILayer)
+	if err != nil {
+		t.Fatal(err)
+	}
+	l, err := partial.CompressedToLayer(&noDiffID{rnd})
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if err := compare.Layers(rnd, l); err != nil {
+		t.Fatalf("compare.Layers: %v", err)
+	}
+}
diff --git a/pkg/v1/partial/uncompressed_test.go b/pkg/v1/partial/uncompressed_test.go
new file mode 100644
index 00000000..a758df52
--- /dev/null
+++ b/pkg/v1/partial/uncompressed_test.go
@@ -0,0 +1,121 @@
+// Copyright 2019 Google LLC All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//    http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package partial_test
+
+import (
+	"io/ioutil"
+	"os"
+	"testing"
+
+	"github.com/google/go-containerregistry/pkg/internal/compare"
+	legacy "github.com/google/go-containerregistry/pkg/legacy/tarball"
+	"github.com/google/go-containerregistry/pkg/name"
+	v1 "github.com/google/go-containerregistry/pkg/v1"
+	"github.com/google/go-containerregistry/pkg/v1/partial"
+	"github.com/google/go-containerregistry/pkg/v1/random"
+	"github.com/google/go-containerregistry/pkg/v1/tarball"
+	"github.com/google/go-containerregistry/pkg/v1/types"
+	"github.com/google/go-containerregistry/pkg/v1/validate"
+)
+
+// legacy/tarball.Write + tarball.Image leverages a lot of uncompressed partials.
+//
+// This is cribbed from pkg/legacy/tarball just to get intra-package coverage.
+func TestLegacyWrite(t *testing.T) {
+	// Make a tempfile for tarball writes.
+	fp, err := ioutil.TempFile("", "")
+	if err != nil {
+		t.Fatalf("Error creating temp file.")
+	}
+	t.Log(fp.Name())
+	defer fp.Close()
+	defer os.Remove(fp.Name())
+
+	// Make a random image
+	randImage, err := random.Image(256, 8)
+	if err != nil {
+		t.Fatalf("Error creating random image: %v", err)
+	}
+	tag, err := name.NewTag("gcr.io/foo/bar:latest", name.StrictValidation)
+	if err != nil {
+		t.Fatalf("Error creating test tag: %v", err)
+	}
+	o, err := os.Create(fp.Name())
+	if err != nil {
+		t.Fatalf("Error creating %q to write image tarball: %v", fp.Name(), err)
+	}
+	defer o.Close()
+	if err := legacy.Write(tag, randImage, o); err != nil {
+		t.Fatalf("Unexpected error writing tarball: %v", err)
+	}
+
+	// Make sure the image is valid and can be loaded.
+	// Load it both by nil and by its name.
+	for _, it := range []*name.Tag{nil, &tag} {
+		tarImage, err := tarball.ImageFromPath(fp.Name(), it)
+		if err != nil {
+			t.Fatalf("Unexpected error reading tarball: %v", err)
+		}
+		if err := validate.Image(tarImage); err != nil {
+			t.Errorf("validate.Image: %v", err)
+		}
+		if err := compare.Images(randImage, tarImage); err != nil {
+			t.Errorf("compare.Images: %v", err)
+		}
+	}
+
+	// Try loading a different tag, it should error.
+	fakeTag, err := name.NewTag("gcr.io/notthistag:latest", name.StrictValidation)
+	if err != nil {
+		t.Fatalf("Error generating tag: %v", err)
+	}
+	if _, err := tarball.ImageFromPath(fp.Name(), &fakeTag); err == nil {
+		t.Errorf("Expected error loading tag %v from image", fakeTag)
+	}
+}
+
+type uncompressedImage struct {
+	img v1.Image
+}
+
+func (i *uncompressedImage) RawConfigFile() ([]byte, error) {
+	return i.img.RawConfigFile()
+}
+
+func (i *uncompressedImage) MediaType() (types.MediaType, error) {
+	return i.img.MediaType()
+}
+
+func (i *uncompressedImage) LayerByDiffID(h v1.Hash) (partial.UncompressedLayer, error) {
+	return i.img.LayerByDiffID(h)
+}
+
+func TestUncompressed(t *testing.T) {
+	rnd, err := random.Image(1024, 1)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	core := &uncompressedImage{rnd}
+
+	img, err := partial.UncompressedToImage(core)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if err := validate.Image(img); err != nil {
+		t.Fatalf("validate.Image: %v", err)
+	}
+}
diff --git a/pkg/v1/remote/transport/basic_test.go b/pkg/v1/remote/transport/basic_test.go
index 72edfae7..68dd90e0 100644
--- a/pkg/v1/remote/transport/basic_test.go
+++ b/pkg/v1/remote/transport/basic_test.go
@@ -63,8 +63,9 @@ func TestBasicTransport(t *testing.T) {
 func TestBasicTransportRegistryToken(t *testing.T) {
 	token := "mytoken"
 	for _, tc := range []struct {
-		auth authn.Authenticator
-		hdr  string
+		auth    authn.Authenticator
+		hdr     string
+		wantErr bool
 	}{{
 		auth: authn.FromConfig(authn.AuthConfig{RegistryToken: token}),
 		hdr:  "Bearer mytoken",
@@ -74,6 +75,10 @@ func TestBasicTransportRegistryToken(t *testing.T) {
 	}, {
 		auth: authn.Anonymous,
 		hdr:  "",
+	}, {
+		auth:    &badAuth{},
+		hdr:     "",
+		wantErr: true,
 	}} {
 		server := httptest.NewServer(
 			http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
@@ -99,7 +104,7 @@ func TestBasicTransportRegistryToken(t *testing.T) {
 		client := http.Client{Transport: &basicTransport{inner: inner, auth: tc.auth, target: "gcr.io"}}
 
 		_, err := client.Get("http://gcr.io/v2/auth")
-		if err != nil {
+		if err != nil && !tc.wantErr {
 			t.Errorf("Unexpected error during Get: %v", err)
 		}
 	}
diff --git a/pkg/v1/remote/transport/bearer_test.go b/pkg/v1/remote/transport/bearer_test.go
index 246e90d5..3a3540b2 100644
--- a/pkg/v1/remote/transport/bearer_test.go
+++ b/pkg/v1/remote/transport/bearer_test.go
@@ -125,10 +125,9 @@ func TestBearerTransport(t *testing.T) {
 		t.Errorf("Unexpected error during NewRegistry: %v", err)
 	}
 
-	bearer := &authn.Bearer{Token: expectedToken}
 	client := http.Client{Transport: &bearerTransport{
 		inner:    &http.Transport{},
-		bearer:   bearer,
+		bearer:   authn.AuthConfig{RegistryToken: expectedToken},
 		registry: registry,
 		scheme:   "http",
 	}}
@@ -172,10 +171,9 @@ func TestBearerTransportTokenRefresh(t *testing.T) {
 		t.Fatalf("Unexpected error during NewRegistry: %v", err)
 	}
 
-	bearer := &authn.Bearer{Token: initialToken}
 	transport := &bearerTransport{
 		inner:    http.DefaultTransport,
-		bearer:   bearer,
+		bearer:   authn.AuthConfig{RegistryToken: initialToken},
 		basic:    &authn.Basic{Username: "foo", Password: "bar"},
 		registry: registry,
 		realm:    server.URL,
@@ -190,8 +188,8 @@ func TestBearerTransportTokenRefresh(t *testing.T) {
 	if res.StatusCode != http.StatusOK {
 		t.Errorf("client.Get final StatusCode got %v, want: %v", res.StatusCode, http.StatusOK)
 	}
-	if transport.bearer.Token != refreshedToken {
-		t.Errorf("Expected Bearer token to be refreshed, got %v, want %v", bearer.Token, refreshedToken)
+	if got, want := transport.bearer.RegistryToken, refreshedToken; got != want {
+		t.Errorf("Expected Bearer token to be refreshed, got %v, want %v", got, want)
 	}
 }
 
@@ -233,10 +231,8 @@ func TestBearerTransportOauthRefresh(t *testing.T) {
 		t.Errorf("Unexpected error during NewRegistry: %v", err)
 	}
 
-	bearer := &authn.Bearer{}
 	transport := &bearerTransport{
 		inner:    http.DefaultTransport,
-		bearer:   bearer,
 		basic:    authn.FromConfig(authn.AuthConfig{IdentityToken: initialToken}),
 		registry: registry,
 		realm:    server.URL,
@@ -253,8 +249,8 @@ func TestBearerTransportOauthRefresh(t *testing.T) {
 	if res.StatusCode != http.StatusOK {
 		t.Errorf("client.Get final StatusCode got %v, want: %v", res.StatusCode, http.StatusOK)
 	}
-	if transport.bearer.Token != accessToken {
-		t.Errorf("Expected Bearer token to be refreshed, got %v, want %v", bearer.Token, accessToken)
+	if want, got := transport.bearer.RegistryToken, accessToken; want != got {
+		t.Errorf("Expected Bearer token to be refreshed, got %v, want %v", got, want)
 	}
 	basicAuthConfig, err := transport.basic.Authorization()
 	if err != nil {
@@ -299,10 +295,8 @@ func TestBearerTransportOauth404Fallback(t *testing.T) {
 		t.Errorf("Unexpected error during NewRegistry: %v", err)
 	}
 
-	bearer := &authn.Bearer{}
 	transport := &bearerTransport{
-		inner:  http.DefaultTransport,
-		bearer: bearer,
+		inner: http.DefaultTransport,
 		basic: authn.FromConfig(authn.AuthConfig{
 			IdentityToken: identityToken,
 			Auth:          basicAuth,
@@ -322,8 +316,8 @@ func TestBearerTransportOauth404Fallback(t *testing.T) {
 	if res.StatusCode != http.StatusOK {
 		t.Errorf("client.Get final StatusCode got %v, want: %v", res.StatusCode, http.StatusOK)
 	}
-	if transport.bearer.Token != accessToken {
-		t.Errorf("Expected Bearer token to be refreshed, got %v, want %v", bearer.Token, accessToken)
+	if got, want := transport.bearer.RegistryToken, accessToken; got != want {
+		t.Errorf("Expected Bearer token to be refreshed, got %v, want %v", got, want)
 	}
 }
 
@@ -463,6 +457,11 @@ func TestCanonicalAddressResolution(t *testing.T) {
 		scheme:   "https",
 		address:  "[2001:db8::1]:",
 		want:     "[2001:db8::1]:443",
+	}, {
+		registry: registry,
+		scheme:   "https",
+		address:  "something:is::wrong]:",
+		want:     "something:is::wrong]:",
 	}}
 
 	for _, tt := range tests {
diff --git a/pkg/v1/remote/transport/transport_test.go b/pkg/v1/remote/transport/transport_test.go
index e9f74769..bdd0cbc3 100644
--- a/pkg/v1/remote/transport/transport_test.go
+++ b/pkg/v1/remote/transport/transport_test.go
@@ -15,6 +15,7 @@
 package transport
 
 import (
+	"errors"
 	"fmt"
 	"io/ioutil"
 	"net/http"
@@ -92,6 +93,30 @@ func TestTransportSelectionBasic(t *testing.T) {
 	}
 }
 
+type badAuth struct{}
+
+func (a *badAuth) Authorization() (*authn.AuthConfig, error) {
+	return nil, errors.New("sorry dave, I'm afraid I can't let you do that")
+}
+
+func TestTransportBadAuth(t *testing.T) {
+	server := httptest.NewServer(
+		http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+			w.Header().Set("WWW-Authenticate", `Bearer realm="http://foo.io"`)
+			http.Error(w, "Unauthorized", http.StatusUnauthorized)
+		}))
+	defer server.Close()
+	tprt := &http.Transport{
+		Proxy: func(req *http.Request) (*url.URL, error) {
+			return url.Parse(server.URL)
+		},
+	}
+
+	if _, err := New(testReference.Context().Registry, &badAuth{}, tprt, []string{testReference.Scope(PullScope)}); err == nil {
+		t.Errorf("New() expected err, got nil")
+	}
+}
+
 func TestTransportSelectionBearer(t *testing.T) {
 	request := 0
 	server := httptest.NewServer(
diff --git a/pkg/v1/remote/write_test.go b/pkg/v1/remote/write_test.go
index 3d356736..89161487 100644
--- a/pkg/v1/remote/write_test.go
+++ b/pkg/v1/remote/write_test.go
@@ -34,6 +34,7 @@ import (
 	"github.com/google/go-containerregistry/pkg/name"
 	"github.com/google/go-containerregistry/pkg/registry"
 	v1 "github.com/google/go-containerregistry/pkg/v1"
+	"github.com/google/go-containerregistry/pkg/v1/empty"
 	"github.com/google/go-containerregistry/pkg/v1/mutate"
 	"github.com/google/go-containerregistry/pkg/v1/partial"
 	"github.com/google/go-containerregistry/pkg/v1/random"
@@ -645,12 +646,16 @@ func TestUploadOne(t *testing.T) {
 	streamPath := "/path/to/upload"
 	commitPath := "/path/to/commit"
 
+	uploaded := false
 	w, closer, err := setupWriter(expectedRepo, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 		switch r.URL.Path {
 		case headPath:
 			if r.Method != http.MethodHead {
 				t.Errorf("Method; got %v, want %v", r.Method, http.MethodHead)
 			}
+			if uploaded {
+				return
+			}
 			http.Error(w, "NotFound", http.StatusNotFound)
 		case initiatePath:
 			if r.Method != http.MethodPost {
@@ -679,6 +684,7 @@ func TestUploadOne(t *testing.T) {
 			if r.Method != http.MethodPut {
 				t.Errorf("Method; got %v, want %v", r.Method, http.MethodPut)
 			}
+			uploaded = true
 			http.Error(w, "Created", http.StatusCreated)
 		default:
 			t.Fatalf("Unexpected path: %v", r.URL.Path)
@@ -693,6 +699,14 @@ func TestUploadOne(t *testing.T) {
 	if err != nil {
 		t.Fatalf("ConfigLayer: %v", err)
 	}
+	ml := &MountableLayer{
+		Layer:     l,
+		Reference: w.ref,
+	}
+	if err := w.uploadOne(ml); err != nil {
+		t.Errorf("uploadOne() = %v", err)
+	}
+	// Hit the existing blob path.
 	if err := w.uploadOne(l); err != nil {
 		t.Errorf("uploadOne() = %v", err)
 	}
@@ -1325,6 +1339,41 @@ func TestTagDescriptor(t *testing.T) {
 	}
 }
 
+func TestNestedIndex(t *testing.T) {
+	// Set up a fake registry.
+	s := httptest.NewServer(registry.New())
+	defer s.Close()
+	u, err := url.Parse(s.URL)
+	if err != nil {
+		t.Fatal(err)
+	}
+	src := fmt.Sprintf("%s/test/tag:src", u.Host)
+	srcRef, err := name.NewTag(src)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	child, err := random.Index(1024, 1, 1)
+	if err != nil {
+		t.Fatal(err)
+	}
+	parent := mutate.AppendManifests(empty.Index, mutate.IndexAddendum{
+		Add: child,
+	})
+
+	if err := WriteIndex(srcRef, parent); err != nil {
+		t.Fatal(err)
+	}
+	pulled, err := Index(srcRef)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	if err := validate.Index(pulled); err != nil {
+		t.Fatalf("validate.Index: %v", err)
+	}
+}
+
 func BenchmarkWrite(b *testing.B) {
 	// unfortunately the registry _and_ the img have caching behaviour, so we need a new registry
 	// and image every iteration of benchmarking.