From 5b6f64de4bd3a381246100f1f53c99f899aac98c Mon Sep 17 00:00:00 2001 From: Ying Li Date: Thu, 5 Nov 2015 10:46:02 -0800 Subject: [PATCH] Refactor notary client tests. Move common code out into helper functions, and split up the bigger tests into tests that specifically test adding targets, getting changelists, publishing, and listing, as opposed to having two giant tests instead. Also depend more on existing functions in the code (such as NotaryRepository.GetChangelists and the server ServerMux), rather than reimplementing them in the tests. Signed-off-by: Ying Li Signed-off-by: David Lawrence Signed-off-by: Ying Li (github: endophage) --- client/client_root_validation_test.go | 67 +-- client/client_test.go | 575 ++++++++++++-------------- 2 files changed, 265 insertions(+), 377 deletions(-) diff --git a/client/client_root_validation_test.go b/client/client_root_validation_test.go index 1bc8763c99..752adbe4bc 100644 --- a/client/client_root_validation_test.go +++ b/client/client_root_validation_test.go @@ -1,12 +1,8 @@ package client import ( - "encoding/json" - "fmt" "io/ioutil" - "net/http" "os" - "path/filepath" "testing" "github.com/Sirupsen/logrus" @@ -37,17 +33,10 @@ func validateRootSuccessfully(t *testing.T, rootType string) { gun := "docker.com/notary" - ts, mux := createTestServer(t) + ts, mux := simpleTestServer(t) defer ts.Close() - repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever) - assert.NoError(t, err, "error creating repository: %s", err) - - rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType) - assert.NoError(t, err, "error generating root key: %s", err) - - err = repo.Initialize(rootKeyID) - assert.NoError(t, err, "error creating repository: %s", err) + repo, _ := initializeRepo(t, rootType, tempBaseDir, gun, ts.URL) // tests need to manually boostrap timestamp as client doesn't generate it err = repo.tufRepo.InitTimestamp() @@ -58,50 +47,7 @@ func validateRootSuccessfully(t *testing.T, rootType string) { allCerts := repo.KeyStoreManager.TrustedCertificateStore().GetCertificates() assert.Len(t, allCerts, 1) - // Now test ListTargets. In preparation, we need to expose some signed - // metadata files on the internal HTTP server. - tempKey, err := data.UnmarshalPrivateKey([]byte(timestampECDSAKeyJSON)) - assert.NoError(t, err) - - repo.KeyStoreManager.KeyStore.AddKey(filepath.Join(filepath.FromSlash(gun), tempKey.ID()), "root", tempKey) - - // Because ListTargets will clear this - savedTUFRepo := repo.tufRepo - - rootJSONFile := filepath.Join(tempBaseDir, "tuf", filepath.FromSlash(gun), "metadata", "root.json") - rootFileBytes, err := ioutil.ReadFile(rootJSONFile) - - signedTargets, err := savedTUFRepo.SignTargets("targets", data.DefaultExpires("targets")) - assert.NoError(t, err) - - signedSnapshot, err := savedTUFRepo.SignSnapshot(data.DefaultExpires("snapshot")) - assert.NoError(t, err) - - signedTimestamp, err := savedTUFRepo.SignTimestamp(data.DefaultExpires("timestamp")) - assert.NoError(t, err) - - mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/root.json", func(w http.ResponseWriter, r *http.Request) { - assert.NoError(t, err) - fmt.Fprint(w, string(rootFileBytes)) - }) - - mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/timestamp.json", func(w http.ResponseWriter, r *http.Request) { - timestampJSON, _ := json.Marshal(signedTimestamp) - fmt.Fprint(w, string(timestampJSON)) - }) - - mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/snapshot.json", func(w http.ResponseWriter, r *http.Request) { - snapshotJSON, _ := json.Marshal(signedSnapshot) - fmt.Fprint(w, string(snapshotJSON)) - }) - - mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/targets.json", func(w http.ResponseWriter, r *http.Request) { - targetsJSON, _ := json.Marshal(signedTargets) - fmt.Fprint(w, string(targetsJSON)) - }) - - _, err = repo.ListTargets() - assert.NoError(t, err) + fakeServerData(t, repo, mux) // // Test TOFUS logic. We remove all certs and expect a new one to be added after ListTargets @@ -126,13 +72,16 @@ func validateRootSuccessfully(t *testing.T, rootType string) { assert.Len(t, repo.KeyStoreManager.TrustedCertificateStore().GetCertificates(), 0) // Add a previously generated certificate with CN=docker.com/notary - err = repo.KeyStoreManager.TrustedCertificateStore().AddCertFromFile("../fixtures/self-signed_docker.com-notary.crt") + err = repo.KeyStoreManager.TrustedCertificateStore().AddCertFromFile( + "../fixtures/self-signed_docker.com-notary.crt") assert.NoError(t, err) // This list targets is expected to fail, since there already exists a certificate // in the store for the dnsName docker.com/notary, so TOFUS doesn't apply _, err = repo.ListTargets() if assert.Error(t, err, "An error was expected") { - assert.Equal(t, err, &keystoremanager.ErrValidationFail{Reason: "failed to validate data with current trusted certificates"}) + assert.Equal(t, err, &keystoremanager.ErrValidationFail{ + Reason: "failed to validate data with current trusted certificates", + }) } } diff --git a/client/client_test.go b/client/client_test.go index eb2864ff52..156d640ccf 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -1,6 +1,7 @@ package client import ( + "bytes" "encoding/json" "fmt" "io/ioutil" @@ -11,15 +12,14 @@ import ( "strings" "testing" - "github.com/docker/distribution/registry/api/v2" + "github.com/Sirupsen/logrus" + ctxu "github.com/docker/distribution/context" "github.com/docker/notary/client/changelist" "github.com/docker/notary/cryptoservice" - "github.com/docker/notary/server/handlers" + "github.com/docker/notary/server" "github.com/docker/notary/server/storage" "github.com/docker/notary/trustmanager" "github.com/docker/notary/tuf/data" - "github.com/docker/notary/utils" - "github.com/gorilla/mux" "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -28,7 +28,7 @@ const timestampKeyJSON = `{"keytype":"rsa","keyval":{"public":"MIIBIjANBgkqhkiG9 const timestampECDSAKeyJSON = ` {"keytype":"ecdsa","keyval":{"public":"MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw==","private":"MHcCAQEEIDqtcdzU7H3AbIPSQaxHl9+xYECt7NpK7B1+6ep5cv9CoAoGCCqGSM49AwEHoUQDQgAEgl3rzMPMEKhS1k/AX16MM4PdidpjJr+z4pj0Td+30QnpbOIARgpyR1PiFztU8BZlqG3cUazvFclr2q/xHvfrqw=="}}` -func createTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) { +func simpleTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) { mux := http.NewServeMux() // TUF will request /v2/docker.com/notary/_trust/tuf/timestamp.key // Return a canned timestamp.key @@ -43,6 +43,41 @@ func createTestServer(t *testing.T) (*httptest.Server, *http.ServeMux) { return ts, mux } +func fullTestServer(t *testing.T) *httptest.Server { + // Set up server + ctx := context.WithValue( + context.Background(), "metaStore", storage.NewMemStorage()) + + // Do not pass one of the const KeyAlgorithms here as the value! Passing a + // string is in itself good test that we are handling it correctly as we + // will be receiving a string from the configuration. + ctx = context.WithValue(ctx, "keyAlgorithm", "ecdsa") + + // Eat the logs instead of spewing them out + var b bytes.Buffer + l := logrus.New() + l.Out = &b + ctx = ctxu.WithLogger(ctx, logrus.NewEntry(l)) + + cryptoService := cryptoservice.NewCryptoService( + "", trustmanager.NewKeyMemoryStore(passphraseRetriever)) + return httptest.NewServer(server.RootHandler(nil, ctx, cryptoService)) +} + +func initializeRepo(t *testing.T, rootType, tempBaseDir, gun, url string) (*NotaryRepository, string) { + repo, err := NewNotaryRepository( + tempBaseDir, gun, url, http.DefaultTransport, passphraseRetriever) + assert.NoError(t, err, "error creating repo: %s", err) + + rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType) + assert.NoError(t, err, "error generating root key: %s", err) + + err = repo.Initialize(rootKeyID) + assert.NoError(t, err, "error creating repository: %s", err) + + return repo, rootKeyID +} + // TestInitRepo runs through the process of initializing a repository and makes // sure the repository looks correct on disk. // We test this with both an RSA and ECDSA root key @@ -61,17 +96,10 @@ func testInitRepo(t *testing.T, rootType string) { assert.NoError(t, err, "failed to create a temporary directory: %s", err) - ts, _ := createTestServer(t) + ts, _ := simpleTestServer(t) defer ts.Close() - repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever) - assert.NoError(t, err, "error creating repo: %s", err) - - rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType) - assert.NoError(t, err, "error generating root key: %s", err) - - err = repo.Initialize(rootKeyID) - assert.NoError(t, err, "error creating repository: %s", err) + repo, rootKeyID := initializeRepo(t, rootType, tempBaseDir, gun, ts.URL) // Inspect contents of the temporary directory expectedDirs := []string{ @@ -165,19 +193,32 @@ func testInitRepo(t *testing.T, rootType string) { } } -// TestAddListTarget adds a target to the repo and confirms that the changelist -// is updated correctly. Then it calls ListTargets and checks the return value. -// Using ListTargets involves serving signed metadata files over the test's -// internal HTTP server. +// TestAddTarget adds a target to the repo and confirms that the changelist +// is updated correctly. // We test this with both an RSA and ECDSA root key -func TestAddListTarget(t *testing.T) { - testAddListTarget(t, data.ECDSAKey) +func TestAddTarget(t *testing.T) { + testAddTarget(t, data.ECDSAKey) if !testing.Short() { - testAddListTarget(t, data.RSAKey) + testAddTarget(t, data.RSAKey) } } -func testAddListTarget(t *testing.T, rootType string) { +func addTarget(t *testing.T, repo *NotaryRepository, targetName, targetFile string) *Target { + target, err := NewTarget(targetName, targetFile) + assert.NoError(t, err, "error creating target") + err = repo.AddTarget(target) + assert.NoError(t, err, "error adding target") + return target +} + +// calls GetChangelist and gets the actual changes out +func getChanges(t *testing.T, repo *NotaryRepository) []changelist.Change { + changeList, err := repo.GetChangelist() + assert.NoError(t, err) + return changeList.List() +} + +func testAddTarget(t *testing.T, rootType string) { // Temporary directory where test files will be created tempBaseDir, err := ioutil.TempDir("", "notary-test-") defer os.RemoveAll(tempBaseDir) @@ -186,151 +227,172 @@ func testAddListTarget(t *testing.T, rootType string) { gun := "docker.com/notary" - ts, mux := createTestServer(t) + ts, _ := simpleTestServer(t) defer ts.Close() - repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever) - assert.NoError(t, err, "error creating repository: %s", err) + repo, _ := initializeRepo(t, rootType, tempBaseDir, gun, ts.URL) - rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType) - assert.NoError(t, err, "error generating root key: %s", err) - - err = repo.Initialize(rootKeyID) + // tests need to manually boostrap timestamp as client doesn't generate it + err = repo.tufRepo.InitTimestamp() assert.NoError(t, err, "error creating repository: %s", err) + assert.Len(t, getChanges(t, repo), 0, "should start with zero changes") + + // Add fixtures/intermediate-ca.crt as a target. There's no particular + // reason for using this file except that it happens to be available as + // a fixture. + addTarget(t, repo, "latest", "../fixtures/intermediate-ca.crt") + changes := getChanges(t, repo) + assert.Len(t, changes, 1, "wrong number of changes files found") + + for _, c := range changes { // there is only one + assert.EqualValues(t, changelist.ActionCreate, c.Action()) + assert.Equal(t, "targets", c.Scope()) + assert.Equal(t, "target", c.Type()) + assert.Equal(t, "latest", c.Path()) + assert.NotEmpty(t, c.Content()) + } + + // Create a second target + addTarget(t, repo, "current", "../fixtures/intermediate-ca.crt") + changes = getChanges(t, repo) + assert.Len(t, changes, 2, "wrong number of changelist files found") + + newFileFound := false + for _, c := range changes { + if c.Path() != "latest" { + assert.EqualValues(t, changelist.ActionCreate, c.Action()) + assert.Equal(t, "targets", c.Scope()) + assert.Equal(t, "target", c.Type()) + assert.Equal(t, "current", c.Path()) + assert.NotEmpty(t, c.Content()) + + newFileFound = true + } + } + assert.True(t, newFileFound, "second changelist file not found") +} + +// TestListTarget fakes serving signed metadata files over the test's +// internal HTTP server to ensure that ListTargets returns the correct number +// of listed targets. +// We test this with both an RSA and ECDSA root key +func TestListTarget(t *testing.T) { + testListEmptyTargets(t, data.ECDSAKey) + testListTarget(t, data.ECDSAKey) + if !testing.Short() { + testListEmptyTargets(t, data.RSAKey) + testListTarget(t, data.RSAKey) + } +} + +func testListEmptyTargets(t *testing.T, rootType string) { + // Temporary directory where test files will be created + tempBaseDir, err := ioutil.TempDir("", "notary-test-") + defer os.RemoveAll(tempBaseDir) + + assert.NoError(t, err, "failed to create a temporary directory: %s", err) + + gun := "docker.com/notary" + + ts := fullTestServer(t) + defer ts.Close() + + repo, _ := initializeRepo(t, rootType, tempBaseDir, gun, ts.URL) // tests need to manually boostrap timestamp as client doesn't generate it err = repo.tufRepo.InitTimestamp() assert.NoError(t, err, "error creating repository: %s", err) - // Add fixtures/intermediate-ca.crt as a target. There's no particular reason - // for using this file except that it happens to be available as - // a fixture. - latestTarget, err := NewTarget("latest", "../fixtures/intermediate-ca.crt") - assert.NoError(t, err, "error creating target") - err = repo.AddTarget(latestTarget) - assert.NoError(t, err, "error adding target") + _, err = repo.ListTargets() + assert.Error(t, err) // no trust data +} - // Look for the changelist file - changelistDirPath := filepath.Join(tempBaseDir, "tuf", filepath.FromSlash(gun), "changelist") +// reads data from the repository in order to fake data being served via +// the ServeMux. +func fakeServerData(t *testing.T, repo *NotaryRepository, mux *http.ServeMux) { + tempKey, err := data.UnmarshalPrivateKey([]byte(timestampECDSAKeyJSON)) + assert.NoError(t, err) - changelistDir, err := os.Open(changelistDirPath) - assert.NoError(t, err, "could not open changelist directory") + savedTUFRepo := repo.tufRepo // in case this is overwritten - fileInfos, err := changelistDir.Readdir(0) - assert.NoError(t, err, "could not read changelist directory") + repo.KeyStoreManager.KeyStore.AddKey( + filepath.Join(filepath.FromSlash(repo.gun), tempKey.ID()), + "nonroot", tempKey) - // Should only be one file in the directory - assert.Len(t, fileInfos, 1, "wrong number of changelist files found") + rootJSONFile := filepath.Join(repo.baseDir, "tuf", + filepath.FromSlash(repo.gun), "metadata", "root.json") + rootFileBytes, err := ioutil.ReadFile(rootJSONFile) - clName := fileInfos[0].Name() - raw, err := ioutil.ReadFile(filepath.Join(changelistDirPath, clName)) - assert.NoError(t, err, "could not read changelist file %s", clName) + signedTargets, err := savedTUFRepo.SignTargets( + "targets", data.DefaultExpires("targets")) + assert.NoError(t, err) - c := &changelist.TufChange{} - err = json.Unmarshal(raw, c) - assert.NoError(t, err, "could not unmarshal changelist file %s", clName) + signedSnapshot, err := savedTUFRepo.SignSnapshot( + data.DefaultExpires("snapshot")) + assert.NoError(t, err) - assert.EqualValues(t, changelist.ActionCreate, c.Actn) - assert.Equal(t, "targets", c.Role) - assert.Equal(t, "target", c.ChangeType) - assert.Equal(t, "latest", c.ChangePath) - assert.NotEmpty(t, c.Data) + signedTimestamp, err := savedTUFRepo.SignTimestamp( + data.DefaultExpires("timestamp")) + assert.NoError(t, err) - changelistDir.Close() + mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/root.json", + func(w http.ResponseWriter, r *http.Request) { + assert.NoError(t, err) + fmt.Fprint(w, string(rootFileBytes)) + }) - // Create a second target - currentTarget, err := NewTarget("current", "../fixtures/intermediate-ca.crt") - assert.NoError(t, err, "error creating target") - err = repo.AddTarget(currentTarget) - assert.NoError(t, err, "error adding target") + mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/timestamp.json", + func(w http.ResponseWriter, r *http.Request) { + timestampJSON, _ := json.Marshal(signedTimestamp) + fmt.Fprint(w, string(timestampJSON)) + }) - changelistDir, err = os.Open(changelistDirPath) - assert.NoError(t, err, "could not open changelist directory") + mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/snapshot.json", + func(w http.ResponseWriter, r *http.Request) { + snapshotJSON, _ := json.Marshal(signedSnapshot) + fmt.Fprint(w, string(snapshotJSON)) + }) - // There should now be a second file in the directory - fileInfos, err = changelistDir.Readdir(0) - assert.NoError(t, err, "could not read changelist directory") + mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/targets.json", + func(w http.ResponseWriter, r *http.Request) { + targetsJSON, _ := json.Marshal(signedTargets) + fmt.Fprint(w, string(targetsJSON)) + }) +} - assert.Len(t, fileInfos, 2, "wrong number of changelist files found") +func testListTarget(t *testing.T, rootType string) { + // Temporary directory where test files will be created + tempBaseDir, err := ioutil.TempDir("", "notary-test-") + defer os.RemoveAll(tempBaseDir) - newFileFound := false - for _, fileInfo := range fileInfos { - if fileInfo.Name() != clName { - clName2 := fileInfo.Name() - raw, err := ioutil.ReadFile(filepath.Join(changelistDirPath, clName2)) - assert.NoError(t, err, "could not read changelist file %s", clName2) + assert.NoError(t, err, "failed to create a temporary directory: %s", err) - c := &changelist.TufChange{} - err = json.Unmarshal(raw, c) - assert.NoError(t, err, "could not unmarshal changelist file %s", clName2) + gun := "docker.com/notary" - assert.EqualValues(t, changelist.ActionCreate, c.Actn) - assert.Equal(t, "targets", c.Role) - assert.Equal(t, "target", c.ChangeType) - assert.Equal(t, "current", c.ChangePath) - assert.NotEmpty(t, c.Data) + ts, mux := simpleTestServer(t) + defer ts.Close() - newFileFound = true - break - } - } + repo, _ := initializeRepo(t, rootType, tempBaseDir, gun, ts.URL) - assert.True(t, newFileFound, "second changelist file not found") + // tests need to manually boostrap timestamp as client doesn't generate it + err = repo.tufRepo.InitTimestamp() + assert.NoError(t, err, "error creating repository: %s", err) - changelistDir.Close() - - // Now test ListTargets. In preparation, we need to expose some signed - // metadata files on the internal HTTP server. + latestTarget := addTarget(t, repo, "latest", "../fixtures/intermediate-ca.crt") + currentTarget := addTarget(t, repo, "current", "../fixtures/intermediate-ca.crt") // Apply the changelist. Normally, this would be done by Publish // load the changelist for this repo - cl, err := changelist.NewFileChangelist(filepath.Join(tempBaseDir, "tuf", filepath.FromSlash(gun), "changelist")) + cl, err := changelist.NewFileChangelist( + filepath.Join(tempBaseDir, "tuf", filepath.FromSlash(gun), "changelist")) assert.NoError(t, err, "could not open changelist") // apply the changelist to the repo err = applyChangelist(repo.tufRepo, cl) assert.NoError(t, err, "could not apply changelist") - tempKey, err := data.UnmarshalPrivateKey([]byte(timestampECDSAKeyJSON)) - assert.NoError(t, err) - - repo.KeyStoreManager.KeyStore.AddKey(filepath.Join(filepath.FromSlash(gun), tempKey.ID()), "nonroot", tempKey) - - // Because ListTargets will clear this - savedTUFRepo := repo.tufRepo - - rootJSONFile := filepath.Join(tempBaseDir, "tuf", filepath.FromSlash(gun), "metadata", "root.json") - rootFileBytes, err := ioutil.ReadFile(rootJSONFile) - - signedTargets, err := savedTUFRepo.SignTargets("targets", data.DefaultExpires("targets")) - assert.NoError(t, err) - - signedSnapshot, err := savedTUFRepo.SignSnapshot(data.DefaultExpires("snapshot")) - assert.NoError(t, err) - - signedTimestamp, err := savedTUFRepo.SignTimestamp(data.DefaultExpires("timestamp")) - assert.NoError(t, err) - - mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/root.json", func(w http.ResponseWriter, r *http.Request) { - assert.NoError(t, err) - fmt.Fprint(w, string(rootFileBytes)) - }) - - mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/timestamp.json", func(w http.ResponseWriter, r *http.Request) { - timestampJSON, _ := json.Marshal(signedTimestamp) - fmt.Fprint(w, string(timestampJSON)) - }) - - mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/snapshot.json", func(w http.ResponseWriter, r *http.Request) { - snapshotJSON, _ := json.Marshal(signedSnapshot) - fmt.Fprint(w, string(snapshotJSON)) - }) - - mux.HandleFunc("/v2/docker.com/notary/_trust/tuf/targets.json", func(w http.ResponseWriter, r *http.Request) { - targetsJSON, _ := json.Marshal(signedTargets) - fmt.Fprint(w, string(targetsJSON)) - }) + fakeServerData(t, repo, mux) targets, err := repo.ListTargets() assert.NoError(t, err) @@ -376,17 +438,10 @@ func testValidateRootKey(t *testing.T, rootType string) { gun := "docker.com/notary" - ts, _ := createTestServer(t) + ts, _ := simpleTestServer(t) defer ts.Close() - repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever) - assert.NoError(t, err, "error creating repository: %s", err) - - rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType) - assert.NoError(t, err, "error generating root key: %s", err) - - err = repo.Initialize(rootKeyID) - assert.NoError(t, err, "error creating repository: %s", err) + initializeRepo(t, rootType, tempBaseDir, gun, ts.URL) rootJSONFile := filepath.Join(tempBaseDir, "tuf", filepath.FromSlash(gun), "metadata", "root.json") @@ -419,6 +474,57 @@ func testValidateRootKey(t *testing.T, rootType string) { } } +// TestGetChangelist ensures that the changelist returned matches the changes +// added. +// We test this with both an RSA and ECDSA root key +func TestGetChangelist(t *testing.T) { + testGetChangelist(t, data.ECDSAKey) + if !testing.Short() { + testGetChangelist(t, data.RSAKey) + } +} + +func testGetChangelist(t *testing.T, rootType string) { + // Temporary directory where test files will be created + tempBaseDir, err := ioutil.TempDir("", "notary-test-") + defer os.RemoveAll(tempBaseDir) + + assert.NoError(t, err, "failed to create a temporary directory: %s", err) + + gun := "docker.com/notary" + ts, _ := simpleTestServer(t) + + repo, _ := initializeRepo(t, rootType, tempBaseDir, gun, ts.URL) + assert.Len(t, getChanges(t, repo), 0, "No changes should be in changelist yet") + + // Create 2 targets + addTarget(t, repo, "latest", "../fixtures/intermediate-ca.crt") + addTarget(t, repo, "current", "../fixtures/intermediate-ca.crt") + + // Test loading changelist + chgs := getChanges(t, repo) + assert.Len(t, chgs, 2, "Wrong number of changes returned from changelist") + + changes := make(map[string]changelist.Change) + for _, ch := range chgs { + 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()) +} + // TestPublish creates a repo, instantiates a notary server, and publishes // the repo to the server. // We test this with both an RSA and ECDSA root key @@ -437,148 +543,19 @@ func testPublish(t *testing.T, rootType string) { assert.NoError(t, err, "failed to create a temporary directory: %s", err) gun := "docker.com/notary" + ts := fullTestServer(t) - // Set up server - ctx := context.WithValue(context.Background(), "metaStore", storage.NewMemStorage()) + repo, _ := initializeRepo(t, rootType, tempBaseDir, gun, ts.URL) - // Do not pass one of the const KeyAlgorithms here as the value! Passing a - // string is in itself good test that we are handling it correctly as we will - // be receiving a string from the configuration. - ctx = context.WithValue(ctx, "keyAlgorithm", "ecdsa") - - hand := utils.RootHandlerFactory(nil, ctx, - cryptoservice.NewCryptoService("", trustmanager.NewKeyMemoryStore(passphraseRetriever))) - - r := mux.NewRouter() - r.Methods("POST").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/").Handler(hand(handlers.AtomicUpdateHandler, "push", "pull")) - r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/{tufRole:(root|targets|snapshot)}.json").Handler(hand(handlers.GetHandler, "pull")) - r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/timestamp.json").Handler(hand(handlers.GetTimestampHandler, "pull")) - r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/timestamp.key").Handler(hand(handlers.GetTimestampKeyHandler, "push", "pull")) - //r.Methods("POST").Path("/v2/{imageName:" + server.RepositoryNameRegexp + "}/_trust/tuf/{tufRole:(root|targets|timestamp|snapshot)}.json").Handler(hand(handlers.UpdateHandler, "push", "pull")) - r.Methods("DELETE").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/").Handler(hand(handlers.DeleteHandler, "push", "pull")) - - ts := httptest.NewServer(r) - - repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever) - assert.NoError(t, err, "error creating repository: %s", err) - - rootKeyID, err := repo.KeyStoreManager.GenRootKey(rootType) - assert.NoError(t, err, "error generating root key: %s", err) - - err = repo.Initialize(rootKeyID) - assert.NoError(t, err, "error creating repository: %s", err) - - // Add fixtures/intermediate-ca.crt as a target. There's no particular reason - // for using this file except that it happens to be available as - // a fixture. - latestTarget, err := NewTarget("latest", "../fixtures/intermediate-ca.crt") - assert.NoError(t, err, "error creating target") - err = repo.AddTarget(latestTarget) - assert.NoError(t, err, "error adding target") - - // Look for the changelist file - changelistDirPath := filepath.Join(tempBaseDir, "tuf", filepath.FromSlash(gun), "changelist") - - changelistDir, err := os.Open(changelistDirPath) - assert.NoError(t, err, "could not open changelist directory") - - fileInfos, err := changelistDir.Readdir(0) - assert.NoError(t, err, "could not read changelist directory") - - // Should only be one file in the directory - assert.Len(t, fileInfos, 1, "wrong number of changelist files found") - - clName := fileInfos[0].Name() - raw, err := ioutil.ReadFile(filepath.Join(changelistDirPath, clName)) - assert.NoError(t, err, "could not read changelist file %s", clName) - - c := &changelist.TufChange{} - err = json.Unmarshal(raw, c) - assert.NoError(t, err, "could not unmarshal changelist file %s", clName) - - assert.EqualValues(t, changelist.ActionCreate, c.Actn) - assert.Equal(t, "targets", c.Role) - assert.Equal(t, "target", c.ChangeType) - assert.Equal(t, "latest", c.ChangePath) - assert.NotEmpty(t, c.Data) - - changelistDir.Close() - - // Create a second target - currentTarget, err := NewTarget("current", "../fixtures/intermediate-ca.crt") - assert.NoError(t, err, "error creating target") - err = repo.AddTarget(currentTarget) - assert.NoError(t, err, "error adding target") - - changelistDir, err = os.Open(changelistDirPath) - assert.NoError(t, err, "could not open changelist directory") - - // There should now be a second file in the directory - fileInfos, err = changelistDir.Readdir(0) - assert.NoError(t, err, "could not read changelist directory") - - assert.Len(t, fileInfos, 2, "wrong number of changelist files found") - - newFileFound := false - for _, fileInfo := range fileInfos { - if fileInfo.Name() != clName { - clName2 := fileInfo.Name() - raw, err := ioutil.ReadFile(filepath.Join(changelistDirPath, clName2)) - assert.NoError(t, err, "could not read changelist file %s", clName2) - - c := &changelist.TufChange{} - err = json.Unmarshal(raw, c) - assert.NoError(t, err, "could not unmarshal changelist file %s", clName2) - - assert.EqualValues(t, changelist.ActionCreate, c.Actn) - assert.Equal(t, "targets", c.Role) - assert.Equal(t, "target", c.ChangeType) - assert.Equal(t, "current", c.ChangePath) - assert.NotEmpty(t, c.Data) - - newFileFound = true - break - } - } - - assert.True(t, newFileFound, "second changelist file not found") - - 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()) + // Create 2 targets + latestTarget := addTarget(t, repo, "latest", "../fixtures/intermediate-ca.crt") + currentTarget := addTarget(t, repo, "current", "../fixtures/intermediate-ca.crt") + assert.Len(t, getChanges(t, repo), 2, "wrong number of changelist files found") // Now test Publish err = repo.Publish() assert.NoError(t, err) - - changelistDir, err = os.Open(changelistDirPath) - assert.NoError(t, err, "could not open changelist directory") - fileInfos, err = changelistDir.Readdir(0) - assert.NoError(t, err, "could not read changelist directory") - // Should only be one file in the directory - assert.Len(t, fileInfos, 0, "wrong number of changelist files found") + assert.Len(t, getChanges(t, repo), 0, "wrong number of changelist files found") // Create a new repo and pull from the server tempBaseDir2, err := ioutil.TempDir("", "notary-test-") @@ -624,45 +601,13 @@ func TestRotate(t *testing.T) { gun := "docker.com/notary" - // Set up server - ctx := context.WithValue(context.Background(), "metaStore", storage.NewMemStorage()) + ts := fullTestServer(t) - // Do not pass one of the const KeyAlgorithms here as the value! Passing a - // string is in itself good test that we are handling it correctly as we will - // be receiving a string from the configuration. - ctx = context.WithValue(ctx, "keyAlgorithm", "ecdsa") + repo, _ := initializeRepo(t, data.ECDSAKey, tempBaseDir, gun, ts.URL) - hand := utils.RootHandlerFactory(nil, ctx, - cryptoservice.NewCryptoService("", trustmanager.NewKeyMemoryStore(passphraseRetriever))) - - r := mux.NewRouter() - r.Methods("POST").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/").Handler(hand(handlers.AtomicUpdateHandler, "push", "pull")) - r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/{tufRole:(root|targets|snapshot)}.json").Handler(hand(handlers.GetHandler, "pull")) - r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/timestamp.json").Handler(hand(handlers.GetTimestampHandler, "pull")) - r.Methods("GET").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/timestamp.key").Handler(hand(handlers.GetTimestampKeyHandler, "push", "pull")) - //r.Methods("POST").Path("/v2/{imageName:" + server.RepositoryNameRegexp + "}/_trust/tuf/{tufRole:(root|targets|timestamp|snapshot)}.json").Handler(hand(handlers.UpdateHandler, "push", "pull")) - r.Methods("DELETE").Path("/v2/{imageName:" + v2.RepositoryNameRegexp.String() + "}/_trust/tuf/").Handler(hand(handlers.DeleteHandler, "push", "pull")) - - ts := httptest.NewServer(r) - - repo, err := NewNotaryRepository(tempBaseDir, gun, ts.URL, http.DefaultTransport, passphraseRetriever) - assert.NoError(t, err, "error creating repository: %s", err) - - rootKeyID, err := repo.KeyStoreManager.GenRootKey(data.ECDSAKey) - assert.NoError(t, err, "error generating root key: %s", err) - - err = repo.Initialize(rootKeyID) - assert.NoError(t, err, "error creating repository: %s", err) - - // Add fixtures/intermediate-ca.crt as a target. There's no particular reason - // for using this file except that it happens to be available as - // a fixture. // Adding a target will allow us to confirm the repository is still valid after // rotating the keys. - latestTarget, err := NewTarget("latest", "../fixtures/intermediate-ca.crt") - assert.NoError(t, err, "error creating target") - err = repo.AddTarget(latestTarget) - assert.NoError(t, err, "error adding target") + addTarget(t, repo, "latest", "../fixtures/intermediate-ca.crt") // Publish err = repo.Publish() @@ -694,12 +639,6 @@ func TestRotate(t *testing.T) { assert.NotEqual(t, snapshotKeyIDs[0], newSnapshotKeyIDs[0]) // Confirm changelist dir empty after publishing changes - // Look for the changelist file - changelistDirPath := filepath.Join(tempBaseDir, "tuf", filepath.FromSlash(gun), "changelist") - changelistDir, err := os.Open(changelistDirPath) - assert.NoError(t, err, "could not open changelist directory") - fileInfos, err := changelistDir.Readdir(0) - assert.NoError(t, err, "could not read changelist directory") - // Should only be one file in the directory - assert.Len(t, fileInfos, 0, "wrong number of changelist files found") + changes := getChanges(t, repo) + assert.Len(t, changes, 0, "wrong number of changelist files found") }