diff --git a/client/client.go b/client/client.go index ee376053c3..ce7a519af1 100644 --- a/client/client.go +++ b/client/client.go @@ -326,6 +326,17 @@ func (r *NotaryRepository) GetTargetByName(name string) (*Target, error) { return &Target{Name: name, Hashes: meta.Hashes, Length: meta.Length}, nil } +// GetChangelist returns the list of the repository's unpublished changes +func (r *NotaryRepository) GetChangelist() (changelist.Changelist, error) { + changelistDir := filepath.Join(r.tufRepoPath, "changelist") + cl, err := changelist.NewFileChangelist(changelistDir) + if err != nil { + logrus.Debug("Error initializing changelist") + return nil, err + } + return cl, nil +} + // 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 { @@ -371,11 +382,8 @@ func (r *NotaryRepository) Publish() error { return err } } - // load the changelist for this repo - changelistDir := filepath.Join(r.tufRepoPath, "changelist") - cl, err := changelist.NewFileChangelist(changelistDir) + cl, err := r.GetChangelist() if err != nil { - logrus.Debug("Error initializing changelist") return err } // apply the changelist to the repo @@ -445,7 +453,7 @@ func (r *NotaryRepository) Publish() error { // This is not a critical problem when only a single host is pushing // but will cause weird behaviour if changelist cleanup is failing // and there are multiple hosts writing to the repo. - logrus.Warn("Unable to clear changelist. You may want to manually delete the folder ", changelistDir) + logrus.Warn("Unable to clear changelist. You may want to manually delete the folder ", filepath.Join(r.tufRepoPath, "changelist")) } return nil } diff --git a/client/client_test.go b/client/client_test.go index f6ed1523ec..082dd0bfd6 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -564,6 +564,30 @@ func testPublish(t *testing.T, rootType data.KeyAlgorithm) { changelistDir.Close() + // Test loading changelist + changes := make(map[string]changelist.Change) + cl, err := repo.GetChangelist() + assert.NoError(t, err, "could not get changelist for repo") + + assert.Len(t, cl.List(), 2, "Wrong number of changes returned from changelist") + for _, ch := range cl.List() { + changes[ch.Path()] = ch + } + + currentChange := changes["current"] + assert.NotNil(t, currentChange, "Expected changelist to contain a change for path 'current'") + assert.EqualValues(t, changelist.ActionCreate, currentChange.Action()) + assert.Equal(t, "targets", currentChange.Scope()) + assert.Equal(t, "target", currentChange.Type()) + assert.Equal(t, "current", currentChange.Path()) + + latestChange := changes["latest"] + assert.NotNil(t, latestChange, "Expected changelist to contain a change for path 'latest'") + assert.EqualValues(t, changelist.ActionCreate, latestChange.Action()) + assert.Equal(t, "targets", latestChange.Scope()) + assert.Equal(t, "target", latestChange.Type()) + assert.Equal(t, "latest", latestChange.Path()) + // Now test Publish err = repo.Publish() assert.NoError(t, err) diff --git a/cmd/notary/main.go b/cmd/notary/main.go index 6772e5dfe6..2f59fc3c36 100644 --- a/cmd/notary/main.go +++ b/cmd/notary/main.go @@ -103,6 +103,7 @@ func main() { cmdTufList.Flags().StringVarP(&remoteTrustServer, "server", "s", serverURL, "Remote trust server location") notaryCmd.AddCommand(cmdTufAdd) notaryCmd.AddCommand(cmdTufRemove) + notaryCmd.AddCommand(cmdTufStatus) notaryCmd.AddCommand(cmdTufPublish) cmdTufPublish.Flags().StringVarP(&remoteTrustServer, "server", "s", serverURL, "Remote trust server location") notaryCmd.AddCommand(cmdTufLookup) diff --git a/cmd/notary/tuf.go b/cmd/notary/tuf.go index 9067f8b703..5273e614cd 100644 --- a/cmd/notary/tuf.go +++ b/cmd/notary/tuf.go @@ -66,6 +66,13 @@ var cmdTufPublish = &cobra.Command{ Run: tufPublish, } +var cmdTufStatus = &cobra.Command{ + Use: "status [ GUN ]", + Short: "displays status of unpublished changes to the local trusted collection.", + Long: "displays status of unpublished changes to the local trusted collection identified by the Globally Unique Name.", + Run: tufStatus, +} + var cmdVerify = &cobra.Command{ Use: "verify [ GUN ] ", Short: "verifies if the content is included in the trusted collection", @@ -192,6 +199,38 @@ func tufLookup(cmd *cobra.Command, args []string) { fmt.Println(target.Name, fmt.Sprintf("sha256:%x", target.Hashes["sha256"]), target.Length) } +func tufStatus(cmd *cobra.Command, args []string) { + if len(args) < 1 { + cmd.Usage() + fatalf("Must specify a GUN") + } + + gun := args[0] + parseConfig() + + nRepo, err := notaryclient.NewNotaryRepository(trustDir, gun, remoteTrustServer, nil, retriever) + if err != nil { + fatalf(err.Error()) + } + + cl, err := nRepo.GetChangelist() + if err != nil { + fatalf(err.Error()) + } + + if len(cl.List()) == 0 { + fmt.Printf("No unpublished changes for %s\n", gun) + return + } + + fmt.Printf("Unpublished changes for %s:\n\n", gun) + fmt.Printf("%-10s%-10s%-12s%s\n", "action", "scope", "type", "path") + fmt.Println("----------------------------------------------------") + for _, ch := range cl.List() { + fmt.Printf("%-10s%-10s%-12s%s\n", ch.Action(), ch.Scope(), ch.Type(), ch.Path()) + } +} + func tufPublish(cmd *cobra.Command, args []string) { if len(args) < 1 { cmd.Usage()