Add test to make sure go-tuf interop data doesn't change
This is a test that makes sure that the metadata produced by go-tuf
cannot change out from under it without this metadata from being
regenerated. It does this by turning the generator script into a
library, and then doing a diff between the snapshotted metadata
and the one we just generated.
Change-Id: I3449bc000f77be6495e3198a786693eef40e446c
diff --git a/client/interop_test.go b/client/interop_test.go
index e64ec7a..13f0d8c 100644
--- a/client/interop_test.go
+++ b/client/interop_test.go
@@ -13,12 +13,67 @@
"github.com/flynn/go-tuf/data"
"github.com/flynn/go-tuf/util"
. "gopkg.in/check.v1"
+
+ goTufGenerator "github.com/flynn/go-tuf/client/testdata/go-tuf/generator"
)
type InteropSuite struct{}
var _ = Suite(&InteropSuite{})
+func (InteropSuite) TestGoClientIdentityConsistentSnapshotFalse(c *C) {
+ checkGoIdentity(c, false)
+}
+
+func (InteropSuite) TestGoClientIdentityConsistentSnapshotTrue(c *C) {
+ checkGoIdentity(c, true)
+}
+
+func checkGoIdentity(c *C, consistentSnapshot bool) {
+ cwd, err := os.Getwd()
+ c.Assert(err, IsNil)
+ testDataDir := filepath.Join(cwd, "testdata")
+
+ tempDir, err := ioutil.TempDir("", "")
+ c.Assert(err, IsNil)
+ defer os.RemoveAll(tempDir)
+
+ // Generate the metadata and compute hashes for all the files.
+ goTufGenerator.Generate(tempDir, filepath.Join(testDataDir, "keys.json"), consistentSnapshot)
+ hashes := computeHashes(c, tempDir)
+
+ snapshotDir := filepath.Join(testDataDir, "go-tuf", fmt.Sprintf("consistent-snapshot-%t", consistentSnapshot))
+ snapshotHashes := computeHashes(c, snapshotDir)
+
+ c.Assert(hashes, DeepEquals, snapshotHashes, Commentf("metadata out of date, regenerate by running client/testdata/go-tuf/regenerate-metadata.sh"))
+}
+
+func computeHashes(c *C, dir string) map[string]string {
+ hashes := make(map[string]string)
+
+ err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+ if info.IsDir() {
+ return nil
+ }
+
+ bytes, err := ioutil.ReadFile(path)
+ if err != nil {
+ return err
+ }
+
+ path, err = filepath.Rel(dir, path)
+ if err != nil {
+ return err
+ }
+ hashes[path] = string(bytes)
+
+ return nil
+ })
+ c.Assert(err, IsNil)
+
+ return hashes
+}
+
func (InteropSuite) TestGoClientCompatibility(c *C) {
names := []string{
"go-tuf",
diff --git a/client/testdata/go-tuf/generate.go b/client/testdata/go-tuf/generate.go
index 35562a5..a30e69a 100644
--- a/client/testdata/go-tuf/generate.go
+++ b/client/testdata/go-tuf/generate.go
@@ -1,146 +1,19 @@
package main
import (
- "encoding/json"
- "fmt"
- "io/ioutil"
"log"
"os"
- "os/exec"
"path/filepath"
- "time"
- tuf "github.com/flynn/go-tuf"
- "github.com/flynn/go-tuf/sign"
+ "github.com/flynn/go-tuf/client/testdata/go-tuf/generator"
)
-var expirationDate = time.Date(2100, time.January, 1, 0, 0, 0, 0, time.UTC)
-
-type persistedKeys struct {
- Encrypted bool `json:"encrypted"`
- Data []*sign.PrivateKey `json:"data"`
-}
-
-func assertNotNil(err error) {
- if err != nil {
- panic(fmt.Sprintf("assertion failed: %s", err))
- }
-}
-
-func copyRepo(src string, dst string) {
- cmd := exec.Command("cp", "-r", src, dst)
- assertNotNil(cmd.Run())
-}
-
-func newRepo(dir string) *tuf.Repo {
- repo, err := tuf.NewRepoIndent(tuf.FileSystemStore(dir, nil), "", "\t")
- assertNotNil(err)
-
- return repo
-}
-
-func commit(dir string, repo *tuf.Repo) {
- assertNotNil(repo.SnapshotWithExpires(tuf.CompressionTypeNone, expirationDate))
- assertNotNil(repo.TimestampWithExpires(expirationDate))
- assertNotNil(repo.Commit())
-
- // Remove the keys directory to make sure we don't accidentally use a key.
- assertNotNil(os.RemoveAll(filepath.Join(dir, "keys")))
-}
-
-func addKeys(repo *tuf.Repo, roleKeys map[string][]*sign.PrivateKey) {
- for role, keys := range roleKeys {
- for _, key := range keys {
- assertNotNil(repo.AddPrivateKeyWithExpires(role, key, expirationDate))
- }
- }
-}
-
-func addTargets(repo *tuf.Repo, dir string, files map[string][]byte) {
- paths := []string{}
- for file, data := range files {
- path := filepath.Join(dir, "staged", "targets", file)
- assertNotNil(os.MkdirAll(filepath.Dir(path), 0755))
- assertNotNil(ioutil.WriteFile(path, data, 0644))
- paths = append(paths, file)
- }
- assertNotNil(repo.AddTargetsWithExpires(paths, nil, expirationDate))
-}
-
-func revokeKeys(repo *tuf.Repo, role string, keys []*sign.PrivateKey) {
- for _, key := range keys {
- assertNotNil(repo.RevokeKeyWithExpires(role, key.PublicData().IDs()[0], expirationDate))
- }
-}
-
-func generateRepos(dir string, consistentSnapshot bool) {
- f, err := os.Open("../keys.json")
- assertNotNil(err)
-
- var roleKeys map[string][][]*sign.PrivateKey
- assertNotNil(json.NewDecoder(f).Decode(&roleKeys))
-
- // Collect all the initial keys we'll use when creating repositories.
- // We'll modify this to reflect rotated keys.
- keys := map[string][]*sign.PrivateKey{
- "root": roleKeys["root"][0],
- "targets": roleKeys["targets"][0],
- "snapshot": roleKeys["snapshot"][0],
- "timestamp": roleKeys["timestamp"][0],
- }
-
- // Create the initial repo.
- dir0 := filepath.Join(dir, "0")
- repo0 := newRepo(dir0)
- repo0.Init(consistentSnapshot)
- addKeys(repo0, keys)
- addTargets(repo0, dir0, map[string][]byte{"0": []byte("0")})
- commit(dir0, repo0)
-
- // Rotate all the keys to make sure that works.
- oldDir := dir0
- i := 1
- for _, role := range []string{"root", "targets", "snapshot", "timestamp"} {
- // Setup the repo.
- stepName := fmt.Sprintf("%d", i)
- d := filepath.Join(dir, stepName)
- copyRepo(oldDir, d)
- repo := newRepo(d)
- addKeys(repo, keys)
-
- // Actually rotate the keys
- revokeKeys(repo, role, roleKeys[role][0])
- addKeys(repo, map[string][]*sign.PrivateKey{
- role: roleKeys[role][1],
- })
- keys[role] = roleKeys[role][1]
-
- // Add a target to make sure that works, then commit.
- addTargets(repo, d, map[string][]byte{stepName: []byte(stepName)})
- commit(d, repo)
-
- i += 1
- oldDir = d
- }
-
- // Add another target file to make sure the workflow worked.
- stepName := fmt.Sprintf("%d", i)
- d := filepath.Join(dir, stepName)
- copyRepo(oldDir, d)
- repo := newRepo(d)
- addKeys(repo, keys)
- addTargets(repo, d, map[string][]byte{stepName: []byte(stepName)})
- commit(d, repo)
-}
-
func main() {
cwd, err := os.Getwd()
- assertNotNil(err)
-
- for _, consistentSnapshot := range []bool{false, true} {
- name := fmt.Sprintf("consistent-snapshot-%t", consistentSnapshot)
- log.Printf("generating %s", name)
- generateRepos(filepath.Join(cwd, name), consistentSnapshot)
+ if err != nil {
+ log.Fatal(err)
}
+ generator.Generate(filepath.Join(cwd, "consistent-snapshot-false"), "../keys.json", false)
+ generator.Generate(filepath.Join(cwd, "consistent-snapshot-true"), "../keys.json", true)
}
diff --git a/client/testdata/go-tuf/generator/generator.go b/client/testdata/go-tuf/generator/generator.go
new file mode 100644
index 0000000..68bcec3
--- /dev/null
+++ b/client/testdata/go-tuf/generator/generator.go
@@ -0,0 +1,140 @@
+package generator
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "time"
+
+ tuf "github.com/flynn/go-tuf"
+ "github.com/flynn/go-tuf/sign"
+)
+
+var expirationDate = time.Date(2100, time.January, 1, 0, 0, 0, 0, time.UTC)
+
+type persistedKeys struct {
+ Encrypted bool `json:"encrypted"`
+ Data []*sign.PrivateKey `json:"data"`
+}
+
+func assertNotNil(err error) {
+ if err != nil {
+ panic(fmt.Sprintf("assertion failed: %s", err))
+ }
+}
+
+func copyRepo(src string, dst string) {
+ cmd := exec.Command("cp", "-r", src, dst)
+ assertNotNil(cmd.Run())
+}
+
+func newRepo(dir string) *tuf.Repo {
+ repo, err := tuf.NewRepoIndent(tuf.FileSystemStore(dir, nil), "", "\t")
+ assertNotNil(err)
+
+ return repo
+}
+
+func commit(dir string, repo *tuf.Repo) {
+ assertNotNil(repo.SnapshotWithExpires(tuf.CompressionTypeNone, expirationDate))
+ assertNotNil(repo.TimestampWithExpires(expirationDate))
+ assertNotNil(repo.Commit())
+
+ // Remove the keys directory to make sure we don't accidentally use a key.
+ assertNotNil(os.RemoveAll(filepath.Join(dir, "keys")))
+}
+
+func addKeys(repo *tuf.Repo, roleKeys map[string][]*sign.PrivateKey) {
+ for role, keys := range roleKeys {
+ for _, key := range keys {
+ assertNotNil(repo.AddPrivateKeyWithExpires(role, key, expirationDate))
+ }
+ }
+}
+
+func addTargets(repo *tuf.Repo, dir string, files map[string][]byte) {
+ paths := []string{}
+ for file, data := range files {
+ path := filepath.Join(dir, "staged", "targets", file)
+ assertNotNil(os.MkdirAll(filepath.Dir(path), 0755))
+ assertNotNil(ioutil.WriteFile(path, data, 0644))
+ paths = append(paths, file)
+ }
+ assertNotNil(repo.AddTargetsWithExpires(paths, nil, expirationDate))
+}
+
+func revokeKeys(repo *tuf.Repo, role string, keys []*sign.PrivateKey) {
+ for _, key := range keys {
+ assertNotNil(repo.RevokeKeyWithExpires(role, key.PublicData().IDs()[0], expirationDate))
+ }
+}
+
+func generateRepos(dir string, roleKeys map[string][][]*sign.PrivateKey, consistentSnapshot bool) {
+ // Collect all the initial keys we'll use when creating repositories.
+ // We'll modify this to reflect rotated keys.
+ keys := map[string][]*sign.PrivateKey{
+ "root": roleKeys["root"][0],
+ "targets": roleKeys["targets"][0],
+ "snapshot": roleKeys["snapshot"][0],
+ "timestamp": roleKeys["timestamp"][0],
+ }
+
+ // Create the initial repo.
+ dir0 := filepath.Join(dir, "0")
+ repo0 := newRepo(dir0)
+ repo0.Init(consistentSnapshot)
+ addKeys(repo0, keys)
+ addTargets(repo0, dir0, map[string][]byte{"0": []byte("0")})
+ commit(dir0, repo0)
+
+ // Rotate all the keys to make sure that works.
+ oldDir := dir0
+ i := 1
+ for _, role := range []string{"root", "targets", "snapshot", "timestamp"} {
+ // Setup the repo.
+ stepName := fmt.Sprintf("%d", i)
+ d := filepath.Join(dir, stepName)
+ copyRepo(oldDir, d)
+ repo := newRepo(d)
+ addKeys(repo, keys)
+
+ // Actually rotate the keys
+ revokeKeys(repo, role, roleKeys[role][0])
+ addKeys(repo, map[string][]*sign.PrivateKey{
+ role: roleKeys[role][1],
+ })
+ keys[role] = roleKeys[role][1]
+
+ // Add a target to make sure that works, then commit.
+ addTargets(repo, d, map[string][]byte{stepName: []byte(stepName)})
+ commit(d, repo)
+
+ i += 1
+ oldDir = d
+ }
+
+ // Add another target file to make sure the workflow worked.
+ stepName := fmt.Sprintf("%d", i)
+ d := filepath.Join(dir, stepName)
+ copyRepo(oldDir, d)
+ repo := newRepo(d)
+ addKeys(repo, keys)
+ addTargets(repo, d, map[string][]byte{stepName: []byte(stepName)})
+ commit(d, repo)
+}
+
+func Generate(dir string, keysPath string, consistentSnapshot bool) {
+ f, err := os.Open(keysPath)
+ assertNotNil(err)
+
+ var roleKeys map[string][][]*sign.PrivateKey
+ assertNotNil(json.NewDecoder(f).Decode(&roleKeys))
+
+ log.Printf("generating %s", dir)
+
+ generateRepos(dir, roleKeys, consistentSnapshot)
+}
diff --git a/client/testdata/go-tuf/regenerate-metadata.sh b/client/testdata/go-tuf/regenerate-metadata.sh
new file mode 100755
index 0000000..ea286a0
--- /dev/null
+++ b/client/testdata/go-tuf/regenerate-metadata.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+set -eu
+
+cd `dirname $0`
+
+for d in consistent-snapshot-false consistent-snapshot-true; do
+ if [[ -e $d ]]; then
+ rm -r $d
+ fi
+done
+
+go run generate.go
+go run ../tools/linkify-metadata.go