Skip to content

Commit

Permalink
notary delete CLI command and integration test
Browse files Browse the repository at this point in the history
Signed-off-by: Riyaz Faizullabhoy <riyaz.faizullabhoy@docker.com>
  • Loading branch information
riyazdf committed Aug 4, 2016
1 parent dc74bd3 commit df1c425
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 15 deletions.
167 changes: 152 additions & 15 deletions cmd/notary/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ import (
"github.com/Sirupsen/logrus"
ctxu "github.com/docker/distribution/context"
"github.com/docker/notary"
"github.com/docker/notary/client"
"github.com/docker/notary/cryptoservice"
"github.com/docker/notary/passphrase"
"github.com/docker/notary/server"
"github.com/docker/notary/server/storage"
notaryStorage "github.com/docker/notary/storage"
"github.com/docker/notary/trustmanager"
"github.com/docker/notary/tuf/data"
"github.com/docker/notary/tuf/utils"
Expand Down Expand Up @@ -245,6 +247,156 @@ func TestClientTUFInteraction(t *testing.T) {
require.False(t, strings.Contains(string(output), target))
}

func TestClientDeleteTUFInteraction(t *testing.T) {
// -- setup --
setUp(t)

tempDir := tempDirWithConfig(t, "{}")
defer os.RemoveAll(tempDir)

server := setupServer()
defer server.Close()

tempFile, err := ioutil.TempFile("", "targetfile")
require.NoError(t, err)
tempFile.Close()
defer os.Remove(tempFile.Name())

// Setup certificate
certFile, err := ioutil.TempFile("", "pemfile")
require.NoError(t, err)

privKey, err := utils.GenerateECDSAKey(rand.Reader)
startTime := time.Now()
endTime := startTime.AddDate(10, 0, 0)
cert, err := cryptoservice.GenerateCertificate(privKey, "gun", startTime, endTime)
require.NoError(t, err)

_, err = certFile.Write(utils.CertToPEM(cert))
require.NoError(t, err)
tempFile.Close()
defer os.Remove(certFile.Name())

var (
output string
target = "helloIamanotarytarget"
)
// -- tests --

// init repo
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun")
require.NoError(t, err)

// add a target
_, err = runCommand(t, tempDir, "add", "gun", target, tempFile.Name())
require.NoError(t, err)

// check status - see target
output, err = runCommand(t, tempDir, "status", "gun")
require.NoError(t, err)
require.True(t, strings.Contains(output, target))

// publish repo
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
require.NoError(t, err)

// list repo - see target
output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun")
require.NoError(t, err)
require.True(t, strings.Contains(string(output), target))

// add a delegation and publish
output, err = runCommand(t, tempDir, "delegation", "add", "gun", "targets/delegation", certFile.Name())
require.NoError(t, err)
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
require.NoError(t, err)

// list delegations - see role
output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun")
require.NoError(t, err)
require.True(t, strings.Contains(string(output), "targets/delegation"))

// Delete the repo metadata locally, so no need for server URL
output, err = runCommand(t, tempDir, "delete", "gun")
require.NoError(t, err)
assertLocalMetadataForGun(t, tempDir, "gun", false)

// list repo - see target still because remote data exists
output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun")
require.NoError(t, err)
require.True(t, strings.Contains(string(output), target))

// list delegations - see role because remote data still exists
output, err = runCommand(t, tempDir, "-s", server.URL, "delegation", "list", "gun")
require.NoError(t, err)
require.True(t, strings.Contains(string(output), "targets/delegation"))

// Trying to delete the repo with the remote flag fails if it's given a badly formed URL
output, err = runCommand(t, tempDir, "-s", "//invalidURLType", "delete", "gun", "--remote")
require.Error(t, err)
// since the connection fails to parse the URL before we can delete anything, local data should exist
assertLocalMetadataForGun(t, tempDir, "gun", true)

// Trying to delete the repo with the remote flag fails if it's given a well-formed URL that doesn't point to a server
output, err = runCommand(t, tempDir, "-s", "https://invalid-server", "delete", "gun", "--remote")
require.Error(t, err)
require.IsType(t, notaryStorage.ErrOffline{}, err)
// In this case, local notary metadata does not exist since local deletion operates first if we have a valid transport
assertLocalMetadataForGun(t, tempDir, "gun", false)

// Delete the repo remotely and locally, pointing to the correct server
output, err = runCommand(t, tempDir, "-s", server.URL, "delete", "gun", "--remote")
require.NoError(t, err)
assertLocalMetadataForGun(t, tempDir, "gun", false)
_, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun")
require.Error(t, err)
require.IsType(t, client.ErrRepositoryNotExist{}, err)

// Silent success on extraneous deletes
output, err = runCommand(t, tempDir, "-s", server.URL, "delete", "gun", "--remote")
require.NoError(t, err)
assertLocalMetadataForGun(t, tempDir, "gun", false)
_, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun")
require.Error(t, err)
require.IsType(t, client.ErrRepositoryNotExist{}, err)

// Now check that we can re-publish the same repo
// init repo
_, err = runCommand(t, tempDir, "-s", server.URL, "init", "gun")
require.NoError(t, err)

// add a target
_, err = runCommand(t, tempDir, "add", "gun", target, tempFile.Name())
require.NoError(t, err)

// check status - see target
output, err = runCommand(t, tempDir, "status", "gun")
require.NoError(t, err)
require.True(t, strings.Contains(output, target))

// publish repo
_, err = runCommand(t, tempDir, "-s", server.URL, "publish", "gun")
require.NoError(t, err)

// list repo - see target
output, err = runCommand(t, tempDir, "-s", server.URL, "list", "gun")
require.NoError(t, err)
require.True(t, strings.Contains(string(output), target))
}

func assertLocalMetadataForGun(t *testing.T, configDir, gun string, shouldExist bool) {
for _, role := range data.BaseRoles {
fileInfo, err := os.Stat(filepath.Join(configDir, "tuf", gun, "metadata", role+".json"))
if shouldExist {
require.NoError(t, err)
require.NotNil(t, fileInfo)
} else {
require.Error(t, err)
require.Nil(t, fileInfo)
}
}
}

// Initializes a repo, adds a target, publishes the target by hash, lists the target,
// verifies the target, and then removes the target.
func TestClientTUFAddByHashInteraction(t *testing.T) {
Expand Down Expand Up @@ -1171,21 +1323,6 @@ func TestClientKeyGenerationRotation(t *testing.T) {
require.True(t, strings.Contains(string(output), target))
}

// Helper method to get the subdirectory for TUF keys
func getKeySubdir(role, gun string) string {
subdir := notary.PrivDir
switch role {
case data.CanonicalRootRole:
return filepath.Join(subdir, notary.RootKeysSubdir)
case data.CanonicalTargetsRole:
return filepath.Join(subdir, notary.NonRootKeysSubdir, gun)
case data.CanonicalSnapshotRole:
return filepath.Join(subdir, notary.NonRootKeysSubdir, gun)
default:
return filepath.Join(subdir, notary.NonRootKeysSubdir)
}
}

// Tests default root key generation
func TestDefaultRootKeyGeneration(t *testing.T) {
// -- setup --
Expand Down
1 change: 1 addition & 0 deletions cmd/notary/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ var exampleValidCommands = []string{
"delegation add repo targets/releases path/to/pem/file.pem",
"delegation remove repo targets/releases",
"witness gun targets/releases",
"delete repo",
}

// config parsing bugs are propagated in all commands
Expand Down
47 changes: 47 additions & 0 deletions cmd/notary/tuf.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,12 @@ var cmdWitnessTemplate = usageTemplate{
Long: "Marks roles to be re-signed the next time they're published. Currently will always bump version and expiry for role. N.B. behaviour may change when thresholding is introduced.",
}

var cmdTUFDeleteTemplate = usageTemplate{
Use: "delete [ GUN ]",
Short: "Deletes all content for a trusted collection",
Long: "Deletes all local content for a trusted collection identified by the Globally Unique Name. Remote data can also be deleted with an additional flag.",
}

type tufCommander struct {
// these need to be set
configGetter func() (*viper.Viper, error)
Expand All @@ -107,6 +113,8 @@ type tufCommander struct {
deleteIdx []int
reset bool
archiveChangelist string

deleteRemote bool
}

func (t *tufCommander) AddToCommand(cmd *cobra.Command) {
Expand Down Expand Up @@ -149,6 +157,10 @@ func (t *tufCommander) AddToCommand(cmd *cobra.Command) {

cmdWitness := cmdWitnessTemplate.ToCommand(t.tufWitness)
cmd.AddCommand(cmdWitness)

cmdTUFDeleteGUN := cmdTUFDeleteTemplate.ToCommand(t.tufDeleteGUN)
cmdTUFDeleteGUN.Flags().BoolVar(&t.deleteRemote, "remote", false, "Delete remote data for GUN in addition to local cache")
cmd.AddCommand(cmdTUFDeleteGUN)
}

func (t *tufCommander) tufWitness(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -301,6 +313,41 @@ func (t *tufCommander) tufAdd(cmd *cobra.Command, args []string) error {
return nil
}

func (t *tufCommander) tufDeleteGUN(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
cmd.Usage()
return fmt.Errorf("Must specify a GUN")
}
config, err := t.configGetter()
if err != nil {
return err
}

gun := args[0]

trustPin, err := getTrustPinning(config)

// Only initialize a roundtripper if we get the remote flag
var rt http.RoundTripper
if t.deleteRemote {
rt, err = getTransport(config, gun, admin)
if err != nil {
return err
}
}

nRepo, err := notaryclient.NewNotaryRepository(
config.GetString("trust_dir"), gun, getRemoteTrustServer(config), rt, t.retriever, trustPin)

if err != nil {
return err
}

cmd.Printf("Deleting trust data for repository %s.\n", gun)

return nRepo.DeleteTrustData(t.deleteRemote)
}

func (t *tufCommander) tufInit(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
cmd.Usage()
Expand Down

0 comments on commit df1c425

Please sign in to comment.