github.com/nullne/docker@v1.13.0-rc1/integration-cli/docker_cli_push_test.go (about)

     1  package main
     2  
     3  import (
     4  	"archive/tar"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"strings"
    13  	"sync"
    14  	"time"
    15  
    16  	"github.com/docker/distribution/reference"
    17  	"github.com/docker/docker/cliconfig"
    18  	"github.com/docker/docker/pkg/integration/checker"
    19  	"github.com/go-check/check"
    20  )
    21  
    22  // Pushing an image to a private registry.
    23  func testPushBusyboxImage(c *check.C) {
    24  	repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
    25  	// tag the image to upload it to the private registry
    26  	dockerCmd(c, "tag", "busybox", repoName)
    27  	// push the image to the registry
    28  	dockerCmd(c, "push", repoName)
    29  }
    30  
    31  func (s *DockerRegistrySuite) TestPushBusyboxImage(c *check.C) {
    32  	testPushBusyboxImage(c)
    33  }
    34  
    35  func (s *DockerSchema1RegistrySuite) TestPushBusyboxImage(c *check.C) {
    36  	testPushBusyboxImage(c)
    37  }
    38  
    39  // pushing an image without a prefix should throw an error
    40  func (s *DockerSuite) TestPushUnprefixedRepo(c *check.C) {
    41  	out, _, err := dockerCmdWithError("push", "busybox")
    42  	c.Assert(err, check.NotNil, check.Commentf("pushing an unprefixed repo didn't result in a non-zero exit status: %s", out))
    43  }
    44  
    45  func testPushUntagged(c *check.C) {
    46  	repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
    47  	expected := "An image does not exist locally with the tag"
    48  
    49  	out, _, err := dockerCmdWithError("push", repoName)
    50  	c.Assert(err, check.NotNil, check.Commentf("pushing the image to the private registry should have failed: output %q", out))
    51  	c.Assert(out, checker.Contains, expected, check.Commentf("pushing the image failed"))
    52  }
    53  
    54  func (s *DockerRegistrySuite) TestPushUntagged(c *check.C) {
    55  	testPushUntagged(c)
    56  }
    57  
    58  func (s *DockerSchema1RegistrySuite) TestPushUntagged(c *check.C) {
    59  	testPushUntagged(c)
    60  }
    61  
    62  func testPushBadTag(c *check.C) {
    63  	repoName := fmt.Sprintf("%v/dockercli/busybox:latest", privateRegistryURL)
    64  	expected := "does not exist"
    65  
    66  	out, _, err := dockerCmdWithError("push", repoName)
    67  	c.Assert(err, check.NotNil, check.Commentf("pushing the image to the private registry should have failed: output %q", out))
    68  	c.Assert(out, checker.Contains, expected, check.Commentf("pushing the image failed"))
    69  }
    70  
    71  func (s *DockerRegistrySuite) TestPushBadTag(c *check.C) {
    72  	testPushBadTag(c)
    73  }
    74  
    75  func (s *DockerSchema1RegistrySuite) TestPushBadTag(c *check.C) {
    76  	testPushBadTag(c)
    77  }
    78  
    79  func testPushMultipleTags(c *check.C) {
    80  	repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
    81  	repoTag1 := fmt.Sprintf("%v/dockercli/busybox:t1", privateRegistryURL)
    82  	repoTag2 := fmt.Sprintf("%v/dockercli/busybox:t2", privateRegistryURL)
    83  	// tag the image and upload it to the private registry
    84  	dockerCmd(c, "tag", "busybox", repoTag1)
    85  
    86  	dockerCmd(c, "tag", "busybox", repoTag2)
    87  
    88  	dockerCmd(c, "push", repoName)
    89  
    90  	// Ensure layer list is equivalent for repoTag1 and repoTag2
    91  	out1, _ := dockerCmd(c, "pull", repoTag1)
    92  
    93  	imageAlreadyExists := ": Image already exists"
    94  	var out1Lines []string
    95  	for _, outputLine := range strings.Split(out1, "\n") {
    96  		if strings.Contains(outputLine, imageAlreadyExists) {
    97  			out1Lines = append(out1Lines, outputLine)
    98  		}
    99  	}
   100  
   101  	out2, _ := dockerCmd(c, "pull", repoTag2)
   102  
   103  	var out2Lines []string
   104  	for _, outputLine := range strings.Split(out2, "\n") {
   105  		if strings.Contains(outputLine, imageAlreadyExists) {
   106  			out1Lines = append(out1Lines, outputLine)
   107  		}
   108  	}
   109  	c.Assert(out2Lines, checker.HasLen, len(out1Lines))
   110  
   111  	for i := range out1Lines {
   112  		c.Assert(out1Lines[i], checker.Equals, out2Lines[i])
   113  	}
   114  }
   115  
   116  func (s *DockerRegistrySuite) TestPushMultipleTags(c *check.C) {
   117  	testPushMultipleTags(c)
   118  }
   119  
   120  func (s *DockerSchema1RegistrySuite) TestPushMultipleTags(c *check.C) {
   121  	testPushMultipleTags(c)
   122  }
   123  
   124  func testPushEmptyLayer(c *check.C) {
   125  	repoName := fmt.Sprintf("%v/dockercli/emptylayer", privateRegistryURL)
   126  	emptyTarball, err := ioutil.TempFile("", "empty_tarball")
   127  	c.Assert(err, check.IsNil, check.Commentf("Unable to create test file"))
   128  
   129  	tw := tar.NewWriter(emptyTarball)
   130  	err = tw.Close()
   131  	c.Assert(err, check.IsNil, check.Commentf("Error creating empty tarball"))
   132  
   133  	freader, err := os.Open(emptyTarball.Name())
   134  	c.Assert(err, check.IsNil, check.Commentf("Could not open test tarball"))
   135  	defer freader.Close()
   136  
   137  	importCmd := exec.Command(dockerBinary, "import", "-", repoName)
   138  	importCmd.Stdin = freader
   139  	out, _, err := runCommandWithOutput(importCmd)
   140  	c.Assert(err, check.IsNil, check.Commentf("import failed: %q", out))
   141  
   142  	// Now verify we can push it
   143  	out, _, err = dockerCmdWithError("push", repoName)
   144  	c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out))
   145  }
   146  
   147  func (s *DockerRegistrySuite) TestPushEmptyLayer(c *check.C) {
   148  	testPushEmptyLayer(c)
   149  }
   150  
   151  func (s *DockerSchema1RegistrySuite) TestPushEmptyLayer(c *check.C) {
   152  	testPushEmptyLayer(c)
   153  }
   154  
   155  // testConcurrentPush pushes multiple tags to the same repo
   156  // concurrently.
   157  func testConcurrentPush(c *check.C) {
   158  	repoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
   159  
   160  	repos := []string{}
   161  	for _, tag := range []string{"push1", "push2", "push3"} {
   162  		repo := fmt.Sprintf("%v:%v", repoName, tag)
   163  		_, err := buildImage(repo, fmt.Sprintf(`
   164  	FROM busybox
   165  	ENTRYPOINT ["/bin/echo"]
   166  	ENV FOO foo
   167  	ENV BAR bar
   168  	CMD echo %s
   169  `, repo), true)
   170  		c.Assert(err, checker.IsNil)
   171  		repos = append(repos, repo)
   172  	}
   173  
   174  	// Push tags, in parallel
   175  	results := make(chan error)
   176  
   177  	for _, repo := range repos {
   178  		go func(repo string) {
   179  			_, _, err := runCommandWithOutput(exec.Command(dockerBinary, "push", repo))
   180  			results <- err
   181  		}(repo)
   182  	}
   183  
   184  	for range repos {
   185  		err := <-results
   186  		c.Assert(err, checker.IsNil, check.Commentf("concurrent push failed with error: %v", err))
   187  	}
   188  
   189  	// Clear local images store.
   190  	args := append([]string{"rmi"}, repos...)
   191  	dockerCmd(c, args...)
   192  
   193  	// Re-pull and run individual tags, to make sure pushes succeeded
   194  	for _, repo := range repos {
   195  		dockerCmd(c, "pull", repo)
   196  		dockerCmd(c, "inspect", repo)
   197  		out, _ := dockerCmd(c, "run", "--rm", repo)
   198  		c.Assert(strings.TrimSpace(out), checker.Equals, "/bin/sh -c echo "+repo)
   199  	}
   200  }
   201  
   202  func (s *DockerRegistrySuite) TestConcurrentPush(c *check.C) {
   203  	testConcurrentPush(c)
   204  }
   205  
   206  func (s *DockerSchema1RegistrySuite) TestConcurrentPush(c *check.C) {
   207  	testConcurrentPush(c)
   208  }
   209  
   210  func (s *DockerRegistrySuite) TestCrossRepositoryLayerPush(c *check.C) {
   211  	sourceRepoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
   212  	// tag the image to upload it to the private registry
   213  	dockerCmd(c, "tag", "busybox", sourceRepoName)
   214  	// push the image to the registry
   215  	out1, _, err := dockerCmdWithError("push", sourceRepoName)
   216  	c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out1))
   217  	// ensure that none of the layers were mounted from another repository during push
   218  	c.Assert(strings.Contains(out1, "Mounted from"), check.Equals, false)
   219  
   220  	digest1 := reference.DigestRegexp.FindString(out1)
   221  	c.Assert(len(digest1), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
   222  
   223  	destRepoName := fmt.Sprintf("%v/dockercli/crossrepopush", privateRegistryURL)
   224  	// retag the image to upload the same layers to another repo in the same registry
   225  	dockerCmd(c, "tag", "busybox", destRepoName)
   226  	// push the image to the registry
   227  	out2, _, err := dockerCmdWithError("push", destRepoName)
   228  	c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out2))
   229  	// ensure that layers were mounted from the first repo during push
   230  	c.Assert(strings.Contains(out2, "Mounted from dockercli/busybox"), check.Equals, true)
   231  
   232  	digest2 := reference.DigestRegexp.FindString(out2)
   233  	c.Assert(len(digest2), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
   234  	c.Assert(digest1, check.Equals, digest2)
   235  
   236  	// ensure that pushing again produces the same digest
   237  	out3, _, err := dockerCmdWithError("push", destRepoName)
   238  	c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out2))
   239  
   240  	digest3 := reference.DigestRegexp.FindString(out3)
   241  	c.Assert(len(digest2), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
   242  	c.Assert(digest3, check.Equals, digest2)
   243  
   244  	// ensure that we can pull and run the cross-repo-pushed repository
   245  	dockerCmd(c, "rmi", destRepoName)
   246  	dockerCmd(c, "pull", destRepoName)
   247  	out4, _ := dockerCmd(c, "run", destRepoName, "echo", "-n", "hello world")
   248  	c.Assert(out4, check.Equals, "hello world")
   249  }
   250  
   251  func (s *DockerSchema1RegistrySuite) TestCrossRepositoryLayerPushNotSupported(c *check.C) {
   252  	sourceRepoName := fmt.Sprintf("%v/dockercli/busybox", privateRegistryURL)
   253  	// tag the image to upload it to the private registry
   254  	dockerCmd(c, "tag", "busybox", sourceRepoName)
   255  	// push the image to the registry
   256  	out1, _, err := dockerCmdWithError("push", sourceRepoName)
   257  	c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out1))
   258  	// ensure that none of the layers were mounted from another repository during push
   259  	c.Assert(strings.Contains(out1, "Mounted from"), check.Equals, false)
   260  
   261  	digest1 := reference.DigestRegexp.FindString(out1)
   262  	c.Assert(len(digest1), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
   263  
   264  	destRepoName := fmt.Sprintf("%v/dockercli/crossrepopush", privateRegistryURL)
   265  	// retag the image to upload the same layers to another repo in the same registry
   266  	dockerCmd(c, "tag", "busybox", destRepoName)
   267  	// push the image to the registry
   268  	out2, _, err := dockerCmdWithError("push", destRepoName)
   269  	c.Assert(err, check.IsNil, check.Commentf("pushing the image to the private registry has failed: %s", out2))
   270  	// schema1 registry should not support cross-repo layer mounts, so ensure that this does not happen
   271  	c.Assert(strings.Contains(out2, "Mounted from"), check.Equals, false)
   272  
   273  	digest2 := reference.DigestRegexp.FindString(out2)
   274  	c.Assert(len(digest2), checker.GreaterThan, 0, check.Commentf("no digest found for pushed manifest"))
   275  	c.Assert(digest1, check.Not(check.Equals), digest2)
   276  
   277  	// ensure that we can pull and run the second pushed repository
   278  	dockerCmd(c, "rmi", destRepoName)
   279  	dockerCmd(c, "pull", destRepoName)
   280  	out3, _ := dockerCmd(c, "run", destRepoName, "echo", "-n", "hello world")
   281  	c.Assert(out3, check.Equals, "hello world")
   282  }
   283  
   284  func (s *DockerTrustSuite) TestTrustedPush(c *check.C) {
   285  	repoName := fmt.Sprintf("%v/dockerclitrusted/pushtest:latest", privateRegistryURL)
   286  	// tag the image and upload it to the private registry
   287  	dockerCmd(c, "tag", "busybox", repoName)
   288  
   289  	pushCmd := exec.Command(dockerBinary, "push", repoName)
   290  	s.trustedCmd(pushCmd)
   291  	out, _, err := runCommandWithOutput(pushCmd)
   292  	c.Assert(err, check.IsNil, check.Commentf("Error running trusted push: %s\n%s", err, out))
   293  	c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push"))
   294  
   295  	// Try pull after push
   296  	pullCmd := exec.Command(dockerBinary, "pull", repoName)
   297  	s.trustedCmd(pullCmd)
   298  	out, _, err = runCommandWithOutput(pullCmd)
   299  	c.Assert(err, check.IsNil, check.Commentf(out))
   300  	c.Assert(string(out), checker.Contains, "Status: Image is up to date", check.Commentf(out))
   301  
   302  	// Assert that we rotated the snapshot key to the server by checking our local keystore
   303  	contents, err := ioutil.ReadDir(filepath.Join(cliconfig.ConfigDir(), "trust/private/tuf_keys", privateRegistryURL, "dockerclitrusted/pushtest"))
   304  	c.Assert(err, check.IsNil, check.Commentf("Unable to read local tuf key files"))
   305  	// Check that we only have 1 key (targets key)
   306  	c.Assert(contents, checker.HasLen, 1)
   307  }
   308  
   309  func (s *DockerTrustSuite) TestTrustedPushWithEnvPasswords(c *check.C) {
   310  	repoName := fmt.Sprintf("%v/dockerclienv/trusted:latest", privateRegistryURL)
   311  	// tag the image and upload it to the private registry
   312  	dockerCmd(c, "tag", "busybox", repoName)
   313  
   314  	pushCmd := exec.Command(dockerBinary, "push", repoName)
   315  	s.trustedCmdWithPassphrases(pushCmd, "12345678", "12345678")
   316  	out, _, err := runCommandWithOutput(pushCmd)
   317  	c.Assert(err, check.IsNil, check.Commentf("Error running trusted push: %s\n%s", err, out))
   318  	c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push"))
   319  
   320  	// Try pull after push
   321  	pullCmd := exec.Command(dockerBinary, "pull", repoName)
   322  	s.trustedCmd(pullCmd)
   323  	out, _, err = runCommandWithOutput(pullCmd)
   324  	c.Assert(err, check.IsNil, check.Commentf(out))
   325  	c.Assert(string(out), checker.Contains, "Status: Image is up to date", check.Commentf(out))
   326  }
   327  
   328  func (s *DockerTrustSuite) TestTrustedPushWithFailingServer(c *check.C) {
   329  	repoName := fmt.Sprintf("%v/dockerclitrusted/failingserver:latest", privateRegistryURL)
   330  	// tag the image and upload it to the private registry
   331  	dockerCmd(c, "tag", "busybox", repoName)
   332  
   333  	pushCmd := exec.Command(dockerBinary, "push", repoName)
   334  	// Using a name that doesn't resolve to an address makes this test faster
   335  	s.trustedCmdWithServer(pushCmd, "https://server.invalid:81/")
   336  	out, _, err := runCommandWithOutput(pushCmd)
   337  	c.Assert(err, check.NotNil, check.Commentf("Missing error while running trusted push w/ no server"))
   338  	c.Assert(out, checker.Contains, "error contacting notary server", check.Commentf("Missing expected output on trusted push"))
   339  }
   340  
   341  func (s *DockerTrustSuite) TestTrustedPushWithoutServerAndUntrusted(c *check.C) {
   342  	repoName := fmt.Sprintf("%v/dockerclitrusted/trustedandnot:latest", privateRegistryURL)
   343  	// tag the image and upload it to the private registry
   344  	dockerCmd(c, "tag", "busybox", repoName)
   345  
   346  	pushCmd := exec.Command(dockerBinary, "push", "--disable-content-trust", repoName)
   347  	// Using a name that doesn't resolve to an address makes this test faster
   348  	s.trustedCmdWithServer(pushCmd, "https://server.invalid")
   349  	out, _, err := runCommandWithOutput(pushCmd)
   350  	c.Assert(err, check.IsNil, check.Commentf("trusted push with no server and --disable-content-trust failed: %s\n%s", err, out))
   351  	c.Assert(out, check.Not(checker.Contains), "Error establishing connection to notary repository", check.Commentf("Missing expected output on trusted push with --disable-content-trust:"))
   352  }
   353  
   354  func (s *DockerTrustSuite) TestTrustedPushWithExistingTag(c *check.C) {
   355  	repoName := fmt.Sprintf("%v/dockerclitag/trusted:latest", privateRegistryURL)
   356  	// tag the image and upload it to the private registry
   357  	dockerCmd(c, "tag", "busybox", repoName)
   358  	dockerCmd(c, "push", repoName)
   359  
   360  	pushCmd := exec.Command(dockerBinary, "push", repoName)
   361  	s.trustedCmd(pushCmd)
   362  	out, _, err := runCommandWithOutput(pushCmd)
   363  	c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out))
   364  	c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag"))
   365  
   366  	// Try pull after push
   367  	pullCmd := exec.Command(dockerBinary, "pull", repoName)
   368  	s.trustedCmd(pullCmd)
   369  	out, _, err = runCommandWithOutput(pullCmd)
   370  	c.Assert(err, check.IsNil, check.Commentf(out))
   371  	c.Assert(string(out), checker.Contains, "Status: Image is up to date", check.Commentf(out))
   372  }
   373  
   374  func (s *DockerTrustSuite) TestTrustedPushWithExistingSignedTag(c *check.C) {
   375  	repoName := fmt.Sprintf("%v/dockerclipushpush/trusted:latest", privateRegistryURL)
   376  	// tag the image and upload it to the private registry
   377  	dockerCmd(c, "tag", "busybox", repoName)
   378  
   379  	// Do a trusted push
   380  	pushCmd := exec.Command(dockerBinary, "push", repoName)
   381  	s.trustedCmd(pushCmd)
   382  	out, _, err := runCommandWithOutput(pushCmd)
   383  	c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out))
   384  	c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag"))
   385  
   386  	// Do another trusted push
   387  	pushCmd = exec.Command(dockerBinary, "push", repoName)
   388  	s.trustedCmd(pushCmd)
   389  	out, _, err = runCommandWithOutput(pushCmd)
   390  	c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out))
   391  	c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag"))
   392  
   393  	dockerCmd(c, "rmi", repoName)
   394  
   395  	// Try pull to ensure the double push did not break our ability to pull
   396  	pullCmd := exec.Command(dockerBinary, "pull", repoName)
   397  	s.trustedCmd(pullCmd)
   398  	out, _, err = runCommandWithOutput(pullCmd)
   399  	c.Assert(err, check.IsNil, check.Commentf("Error running trusted pull: %s\n%s", err, out))
   400  	c.Assert(out, checker.Contains, "Status: Downloaded", check.Commentf("Missing expected output on trusted pull with --disable-content-trust"))
   401  
   402  }
   403  
   404  func (s *DockerTrustSuite) TestTrustedPushWithIncorrectPassphraseForNonRoot(c *check.C) {
   405  	repoName := fmt.Sprintf("%v/dockercliincorretpwd/trusted:latest", privateRegistryURL)
   406  	// tag the image and upload it to the private registry
   407  	dockerCmd(c, "tag", "busybox", repoName)
   408  
   409  	// Push with default passphrases
   410  	pushCmd := exec.Command(dockerBinary, "push", repoName)
   411  	s.trustedCmd(pushCmd)
   412  	out, _, err := runCommandWithOutput(pushCmd)
   413  	c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out))
   414  	c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push:\n%s", out))
   415  
   416  	// Push with wrong passphrases
   417  	pushCmd = exec.Command(dockerBinary, "push", repoName)
   418  	s.trustedCmdWithPassphrases(pushCmd, "12345678", "87654321")
   419  	out, _, err = runCommandWithOutput(pushCmd)
   420  	c.Assert(err, check.NotNil, check.Commentf("Error missing from trusted push with short targets passphrase: \n%s", out))
   421  	c.Assert(out, checker.Contains, "could not find necessary signing keys", check.Commentf("Missing expected output on trusted push with short targets/snapsnot passphrase"))
   422  }
   423  
   424  func (s *DockerTrustSuite) TestTrustedPushWithExpiredSnapshot(c *check.C) {
   425  	c.Skip("Currently changes system time, causing instability")
   426  	repoName := fmt.Sprintf("%v/dockercliexpiredsnapshot/trusted:latest", privateRegistryURL)
   427  	// tag the image and upload it to the private registry
   428  	dockerCmd(c, "tag", "busybox", repoName)
   429  
   430  	// Push with default passphrases
   431  	pushCmd := exec.Command(dockerBinary, "push", repoName)
   432  	s.trustedCmd(pushCmd)
   433  	out, _, err := runCommandWithOutput(pushCmd)
   434  	c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out))
   435  	c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push"))
   436  
   437  	// Snapshots last for three years. This should be expired
   438  	fourYearsLater := time.Now().Add(time.Hour * 24 * 365 * 4)
   439  
   440  	runAtDifferentDate(fourYearsLater, func() {
   441  		// Push with wrong passphrases
   442  		pushCmd = exec.Command(dockerBinary, "push", repoName)
   443  		s.trustedCmd(pushCmd)
   444  		out, _, err = runCommandWithOutput(pushCmd)
   445  		c.Assert(err, check.NotNil, check.Commentf("Error missing from trusted push with expired snapshot: \n%s", out))
   446  		c.Assert(out, checker.Contains, "repository out-of-date", check.Commentf("Missing expected output on trusted push with expired snapshot"))
   447  	})
   448  }
   449  
   450  func (s *DockerTrustSuite) TestTrustedPushWithExpiredTimestamp(c *check.C) {
   451  	c.Skip("Currently changes system time, causing instability")
   452  	repoName := fmt.Sprintf("%v/dockercliexpiredtimestamppush/trusted:latest", privateRegistryURL)
   453  	// tag the image and upload it to the private registry
   454  	dockerCmd(c, "tag", "busybox", repoName)
   455  
   456  	// Push with default passphrases
   457  	pushCmd := exec.Command(dockerBinary, "push", repoName)
   458  	s.trustedCmd(pushCmd)
   459  	out, _, err := runCommandWithOutput(pushCmd)
   460  	c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out))
   461  	c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push"))
   462  
   463  	// The timestamps expire in two weeks. Lets check three
   464  	threeWeeksLater := time.Now().Add(time.Hour * 24 * 21)
   465  
   466  	// Should succeed because the server transparently re-signs one
   467  	runAtDifferentDate(threeWeeksLater, func() {
   468  		pushCmd := exec.Command(dockerBinary, "push", repoName)
   469  		s.trustedCmd(pushCmd)
   470  		out, _, err := runCommandWithOutput(pushCmd)
   471  		c.Assert(err, check.IsNil, check.Commentf("Error running trusted push: %s\n%s", err, out))
   472  		c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with expired timestamp"))
   473  	})
   474  }
   475  
   476  func (s *DockerTrustSuite) TestTrustedPushWithReleasesDelegationOnly(c *check.C) {
   477  	testRequires(c, NotaryHosting)
   478  	repoName := fmt.Sprintf("%v/dockerclireleasedelegationinitfirst/trusted", privateRegistryURL)
   479  	targetName := fmt.Sprintf("%s:latest", repoName)
   480  	s.notaryInitRepo(c, repoName)
   481  	s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public)
   482  	s.notaryPublish(c, repoName)
   483  
   484  	s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private)
   485  
   486  	// tag the image and upload it to the private registry
   487  	dockerCmd(c, "tag", "busybox", targetName)
   488  
   489  	pushCmd := exec.Command(dockerBinary, "push", targetName)
   490  	s.trustedCmd(pushCmd)
   491  	out, _, err := runCommandWithOutput(pushCmd)
   492  	c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out))
   493  	c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag"))
   494  	// check to make sure that the target has been added to targets/releases and not targets
   495  	s.assertTargetInRoles(c, repoName, "latest", "targets/releases")
   496  	s.assertTargetNotInRoles(c, repoName, "latest", "targets")
   497  
   498  	// Try pull after push
   499  	os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust"))
   500  
   501  	pullCmd := exec.Command(dockerBinary, "pull", targetName)
   502  	s.trustedCmd(pullCmd)
   503  	out, _, err = runCommandWithOutput(pullCmd)
   504  	c.Assert(err, check.IsNil, check.Commentf(out))
   505  	c.Assert(string(out), checker.Contains, "Status: Image is up to date", check.Commentf(out))
   506  }
   507  
   508  func (s *DockerTrustSuite) TestTrustedPushSignsAllFirstLevelRolesWeHaveKeysFor(c *check.C) {
   509  	testRequires(c, NotaryHosting)
   510  	repoName := fmt.Sprintf("%v/dockerclimanyroles/trusted", privateRegistryURL)
   511  	targetName := fmt.Sprintf("%s:latest", repoName)
   512  	s.notaryInitRepo(c, repoName)
   513  	s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public)
   514  	s.notaryCreateDelegation(c, repoName, "targets/role2", s.not.keys[1].Public)
   515  	s.notaryCreateDelegation(c, repoName, "targets/role3", s.not.keys[2].Public)
   516  
   517  	// import everything except the third key
   518  	s.notaryImportKey(c, repoName, "targets/role1", s.not.keys[0].Private)
   519  	s.notaryImportKey(c, repoName, "targets/role2", s.not.keys[1].Private)
   520  
   521  	s.notaryCreateDelegation(c, repoName, "targets/role1/subrole", s.not.keys[3].Public)
   522  	s.notaryImportKey(c, repoName, "targets/role1/subrole", s.not.keys[3].Private)
   523  
   524  	s.notaryPublish(c, repoName)
   525  
   526  	// tag the image and upload it to the private registry
   527  	dockerCmd(c, "tag", "busybox", targetName)
   528  
   529  	pushCmd := exec.Command(dockerBinary, "push", targetName)
   530  	s.trustedCmd(pushCmd)
   531  	out, _, err := runCommandWithOutput(pushCmd)
   532  	c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out))
   533  	c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag"))
   534  
   535  	// check to make sure that the target has been added to targets/role1 and targets/role2, and
   536  	// not targets (because there are delegations) or targets/role3 (due to missing key) or
   537  	// targets/role1/subrole (due to it being a second level delegation)
   538  	s.assertTargetInRoles(c, repoName, "latest", "targets/role1", "targets/role2")
   539  	s.assertTargetNotInRoles(c, repoName, "latest", "targets")
   540  
   541  	// Try pull after push
   542  	os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust"))
   543  
   544  	// pull should fail because none of these are the releases role
   545  	pullCmd := exec.Command(dockerBinary, "pull", targetName)
   546  	s.trustedCmd(pullCmd)
   547  	out, _, err = runCommandWithOutput(pullCmd)
   548  	c.Assert(err, check.NotNil, check.Commentf(out))
   549  }
   550  
   551  func (s *DockerTrustSuite) TestTrustedPushSignsForRolesWithKeysAndValidPaths(c *check.C) {
   552  	repoName := fmt.Sprintf("%v/dockerclirolesbykeysandpaths/trusted", privateRegistryURL)
   553  	targetName := fmt.Sprintf("%s:latest", repoName)
   554  	s.notaryInitRepo(c, repoName)
   555  	s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public, "l", "z")
   556  	s.notaryCreateDelegation(c, repoName, "targets/role2", s.not.keys[1].Public, "x", "y")
   557  	s.notaryCreateDelegation(c, repoName, "targets/role3", s.not.keys[2].Public, "latest")
   558  	s.notaryCreateDelegation(c, repoName, "targets/role4", s.not.keys[3].Public, "latest")
   559  
   560  	// import everything except the third key
   561  	s.notaryImportKey(c, repoName, "targets/role1", s.not.keys[0].Private)
   562  	s.notaryImportKey(c, repoName, "targets/role2", s.not.keys[1].Private)
   563  	s.notaryImportKey(c, repoName, "targets/role4", s.not.keys[3].Private)
   564  
   565  	s.notaryPublish(c, repoName)
   566  
   567  	// tag the image and upload it to the private registry
   568  	dockerCmd(c, "tag", "busybox", targetName)
   569  
   570  	pushCmd := exec.Command(dockerBinary, "push", targetName)
   571  	s.trustedCmd(pushCmd)
   572  	out, _, err := runCommandWithOutput(pushCmd)
   573  	c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out))
   574  	c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag"))
   575  
   576  	// check to make sure that the target has been added to targets/role1 and targets/role4, and
   577  	// not targets (because there are delegations) or targets/role2 (due to path restrictions) or
   578  	// targets/role3 (due to missing key)
   579  	s.assertTargetInRoles(c, repoName, "latest", "targets/role1", "targets/role4")
   580  	s.assertTargetNotInRoles(c, repoName, "latest", "targets")
   581  
   582  	// Try pull after push
   583  	os.RemoveAll(filepath.Join(cliconfig.ConfigDir(), "trust"))
   584  
   585  	// pull should fail because none of these are the releases role
   586  	pullCmd := exec.Command(dockerBinary, "pull", targetName)
   587  	s.trustedCmd(pullCmd)
   588  	out, _, err = runCommandWithOutput(pullCmd)
   589  	c.Assert(err, check.NotNil, check.Commentf(out))
   590  }
   591  
   592  func (s *DockerTrustSuite) TestTrustedPushDoesntSignTargetsIfDelegationsExist(c *check.C) {
   593  	testRequires(c, NotaryHosting)
   594  	repoName := fmt.Sprintf("%v/dockerclireleasedelegationnotsignable/trusted", privateRegistryURL)
   595  	targetName := fmt.Sprintf("%s:latest", repoName)
   596  	s.notaryInitRepo(c, repoName)
   597  	s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public)
   598  	s.notaryPublish(c, repoName)
   599  
   600  	// do not import any delegations key
   601  
   602  	// tag the image and upload it to the private registry
   603  	dockerCmd(c, "tag", "busybox", targetName)
   604  
   605  	pushCmd := exec.Command(dockerBinary, "push", targetName)
   606  	s.trustedCmd(pushCmd)
   607  	out, _, err := runCommandWithOutput(pushCmd)
   608  	c.Assert(err, check.NotNil, check.Commentf("trusted push succeeded but should have failed:\n%s", out))
   609  	c.Assert(out, checker.Contains, "no valid signing keys",
   610  		check.Commentf("Missing expected output on trusted push without keys"))
   611  
   612  	s.assertTargetNotInRoles(c, repoName, "latest", "targets", "targets/role1")
   613  }
   614  
   615  func (s *DockerRegistryAuthHtpasswdSuite) TestPushNoCredentialsNoRetry(c *check.C) {
   616  	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
   617  	dockerCmd(c, "tag", "busybox", repoName)
   618  	out, _, err := dockerCmdWithError("push", repoName)
   619  	c.Assert(err, check.NotNil, check.Commentf(out))
   620  	c.Assert(out, check.Not(checker.Contains), "Retrying")
   621  	c.Assert(out, checker.Contains, "no basic auth credentials")
   622  }
   623  
   624  // This may be flaky but it's needed not to regress on unauthorized push, see #21054
   625  func (s *DockerSuite) TestPushToCentralRegistryUnauthorized(c *check.C) {
   626  	testRequires(c, Network)
   627  	repoName := "test/busybox"
   628  	dockerCmd(c, "tag", "busybox", repoName)
   629  	out, _, err := dockerCmdWithError("push", repoName)
   630  	c.Assert(err, check.NotNil, check.Commentf(out))
   631  	c.Assert(out, check.Not(checker.Contains), "Retrying")
   632  }
   633  
   634  func getTestTokenService(status int, body string, retries int) *httptest.Server {
   635  	var mu sync.Mutex
   636  	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   637  		mu.Lock()
   638  		if retries > 0 {
   639  			w.WriteHeader(http.StatusServiceUnavailable)
   640  			w.Header().Set("Content-Type", "application/json")
   641  			w.Write([]byte(`{"errors":[{"code":"UNAVAILABLE","message":"cannot create token at this time"}]}`))
   642  			retries--
   643  		} else {
   644  			w.WriteHeader(status)
   645  			w.Header().Set("Content-Type", "application/json")
   646  			w.Write([]byte(body))
   647  		}
   648  		mu.Unlock()
   649  	}))
   650  }
   651  
   652  func (s *DockerRegistryAuthTokenSuite) TestPushTokenServiceUnauthResponse(c *check.C) {
   653  	ts := getTestTokenService(http.StatusUnauthorized, `{"errors": [{"Code":"UNAUTHORIZED", "message": "a message", "detail": null}]}`, 0)
   654  	defer ts.Close()
   655  	s.setupRegistryWithTokenService(c, ts.URL)
   656  	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
   657  	dockerCmd(c, "tag", "busybox", repoName)
   658  	out, _, err := dockerCmdWithError("push", repoName)
   659  	c.Assert(err, check.NotNil, check.Commentf(out))
   660  	c.Assert(out, checker.Not(checker.Contains), "Retrying")
   661  	c.Assert(out, checker.Contains, "unauthorized: a message")
   662  }
   663  
   664  func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseUnauthorized(c *check.C) {
   665  	ts := getTestTokenService(http.StatusUnauthorized, `{"error": "unauthorized"}`, 0)
   666  	defer ts.Close()
   667  	s.setupRegistryWithTokenService(c, ts.URL)
   668  	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
   669  	dockerCmd(c, "tag", "busybox", repoName)
   670  	out, _, err := dockerCmdWithError("push", repoName)
   671  	c.Assert(err, check.NotNil, check.Commentf(out))
   672  	c.Assert(out, checker.Not(checker.Contains), "Retrying")
   673  	split := strings.Split(out, "\n")
   674  	c.Assert(split[len(split)-2], check.Equals, "unauthorized: authentication required")
   675  }
   676  
   677  func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseError(c *check.C) {
   678  	ts := getTestTokenService(http.StatusTooManyRequests, `{"errors": [{"code":"TOOMANYREQUESTS","message":"out of tokens"}]}`, 4)
   679  	defer ts.Close()
   680  	s.setupRegistryWithTokenService(c, ts.URL)
   681  	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
   682  	dockerCmd(c, "tag", "busybox", repoName)
   683  	out, _, err := dockerCmdWithError("push", repoName)
   684  	c.Assert(err, check.NotNil, check.Commentf(out))
   685  	c.Assert(out, checker.Contains, "Retrying")
   686  	c.Assert(out, checker.Not(checker.Contains), "Retrying in 15")
   687  	split := strings.Split(out, "\n")
   688  	c.Assert(split[len(split)-2], check.Equals, "toomanyrequests: out of tokens")
   689  }
   690  
   691  func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseUnparsable(c *check.C) {
   692  	ts := getTestTokenService(http.StatusForbidden, `no way`, 0)
   693  	defer ts.Close()
   694  	s.setupRegistryWithTokenService(c, ts.URL)
   695  	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
   696  	dockerCmd(c, "tag", "busybox", repoName)
   697  	out, _, err := dockerCmdWithError("push", repoName)
   698  	c.Assert(err, check.NotNil, check.Commentf(out))
   699  	c.Assert(out, checker.Not(checker.Contains), "Retrying")
   700  	split := strings.Split(out, "\n")
   701  	c.Assert(split[len(split)-2], checker.Contains, "error parsing HTTP 403 response body: ")
   702  }
   703  
   704  func (s *DockerRegistryAuthTokenSuite) TestPushMisconfiguredTokenServiceResponseNoToken(c *check.C) {
   705  	ts := getTestTokenService(http.StatusOK, `{"something": "wrong"}`, 0)
   706  	defer ts.Close()
   707  	s.setupRegistryWithTokenService(c, ts.URL)
   708  	repoName := fmt.Sprintf("%s/busybox", privateRegistryURL)
   709  	dockerCmd(c, "tag", "busybox", repoName)
   710  	out, _, err := dockerCmdWithError("push", repoName)
   711  	c.Assert(err, check.NotNil, check.Commentf(out))
   712  	c.Assert(out, checker.Not(checker.Contains), "Retrying")
   713  	split := strings.Split(out, "\n")
   714  	c.Assert(split[len(split)-2], check.Equals, "authorization server did not include a token in the response")
   715  }