diff --git a/api/client/trust.go b/api/client/trust.go index 4d984cfa6f..ab29269939 100644 --- a/api/client/trust.go +++ b/api/client/trust.go @@ -144,15 +144,21 @@ func (cli *DockerCli) getNotaryRepository(repoInfo *registry.RepositoryInfo, aut if err != nil { return nil, err } - resp, err := pingClient.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() challengeManager := auth.NewSimpleChallengeManager() - if err := challengeManager.AddResponse(resp); err != nil { - return nil, err + + resp, err := pingClient.Do(req) + if err != nil { + // Ignore error on ping to operate in offline mode + logrus.Debugf("Error pinging notary server %q: %s", endpointStr, err) + } else { + defer resp.Body.Close() + + // Add response to the challenge manager to parse out + // authentication header and register authentication method + if err := challengeManager.AddResponse(resp); err != nil { + return nil, err + } } creds := simpleCredentialStore{auth: authConfig} @@ -248,6 +254,8 @@ func notaryError(err error) error { return fmt.Errorf("remote repository out-of-date: %v", err) case trustmanager.ErrKeyNotFound: return fmt.Errorf("signing keys not found: %v", err) + case *net.OpError: + return fmt.Errorf("error contacting notary server: %v", err) } return err diff --git a/integration-cli/docker_cli_pull_trusted_test.go b/integration-cli/docker_cli_pull_trusted_test.go index d0514573c7..4e796f0e4c 100644 --- a/integration-cli/docker_cli_pull_trusted_test.go +++ b/integration-cli/docker_cli_pull_trusted_test.go @@ -223,3 +223,44 @@ func (s *DockerTrustSuite) TestTrustedPullWithExpiredSnapshot(c *check.C) { } }) } + +func (s *DockerTrustSuite) TestTrustedOfflinePull(c *check.C) { + repoName := s.setupTrustedImage(c, "trusted-offline-pull") + + pullCmd := exec.Command(dockerBinary, "pull", repoName) + s.trustedCmdWithServer(pullCmd, "https://invalidnotaryserver") + out, _, err := runCommandWithOutput(pullCmd) + if err == nil { + c.Fatalf("Expected error pulling with invalid notary server:\n%s", out) + } + + if !strings.Contains(string(out), "error contacting notary server") { + c.Fatalf("Missing expected output on trusted pull:\n%s", out) + } + + // Do valid trusted pull to warm cache + pullCmd = exec.Command(dockerBinary, "pull", repoName) + s.trustedCmd(pullCmd) + out, _, err = runCommandWithOutput(pullCmd) + if err != nil { + c.Fatalf("Error running trusted pull: %s\n%s", err, out) + } + + if !strings.Contains(string(out), "Tagging") { + c.Fatalf("Missing expected output on trusted push:\n%s", out) + } + + dockerCmd(c, "rmi", repoName) + + // Try pull again with invalid notary server, should use cache + pullCmd = exec.Command(dockerBinary, "pull", repoName) + s.trustedCmdWithServer(pullCmd, "https://invalidnotaryserver") + out, _, err = runCommandWithOutput(pullCmd) + if err != nil { + c.Fatalf("Error running trusted pull: %s\n%s", err, out) + } + + if !strings.Contains(string(out), "Tagging") { + c.Fatalf("Missing expected output on trusted push:\n%s", out) + } +} diff --git a/integration-cli/docker_cli_push_test.go b/integration-cli/docker_cli_push_test.go index b5c4b527cb..badc3fd516 100644 --- a/integration-cli/docker_cli_push_test.go +++ b/integration-cli/docker_cli_push_test.go @@ -154,7 +154,7 @@ func (s *DockerTrustSuite) TestTrustedPushWithFaillingServer(c *check.C) { c.Fatalf("Missing error while running trusted push w/ no server") } - if !strings.Contains(string(out), "Error establishing connection to notary repository") { + if !strings.Contains(string(out), "error contacting notary server") { c.Fatalf("Missing expected output on trusted push:\n%s", out) } }