Merge pull request #19355 from riyazdf/notary-revendor

notary revendor into docker
diff --git a/Dockerfile b/Dockerfile
index 18c0926..d498eb6 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -168,7 +168,7 @@
 	&& rm -rf "$GOPATH"
 
 # Install notary server
-ENV NOTARY_VERSION docker-v1.10-2
+ENV NOTARY_VERSION docker-v1.10-3
 RUN set -x \
 	&& export GOPATH="$(mktemp -d)" \
 	&& git clone https://github.com/docker/notary.git "$GOPATH/src/github.com/docker/notary" \
diff --git a/api/client/trust.go b/api/client/trust.go
index f347689..d06db5d 100644
--- a/api/client/trust.go
+++ b/api/client/trust.go
@@ -284,13 +284,15 @@
 	case signed.ErrInvalidKeyType:
 		return fmt.Errorf("Warning: potential malicious behavior - trust data mismatch for remote repository %s: %v", repoName, err)
 	case signed.ErrNoKeys:
-		return fmt.Errorf("Error: could not find signing keys for remote repository %s: %v", repoName, err)
+		return fmt.Errorf("Error: could not find signing keys for remote repository %s, or could not decrypt signing key: %v", repoName, err)
 	case signed.ErrLowVersion:
 		return fmt.Errorf("Warning: potential malicious behavior - trust data version is lower than expected for remote repository %s: %v", repoName, err)
-	case signed.ErrInsufficientSignatures:
+	case signed.ErrRoleThreshold:
 		return fmt.Errorf("Warning: potential malicious behavior - trust data has insufficient signatures for remote repository %s: %v", repoName, err)
 	case client.ErrRepositoryNotExist:
-		return fmt.Errorf("Error: remote trust data repository not initialized for %s: %v", repoName, err)
+		return fmt.Errorf("Error: remote trust data does not exist for %s: %v", repoName, err)
+	case signed.ErrInsufficientSignatures:
+		return fmt.Errorf("Error: could not produce valid signature for %s.  If Yubikey was used, was touch input provided?: %v", repoName, err)
 	}
 
 	return err
diff --git a/hack/vendor.sh b/hack/vendor.sh
index d952a34..091c977 100755
--- a/hack/vendor.sh
+++ b/hack/vendor.sh
@@ -50,7 +50,7 @@
 clone git github.com/vbatts/tar-split v0.9.11
 
 # get desired notary commit, might also need to be updated in Dockerfile
-clone git github.com/docker/notary docker-v1.10-2
+clone git github.com/docker/notary docker-v1.10-3
 
 clone git google.golang.org/grpc 174192fc93efcb188fc8f46ca447f0da606b6885 https://github.com/grpc/grpc-go.git
 clone git github.com/miekg/pkcs11 80f102b5cac759de406949c47f0928b99bd64cdf
diff --git a/integration-cli/docker_cli_create_test.go b/integration-cli/docker_cli_create_test.go
index 4bd90b0..096074c 100644
--- a/integration-cli/docker_cli_create_test.go
+++ b/integration-cli/docker_cli_create_test.go
@@ -312,7 +312,7 @@
 	s.trustedCmd(createCmd)
 	out, _, err := runCommandWithOutput(createCmd)
 	c.Assert(err, check.Not(check.IsNil))
-	c.Assert(string(out), checker.Contains, "does not have trust data for", check.Commentf("Missing expected output on trusted create:\n%s", out))
+	c.Assert(string(out), checker.Contains, "trust data unavailable.  Has a notary repository been initialized?", check.Commentf("Missing expected output on trusted create:\n%s", out))
 
 }
 
@@ -402,7 +402,7 @@
 	s.trustedCmd(createCmd)
 	out, _, err = runCommandWithOutput(createCmd)
 	c.Assert(err, check.Not(check.IsNil))
-	c.Assert(string(out), checker.Contains, "failed to validate data with current trusted certificates", check.Commentf("Missing expected output on trusted push:\n%s", out))
+	c.Assert(string(out), checker.Contains, "valid signatures did not meet threshold", check.Commentf("Missing expected output on trusted push:\n%s", out))
 
 }
 
