diff --git a/client/client.go b/client/client.go index e837552318..4d3a757d2e 100644 --- a/client/client.go +++ b/client/client.go @@ -522,7 +522,7 @@ func (r *NotaryRepository) GetChangelist() (changelist.Changelist, error) { // Publish pushes the local changes in signed material to the remote notary-server // Conceptually it performs an operation similar to a `git rebase` func (r *NotaryRepository) Publish() error { - var updateRoot bool + var initialPublish bool // attempt to initialize the repo from the remote store c, err := r.bootstrapClient() if err != nil { @@ -538,10 +538,11 @@ func (r *NotaryRepository) Publish() error { return err } // We had local data but the server doesn't know about the repo yet, - // ensure we will push the initial root file. The root may not - // be marked as Dirty, since there may not be any changes that - // update it, so use a different boolean. - updateRoot = true + // ensure we will push the initial root and targets file. Either or + // both of the root and targets may not be marked as Dirty, since + // there may not be any changes that update them, so use a + // different boolean. + initialPublish = true } else { // The remote store returned an error other than 404. We're // unable to determine if the repo has been initialized or not. @@ -576,7 +577,7 @@ func (r *NotaryRepository) Publish() error { 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 || updateRoot { + if nearExpiry(r.tufRepo.Root) || r.tufRepo.Root.Dirty || initialPublish { rootJSON, err := serializeCanonicalRole(r.tufRepo, data.CanonicalRootRole) if err != nil { return err @@ -586,7 +587,7 @@ func (r *NotaryRepository) Publish() error { // iterate through all the targets files - if they are dirty, sign and update for roleName, roleObj := range r.tufRepo.Targets { - if roleObj.Dirty { + if roleObj.Dirty || (roleName == data.CanonicalTargetsRole && initialPublish) { targetsJSON, err := serializeCanonicalRole(r.tufRepo, roleName) if err != nil { return err diff --git a/client/client_test.go b/client/client_test.go index 21faf6fb63..0a29aa2677 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -1140,28 +1140,74 @@ func testGetChangelist(t *testing.T, rootType string) { assert.Equal(t, "latest", latestChange.Path()) } -// Create a repo, instantiate a notary server, and publish the repo to the -// server, signing all the non-timestamp metadata. +// Create a repo, instantiate a notary server, and publish the bare repo to the +// server, signing all the non-timestamp metadata. Root, targets, and snapshots +// (if locally signing) should be sent. +func TestPublishBareRepo(t *testing.T) { + testPublishNoData(t, data.ECDSAKey, true) + testPublishNoData(t, data.ECDSAKey, false) + if !testing.Short() { + testPublishNoData(t, data.RSAKey, true) + testPublishNoData(t, data.RSAKey, false) + } +} + +func testPublishNoData(t *testing.T, rootType string, serverManagesSnapshot bool) { + var tempDirs [2]string + for i := 0; i < 2; i++ { + tempBaseDir, err := ioutil.TempDir("", "notary-test-") + assert.NoError(t, err, "failed to create a temporary directory: %s", err) + defer os.RemoveAll(tempBaseDir) + tempDirs[i] = tempBaseDir + } + + gun := "docker.com/notary" + ts := fullTestServer(t) + defer ts.Close() + + repo1, _ := initializeRepo(t, rootType, tempDirs[0], gun, ts.URL, + serverManagesSnapshot) + assert.NoError(t, repo1.Publish()) + + // use another repo to check metadata + repo2, err := NewNotaryRepository(tempDirs[1], gun, ts.URL, + http.DefaultTransport, passphraseRetriever) + assert.NoError(t, err, "error creating repository: %s", err) + + targets, err := repo2.ListTargets() + assert.NoError(t, err) + assert.Empty(t, targets) + + for role := range data.ValidRoles { + // we don't cache timstamp metadata + if role != data.CanonicalTimestampRole { + assertRepoHasExpectedMetadata(t, repo2, role, true) + } + } +} + +// Create a repo, instantiate a notary server, and publish the repo with +// some targets to the server, signing all the non-timestamp metadata. // We test this with both an RSA and ECDSA root key func TestPublishClientHasSnapshotKey(t *testing.T) { - testPublish(t, data.ECDSAKey, false) + testPublishWithData(t, data.ECDSAKey, false) if !testing.Short() { - testPublish(t, data.RSAKey, false) + testPublishWithData(t, data.RSAKey, false) } } // Create a repo, instantiate a notary server (designating the server as the -// snapshot signer) , and publish the repo to the server, signing the root and -// targets metadata only. The server should sign just fine. +// snapshot signer) , and publish the repo with some targets to the server, +// signing the root and targets metadata only. The server should sign just fine. // We test this with both an RSA and ECDSA root key func TestPublishAfterInitServerHasSnapshotKey(t *testing.T) { - testPublish(t, data.ECDSAKey, true) + testPublishWithData(t, data.ECDSAKey, true) if !testing.Short() { - testPublish(t, data.RSAKey, true) + testPublishWithData(t, data.RSAKey, true) } } -func testPublish(t *testing.T, rootType string, serverManagesSnapshot bool) { +func testPublishWithData(t *testing.T, rootType string, serverManagesSnapshot bool) { // Temporary directory where test files will be created tempBaseDir, err := ioutil.TempDir("/tmp", "notary-test-") defer os.RemoveAll(tempBaseDir)