diff --git a/integration-cli/docker_cli_pull_trusted_test.go b/integration-cli/docker_cli_pull_trusted_test.go
index 7bcfd5f..fbd50b5 100644
--- a/integration-cli/docker_cli_pull_trusted_test.go
+++ b/integration-cli/docker_cli_pull_trusted_test.go
@@ -59,7 +59,7 @@
 	out, _, err := runCommandWithOutput(pullCmd)
 
 	c.Assert(err, check.NotNil, check.Commentf(out))
-	c.Assert(string(out), checker.Contains, "Error: remote trust data repository not initialized", check.Commentf(out))
+	c.Assert(string(out), checker.Contains, "Error: remote trust data does not exist", check.Commentf(out))
 }
 
 func (s *DockerTrustSuite) TestPullWhenCertExpired(c *check.C) {
@@ -141,7 +141,7 @@
 	out, _, err = runCommandWithOutput(pullCmd)
 
 	c.Assert(err, check.NotNil, check.Commentf(out))
-	c.Assert(string(out), checker.Contains, "failed to validate data with current trusted certificates", check.Commentf(out))
+	c.Assert(string(out), checker.Contains, "valid signatures did not meet threshold", check.Commentf(out))
 }
 
 func (s *DockerTrustSuite) TestTrustedPullWithExpiredSnapshot(c *check.C) {
diff --git a/integration-cli/docker_cli_run_test.go b/integration-cli/docker_cli_run_test.go
index 0cf8a1c..f34c235 100644
--- a/integration-cli/docker_cli_run_test.go
+++ b/integration-cli/docker_cli_run_test.go
@@ -3303,7 +3303,7 @@
 		c.Fatalf("Expected to fail on this run due to different remote data: %s\n%s", err, out)
 	}
 
-	if !strings.Contains(string(out), "failed to validate data with current trusted certificates") {
+	if !strings.Contains(string(out), "valid signatures did not meet threshold") {
 		c.Fatalf("Missing expected output on trusted push:\n%s", out)
 	}
 }
diff --git a/vendor/src/github.com/docker/notary/CONTRIBUTING.md b/vendor/src/github.com/docker/notary/CONTRIBUTING.md
index dbe4fe2..0d4d16f 100644
--- a/vendor/src/github.com/docker/notary/CONTRIBUTING.md
+++ b/vendor/src/github.com/docker/notary/CONTRIBUTING.md
@@ -19,7 +19,6 @@
 Then please do not open an issue here yet - you should first try one of the following support forums:
 
  - irc: #docker-trust on freenode
- - mailing-list: <trust@dockerproject.org> or https://groups.google.com/a/dockerproject.org/forum/#!forum/trust
 
 ## Reporting an issue properly
 
diff --git a/vendor/src/github.com/docker/notary/MAINTAINERS b/vendor/src/github.com/docker/notary/MAINTAINERS
index 73741d6..999e280 100644
--- a/vendor/src/github.com/docker/notary/MAINTAINERS
+++ b/vendor/src/github.com/docker/notary/MAINTAINERS
@@ -16,6 +16,7 @@
 			"dmcgowan",
 			"endophage",
 			"nathanmccauley",
+			"riyazdf",
 		]
 
 [people]
@@ -50,3 +51,8 @@
 	Name = "Nathan McCauley"
 	Email = "nathan.mccauley@docker.com"
 	GitHub = "nathanmccauley"
+
+	[people.riyazdf]
+	Name = "Riyaz Faizullabhoy"
+	Email = "riyaz@docker.com"
+	GitHub = "riyazdf"
diff --git a/vendor/src/github.com/docker/notary/Makefile b/vendor/src/github.com/docker/notary/Makefile
index 641ba69..c632423 100644
--- a/vendor/src/github.com/docker/notary/Makefile
+++ b/vendor/src/github.com/docker/notary/Makefile
@@ -92,14 +92,21 @@
 	@echo "+ $@"
 	@go build -tags "${NOTARY_BUILDTAGS}" -v ${GO_LDFLAGS} ./...
 
+# When running `go test ./...`, it runs all the suites in parallel, which causes
+# problems when running with a yubikey
 test: TESTOPTS =
 test: go_version
+	@echo Note: when testing with a yubikey plugged in, make sure to include 'TESTOPTS="-p 1"'
 	@echo "+ $@ $(TESTOPTS)"
+	@echo
 	go test -tags "${NOTARY_BUILDTAGS}" $(TESTOPTS) ./...
 
+test-full: TESTOPTS =
 test-full: vet lint
+	@echo Note: when testing with a yubikey plugged in, make sure to include 'TESTOPTS="-p 1"'
 	@echo "+ $@"
-	go test -tags "${NOTARY_BUILDTAGS}" -v ./...
+	@echo
+	go test -tags "${NOTARY_BUILDTAGS}" $(TESTOPTS) -v ./...
 
 protos:
 	@protoc --go_out=plugins=grpc:. proto/*.proto
@@ -118,14 +125,18 @@
 	@mkdir -p "$(COVERDIR)"
 	$(foreach PKG,$(PKGS),$(call gocover,$(PKG)))
 
+# Generates the cover binaries and runs them all in serial, so this can be used
+# run all tests with a yubikey without any problems
 cover: GO_EXC := go
        OPTS = -tags "${NOTARY_BUILDTAGS}" -coverpkg "$(shell ./coverpkg.sh $(1) $(NOTARY_PKG))"
 cover: gen-cover covmerge
 	@go tool cover -html="$(COVERPROFILE)"
 
-# Codecov knows how to merge multiple coverage files
+# Generates the cover binaries and runs them all in serial, so this can be used
+# run all tests with a yubikey without any problems
 ci: OPTS = -tags "${NOTARY_BUILDTAGS}" -race -coverpkg "$(shell ./coverpkg.sh $(1) $(NOTARY_PKG))"
     GO_EXC := godep go
+# Codecov knows how to merge multiple coverage files, so covmerge is not needed
 ci: gen-cover
 
 covmerge:
@@ -151,10 +162,10 @@
 	@docker build --rm --force-rm -t notary .
 
 server-dockerfile:
-	@docker build --rm --force-rm -f Dockerfile.server -t notary-server .
+	@docker build --rm --force-rm -f server.Dockerfile -t notary-server .
 
 signer-dockerfile:
-	@docker build --rm --force-rm -f Dockerfile.signer -t notary-signer .
+	@docker build --rm --force-rm -f signer.Dockerfile -t notary-signer .
 
 docker-images: notary-dockerfile server-dockerfile signer-dockerfile
 
diff --git a/vendor/src/github.com/docker/notary/client/client.go b/vendor/src/github.com/docker/notary/client/client.go
index b065936..a3c7b7f 100644
--- a/vendor/src/github.com/docker/notary/client/client.go
+++ b/vendor/src/github.com/docker/notary/client/client.go
@@ -419,7 +419,7 @@
 // subtree and also the "targets/x" subtree, as we will defer parsing it until
 // we explicitly reach it in our iteration of the provided list of roles.
 func (r *NotaryRepository) ListTargets(roles ...string) ([]*TargetWithRole, error) {
-	_, err := r.Update()
+	_, err := r.Update(false)
 	if err != nil {
 		return nil, err
 	}
@@ -479,7 +479,7 @@
 // will be returned
 // See the IMPORTANT section on ListTargets above. Those roles also apply here.
 func (r *NotaryRepository) GetTargetByName(name string, roles ...string) (*TargetWithRole, error) {
-	c, err := r.Update()
+	c, err := r.Update(false)
 	if err != nil {
 		return nil, err
 	}
@@ -514,7 +514,7 @@
 func (r *NotaryRepository) Publish() error {
 	var initialPublish bool
 	// update first before publishing
-	_, err := r.Update()
+	_, err := r.Update(true)
 	if err != nil {
 		// If the remote is not aware of the repo, then this is being published
 		// for the first time.  Try to load from disk instead for publishing.
@@ -555,13 +555,21 @@
 	// we send anything to remote
 	updatedFiles := make(map[string][]byte)
 
-	// check if our root file is nearing expiry. Resign if it is.
-	if nearExpiry(r.tufRepo.Root) || r.tufRepo.Root.Dirty || initialPublish {
+	// check if our root file is nearing expiry or dirty. Resign if it is.  If
+	// root is not dirty but we are publishing for the first time, then just
+	// publish the existing root we have.
+	if nearExpiry(r.tufRepo.Root) || r.tufRepo.Root.Dirty {
 		rootJSON, err := serializeCanonicalRole(r.tufRepo, data.CanonicalRootRole)
 		if err != nil {
 			return err
 		}
 		updatedFiles[data.CanonicalRootRole] = rootJSON
+	} else if initialPublish {
+		rootJSON, err := r.tufRepo.Root.MarshalJSON()
+		if err != nil {
+			return err
+		}
+		updatedFiles[data.CanonicalRootRole] = rootJSON
 	}
 
 	// iterate through all the targets files - if they are dirty, sign and update
@@ -714,75 +722,94 @@
 	return r.fileStore.SetMeta(data.CanonicalSnapshotRole, snapshotJSON)
 }
 
+// returns a properly constructed ErrRepositoryNotExist error based on this
+// repo's information
+func (r *NotaryRepository) errRepositoryNotExist() error {
+	host := r.baseURL
+	parsed, err := url.Parse(r.baseURL)
+	if err == nil {
+		host = parsed.Host // try to exclude the scheme and any paths
+	}
+	return ErrRepositoryNotExist{remote: host, gun: r.gun}
+}
+
 // Update bootstraps a trust anchor (root.json) before updating all the
 // metadata from the repo.
-func (r *NotaryRepository) Update() (*tufclient.Client, error) {
-	c, err := r.bootstrapClient()
+func (r *NotaryRepository) Update(forWrite bool) (*tufclient.Client, error) {
+	c, err := r.bootstrapClient(forWrite)
 	if err != nil {
 		if _, ok := err.(store.ErrMetaNotFound); ok {
-			host := r.baseURL
-			parsed, err := url.Parse(r.baseURL)
-			if err == nil {
-				host = parsed.Host // try to exclude the scheme and any paths
-			}
-			return nil, ErrRepositoryNotExist{remote: host, gun: r.gun}
+			return nil, r.errRepositoryNotExist()
 		}
 		return nil, err
 	}
 	err = c.Update()
 	if err != nil {
+		if notFound, ok := err.(store.ErrMetaNotFound); ok && notFound.Resource == data.CanonicalRootRole {
+			return nil, r.errRepositoryNotExist()
+		}
 		return nil, err
 	}
 	return c, nil
 }
 
-func (r *NotaryRepository) bootstrapClient() (*tufclient.Client, error) {
-	var rootJSON []byte
-	remote, err := getRemoteStore(r.baseURL, r.gun, r.roundTrip)
-	if err == nil {
+// bootstrapClient attempts to bootstrap a root.json to be used as the trust
+// anchor for a repository. The checkInitialized argument indicates whether
+// we should always attempt to contact the server to determine if the repository
+// is initialized or not. If set to true, we will always attempt to download
+// and return an error if the remote repository errors.
+func (r *NotaryRepository) bootstrapClient(checkInitialized bool) (*tufclient.Client, error) {
+	var (
+		rootJSON   []byte
+		err        error
+		signedRoot *data.SignedRoot
+	)
+	// try to read root from cache first. We will trust this root
+	// until we detect a problem during update which will cause
+	// us to download a new root and perform a rotation.
+	rootJSON, cachedRootErr := r.fileStore.GetMeta("root", maxSize)
+
+	if cachedRootErr == nil {
+		signedRoot, cachedRootErr = r.validateRoot(rootJSON)
+	}
+
+	remote, remoteErr := getRemoteStore(r.baseURL, r.gun, r.roundTrip)
+	if remoteErr != nil {
+		logrus.Error(remoteErr)
+	} else if cachedRootErr != nil || checkInitialized {
+		// remoteErr was nil and we had a cachedRootErr (or are specifically
+		// checking for initialization of the repo).
+
 		// if remote store successfully set up, try and get root from remote
-		rootJSON, err = remote.GetMeta("root", maxSize)
-	}
-
-	// if remote store couldn't be setup, or we failed to get a root from it
-	// load the root from cache (offline operation)
-	if err != nil {
-		if err, ok := err.(store.ErrMetaNotFound); ok {
-			// if the error was MetaNotFound then we successfully contacted
-			// the store and it doesn't know about the repo.
+		tmpJSON, err := remote.GetMeta("root", maxSize)
+		if err != nil {
+			// we didn't have a root in cache and were unable to load one from
+			// the server. Nothing we can do but error.
 			return nil, err
 		}
-		result, cacheErr := r.fileStore.GetMeta("root", maxSize)
-		if cacheErr != nil {
-			// if cache didn't return a root, we cannot proceed - just return
-			// the original error.
-			return nil, err
-		}
-		rootJSON = result
-		logrus.Debugf(
-			"Using local cache instead of remote due to failure: %s", err.Error())
-	}
-	// can't just unmarshal into SignedRoot because validate root
-	// needs the root.Signed field to still be []byte for signature
-	// validation
-	root := &data.Signed{}
-	err = json.Unmarshal(rootJSON, root)
-	if err != nil {
-		return nil, err
-	}
+		if cachedRootErr != nil {
+			// we always want to use the downloaded root if there was a cache
+			// error.
+			signedRoot, err = r.validateRoot(tmpJSON)
+			if err != nil {
+				return nil, err
+			}
 
-	err = r.CertManager.ValidateRoot(root, r.gun)
-	if err != nil {
-		return nil, err
+			err = r.fileStore.SetMeta("root", tmpJSON)
+			if err != nil {
+				// if we can't write cache we should still continue, just log error
+				logrus.Errorf("could not save root to cache: %s", err.Error())
+			}
+		}
 	}
 
 	kdb := keys.NewDB()
 	r.tufRepo = tuf.NewRepo(kdb, r.CryptoService)
 
-	signedRoot, err := data.RootFromSigned(root)
-	if err != nil {
-		return nil, err
+	if signedRoot == nil {
+		return nil, ErrRepoNotInitialized{}
 	}
+
 	err = r.tufRepo.SetRoot(signedRoot)
 	if err != nil {
 		return nil, err
@@ -796,6 +823,28 @@
 	), nil
 }
 
+// validateRoot MUST only be used during bootstrapping. It will only validate
+// signatures of the root based on known keys, not expiry or other metadata.
+// This is so that an out of date root can be loaded to be used in a rotation
+// should the TUF update process detect a problem.
+func (r *NotaryRepository) validateRoot(rootJSON []byte) (*data.SignedRoot, error) {
+	// can't just unmarshal into SignedRoot because validate root
+	// needs the root.Signed field to still be []byte for signature
+	// validation
+	root := &data.Signed{}
+	err := json.Unmarshal(rootJSON, root)
+	if err != nil {
+		return nil, err
+	}
+
+	err = r.CertManager.ValidateRoot(root, r.gun)
+	if err != nil {
+		return nil, err
+	}
+
+	return data.RootFromSigned(root)
+}
+
 // RotateKey removes all existing keys associated with the role, and either
 // creates and adds one new key or delegates managing the key to the server.
 // These changes are staged in a changelist until publish is called.
diff --git a/vendor/src/github.com/docker/notary/client/helpers.go b/vendor/src/github.com/docker/notary/client/helpers.go
index 23b9249..304ac3d 100644
--- a/vendor/src/github.com/docker/notary/client/helpers.go
+++ b/vendor/src/github.com/docker/notary/client/helpers.go
@@ -17,7 +17,7 @@
 
 // Use this to initialize remote HTTPStores from the config settings
 func getRemoteStore(baseURL, gun string, rt http.RoundTripper) (store.RemoteStore, error) {
-	return store.NewHTTPStore(
+	s, err := store.NewHTTPStore(
 		baseURL+"/v2/"+gun+"/_trust/tuf/",
 		"",
 		"json",
@@ -25,6 +25,10 @@
 		"key",
 		rt,
 	)
+	if err != nil {
+		return store.OfflineStore{}, err
+	}
+	return s, err
 }
 
 func applyChangelist(repo *tuf.Repo, cl changelist.Changelist) error {
diff --git a/vendor/src/github.com/docker/notary/client/repo_pkcs11.go b/vendor/src/github.com/docker/notary/client/repo_pkcs11.go
index b93f9bf..dd697ff 100644
--- a/vendor/src/github.com/docker/notary/client/repo_pkcs11.go
+++ b/vendor/src/github.com/docker/notary/client/repo_pkcs11.go
@@ -26,7 +26,7 @@
 	keyStores := []trustmanager.KeyStore{fileKeyStore}
 	yubiKeyStore, _ := yubikey.NewYubiKeyStore(fileKeyStore, retriever)
 	if yubiKeyStore != nil {
-		keyStores = append(keyStores, yubiKeyStore)
+		keyStores = []trustmanager.KeyStore{yubiKeyStore, fileKeyStore}
 	}
 
 	return repositoryFromKeystores(baseDir, gun, baseURL, rt, keyStores)
diff --git a/vendor/src/github.com/docker/notary/cryptoservice/crypto_service.go b/vendor/src/github.com/docker/notary/cryptoservice/crypto_service.go
index d473564..f5bfa07 100644
--- a/vendor/src/github.com/docker/notary/cryptoservice/crypto_service.go
+++ b/vendor/src/github.com/docker/notary/cryptoservice/crypto_service.go
@@ -73,9 +73,9 @@
 
 }
 
-// GetPrivateKey returns a private key by ID. It tries to get the key first
-// without a GUN (in which case it's a root key).  If that fails, try to get
-// the key with the GUN (non-root key).
+// GetPrivateKey returns a private key and role if present by ID.
+// It tries to get the key first without a GUN (in which case it's a root key).
+// If that fails, try to get the key with the GUN (non-root key).
 // If that fails, then we don't have the key.
 func (cs *CryptoService) GetPrivateKey(keyID string) (k data.PrivateKey, role string, err error) {
 	keyPaths := []string{keyID, filepath.Join(cs.gun, keyID)}
diff --git a/vendor/src/github.com/docker/notary/docker-compose.yml b/vendor/src/github.com/docker/notary/docker-compose.yml
index 5bd578c..17b5798 100644
--- a/vendor/src/github.com/docker/notary/docker-compose.yml
+++ b/vendor/src/github.com/docker/notary/docker-compose.yml
@@ -1,6 +1,6 @@
 notaryserver:
   build: .
-  dockerfile: Dockerfile.server
+  dockerfile: server.Dockerfile
   links:
    - notarymysql
    - notarysigner
@@ -15,7 +15,7 @@
    - /dev/bus/usb/003/010:/dev/bus/usb/002/010
    - /var/run/pcscd/pcscd.comm:/var/run/pcscd/pcscd.comm
   build: .
-  dockerfile: Dockerfile.signer
+  dockerfile: signer.Dockerfile
   links:
    - notarymysql
   command: -config=fixtures/signer-config.json
diff --git a/vendor/src/github.com/docker/notary/Dockerfile.server b/vendor/src/github.com/docker/notary/server.Dockerfile
similarity index 100%
rename from vendor/src/github.com/docker/notary/Dockerfile.server
rename to vendor/src/github.com/docker/notary/server.Dockerfile
diff --git a/vendor/src/github.com/docker/notary/Dockerfile.signer b/vendor/src/github.com/docker/notary/signer.Dockerfile
similarity index 100%
rename from vendor/src/github.com/docker/notary/Dockerfile.signer
rename to vendor/src/github.com/docker/notary/signer.Dockerfile
diff --git a/vendor/src/github.com/docker/notary/tuf/client/client.go b/vendor/src/github.com/docker/notary/tuf/client/client.go
index 9d28a8c..263ee42 100644
--- a/vendor/src/github.com/docker/notary/tuf/client/client.go
+++ b/vendor/src/github.com/docker/notary/tuf/client/client.go
@@ -129,6 +129,7 @@
 
 // downloadRoot is responsible for downloading the root.json
 func (c *Client) downloadRoot() error {
+	logrus.Debug("Downloading Root...")
 	role := data.CanonicalRootRole
 	size := maxSize
 	var expectedSha256 []byte
@@ -240,7 +241,7 @@
 // Timestamps are special in that we ALWAYS attempt to download and only
 // use cache if the download fails (and the cache is still valid).
 func (c *Client) downloadTimestamp() error {
-	logrus.Debug("downloadTimestamp")
+	logrus.Debug("Downloading Timestamp...")
 	role := data.CanonicalTimestampRole
 
 	// We may not have a cached timestamp if this is the first time
@@ -299,7 +300,7 @@
 
 // downloadSnapshot is responsible for downloading the snapshot.json
 func (c *Client) downloadSnapshot() error {
-	logrus.Debug("downloadSnapshot")
+	logrus.Debug("Downloading Snapshot...")
 	role := data.CanonicalSnapshotRole
 	if c.local.Timestamp == nil {
 		return ErrMissingMeta{role: "snapshot"}
@@ -372,6 +373,7 @@
 // It uses a pre-order tree traversal as it's necessary to download parents first
 // to obtain the keys to validate children.
 func (c *Client) downloadTargets(role string) error {
+	logrus.Debug("Downloading Targets...")
 	stack := utils.NewStack()
 	stack.Push(role)
 	for !stack.Empty() {
diff --git a/vendor/src/github.com/docker/notary/tuf/data/root.go b/vendor/src/github.com/docker/notary/tuf/data/root.go
index 9ef8cd6..e555cbd 100644
--- a/vendor/src/github.com/docker/notary/tuf/data/root.go
+++ b/vendor/src/github.com/docker/notary/tuf/data/root.go
@@ -43,10 +43,11 @@
 
 // ToSigned partially serializes a SignedRoot for further signing
 func (r SignedRoot) ToSigned() (*Signed, error) {
-	s, err := json.MarshalCanonical(r.Signed)
+	s, err := defaultSerializer.MarshalCanonical(r.Signed)
 	if err != nil {
 		return nil, err
 	}
+	// cast into a json.RawMessage
 	signed := json.RawMessage{}
 	err = signed.UnmarshalJSON(s)
 	if err != nil {
@@ -60,6 +61,15 @@
 	}, nil
 }
 
+// MarshalJSON returns the serialized form of SignedRoot as bytes
+func (r SignedRoot) MarshalJSON() ([]byte, error) {
+	signed, err := r.ToSigned()
+	if err != nil {
+		return nil, err
+	}
+	return defaultSerializer.Marshal(signed)
+}
+
 // RootFromSigned fully unpacks a Signed object into a SignedRoot
 func RootFromSigned(s *Signed) (*SignedRoot, error) {
 	r := Root{}
diff --git a/vendor/src/github.com/docker/notary/tuf/data/serializer.go b/vendor/src/github.com/docker/notary/tuf/data/serializer.go
new file mode 100644
index 0000000..91fa1bc
--- /dev/null
+++ b/vendor/src/github.com/docker/notary/tuf/data/serializer.go
@@ -0,0 +1,36 @@
+package data
+
+import "github.com/jfrazelle/go/canonical/json"
+
+// Serializer is an interface that can marshal and unmarshal TUF data.  This
+// is expected to be a canonical JSON marshaller
+type serializer interface {
+	MarshalCanonical(from interface{}) ([]byte, error)
+	Marshal(from interface{}) ([]byte, error)
+	Unmarshal(from []byte, to interface{}) error
+}
+
+// CanonicalJSON marshals to and from canonical JSON
+type canonicalJSON struct{}
+
+// MarshalCanonical returns the canonical JSON form of a thing
+func (c canonicalJSON) MarshalCanonical(from interface{}) ([]byte, error) {
+	return json.MarshalCanonical(from)
+}
+
+// Marshal returns the regular non-canonical JSON form of a thing
+func (c canonicalJSON) Marshal(from interface{}) ([]byte, error) {
+	return json.Marshal(from)
+}
+
+// Unmarshal unmarshals some JSON bytes
+func (c canonicalJSON) Unmarshal(from []byte, to interface{}) error {
+	return json.Unmarshal(from, to)
+}
+
+// defaultSerializer is a canonical JSON serializer
+var defaultSerializer serializer = canonicalJSON{}
+
+func setDefaultSerializer(s serializer) {
+	defaultSerializer = s
+}
diff --git a/vendor/src/github.com/docker/notary/tuf/signed/ed25519.go b/vendor/src/github.com/docker/notary/tuf/signed/ed25519.go
index 3f7ad1e..e09b550 100644
--- a/vendor/src/github.com/docker/notary/tuf/signed/ed25519.go
+++ b/vendor/src/github.com/docker/notary/tuf/signed/ed25519.go
@@ -95,7 +95,7 @@
 	return data.PublicKeyFromPrivate(e.keys[keyID].privKey)
 }
 
-// GetPrivateKey returns a single private key based on the ID
+// GetPrivateKey returns a single private key and role if present, based on the ID
 func (e *Ed25519) GetPrivateKey(keyID string) (data.PrivateKey, string, error) {
 	if k, ok := e.keys[keyID]; ok {
 		return k.privKey, k.role, nil
diff --git a/vendor/src/github.com/docker/notary/tuf/store/httpstore.go b/vendor/src/github.com/docker/notary/tuf/store/httpstore.go
index 66e4bcc..ef69a61 100644
--- a/vendor/src/github.com/docker/notary/tuf/store/httpstore.go
+++ b/vendor/src/github.com/docker/notary/tuf/store/httpstore.go
@@ -118,12 +118,12 @@
 	return err
 }
 
-func translateStatusToError(resp *http.Response) error {
+func translateStatusToError(resp *http.Response, resource string) error {
 	switch resp.StatusCode {
 	case http.StatusOK:
 		return nil
 	case http.StatusNotFound:
-		return ErrMetaNotFound{}
+		return ErrMetaNotFound{Resource: resource}
 	case http.StatusBadRequest:
 		return tryUnmarshalError(resp, ErrInvalidOperation{})
 	default:
@@ -148,7 +148,7 @@
 		return nil, err
 	}
 	defer resp.Body.Close()
-	if err := translateStatusToError(resp); err != nil {
+	if err := translateStatusToError(resp, name); err != nil {
 		logrus.Debugf("received HTTP status %d when requesting %s.", resp.StatusCode, name)
 		return nil, err
 	}
@@ -179,7 +179,7 @@
 		return err
 	}
 	defer resp.Body.Close()
-	return translateStatusToError(resp)
+	return translateStatusToError(resp, "POST "+name)
 }
 
 // NewMultiPartMetaRequest builds a request with the provided metadata updates
@@ -223,7 +223,8 @@
 		return err
 	}
 	defer resp.Body.Close()
-	return translateStatusToError(resp)
+	// if this 404's something is pretty wrong
+	return translateStatusToError(resp, "POST metadata endpoint")
 }
 
 func (s HTTPStore) buildMetaURL(name string) (*url.URL, error) {
@@ -271,7 +272,7 @@
 		return nil, err
 	}
 	defer resp.Body.Close()
-	if err := translateStatusToError(resp); err != nil {
+	if err := translateStatusToError(resp, path); err != nil {
 		return nil, err
 	}
 	return resp.Body, nil
@@ -292,7 +293,7 @@
 		return nil, err
 	}
 	defer resp.Body.Close()
-	if err := translateStatusToError(resp); err != nil {
+	if err := translateStatusToError(resp, role+" key"); err != nil {
 		return nil, err
 	}
 	body, err := ioutil.ReadAll(resp.Body)
diff --git a/vendor/src/github.com/docker/notary/tuf/store/offlinestore.go b/vendor/src/github.com/docker/notary/tuf/store/offlinestore.go
new file mode 100644
index 0000000..d32e113
--- /dev/null
+++ b/vendor/src/github.com/docker/notary/tuf/store/offlinestore.go
@@ -0,0 +1,43 @@
+package store
+
+import (
+	"io"
+)
+
+// ErrOffline is used to indicate we are operating offline
+type ErrOffline struct{}
+
+func (e ErrOffline) Error() string {
+	return "client is offline"
+}
+
+var err = ErrOffline{}
+
+// OfflineStore is to be used as a placeholder for a nil store. It simply
+// return ErrOffline for every operation
+type OfflineStore struct{}
+
+// GetMeta return ErrOffline
+func (es OfflineStore) GetMeta(name string, size int64) ([]byte, error) {
+	return nil, err
+}
+
+// SetMeta return ErrOffline
+func (es OfflineStore) SetMeta(name string, blob []byte) error {
+	return err
+}
+
+// SetMultiMeta return ErrOffline
+func (es OfflineStore) SetMultiMeta(map[string][]byte) error {
+	return err
+}
+
+// GetKey return ErrOffline
+func (es OfflineStore) GetKey(role string) ([]byte, error) {
+	return nil, err
+}
+
+// GetTarget return ErrOffline
+func (es OfflineStore) GetTarget(path string) (io.ReadCloser, error) {
+	return nil, err
+}