blob: 8b6a1073e438831685e2e7ca394db9b64718cb2a [file] [log] [blame]
package client
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net"
"net/http"
"os"
"os/exec"
"path/filepath"
"strings"
tuf "github.com/flynn/go-tuf"
"github.com/flynn/go-tuf/data"
"github.com/flynn/go-tuf/util"
"golang.org/x/crypto/ed25519"
. "gopkg.in/check.v1"
)
type InteropSuite struct{}
var _ = Suite(&InteropSuite{})
var pythonTargets = map[string][]byte{
"/file1.txt": []byte("file1.txt"),
"/dir/file2.txt": []byte("file2.txt"),
}
func (InteropSuite) TestGoClientPythonGenerated(c *C) {
// start file server
cwd, err := os.Getwd()
c.Assert(err, IsNil)
testDataDir := filepath.Join(cwd, "testdata")
addr, cleanup := startFileServer(c, testDataDir)
defer cleanup()
for _, dir := range []string{"with-consistent-snapshot", "without-consistent-snapshot"} {
remote, err := HTTPRemoteStore(
fmt.Sprintf("http://%s/%s/repository", addr, dir),
&HTTPRemoteOptions{MetadataPath: "metadata", TargetsPath: "targets"},
nil,
)
c.Assert(err, IsNil)
// initiate a client with the root keys
f, err := os.Open(filepath.Join("testdata", dir, "keystore", "root_key.pub"))
c.Assert(err, IsNil)
key := &data.Key{}
c.Assert(json.NewDecoder(f).Decode(key), IsNil)
c.Assert(key.Type, Equals, "ed25519")
c.Assert(key.Value.Public, HasLen, ed25519.PublicKeySize)
client := NewClient(MemoryLocalStore(), remote)
c.Assert(client.Init([]*data.Key{key}, 1), IsNil)
// check update returns the correct updated targets
files, err := client.Update()
c.Assert(err, IsNil)
c.Assert(files, HasLen, len(pythonTargets))
for name, data := range pythonTargets {
file, ok := files[name]
if !ok {
c.Fatalf("expected updated targets to contain %s", name)
}
meta, err := util.GenerateFileMeta(bytes.NewReader(data), file.HashAlgorithms()...)
c.Assert(err, IsNil)
c.Assert(util.FileMetaEqual(file, meta), IsNil)
}
// download the files and check they have the correct content
for name, data := range pythonTargets {
var dest testDestination
c.Assert(client.Download(name, &dest), IsNil)
c.Assert(dest.deleted, Equals, false)
c.Assert(dest.String(), Equals, string(data))
}
}
}
func generateRepoFS(c *C, dir string, files map[string][]byte, consistentSnapshot bool) *tuf.Repo {
repo, err := tuf.NewRepo(tuf.FileSystemStore(dir, nil))
c.Assert(err, IsNil)
if !consistentSnapshot {
c.Assert(repo.Init(false), IsNil)
}
for _, role := range []string{"root", "snapshot", "targets", "timestamp"} {
_, err := repo.GenKey(role)
c.Assert(err, IsNil)
}
for file, data := range files {
path := filepath.Join(dir, "staged", "targets", file)
c.Assert(os.MkdirAll(filepath.Dir(path), 0755), IsNil)
c.Assert(ioutil.WriteFile(path, data, 0644), IsNil)
c.Assert(repo.AddTarget(file, nil), IsNil)
}
c.Assert(repo.Snapshot(tuf.CompressionTypeNone), IsNil)
c.Assert(repo.Timestamp(), IsNil)
c.Assert(repo.Commit(), IsNil)
return repo
}
func (InteropSuite) TestPythonClientGoGenerated(c *C) {
// clone the Python client if necessary
cwd, err := os.Getwd()
c.Assert(err, IsNil)
tufDir := filepath.Join(cwd, "testdata", "tuf")
if _, err := os.Stat(tufDir); os.IsNotExist(err) {
c.Assert(exec.Command(
"git",
"clone",
"--quiet",
"--branch=v0.9.9",
"--depth=1",
"https://github.com/theupdateframework/tuf.git",
tufDir,
).Run(), IsNil)
}
tmp := c.MkDir()
files := map[string][]byte{
"foo.txt": []byte("foo"),
"bar/baz.txt": []byte("baz"),
}
// start file server
addr, cleanup := startFileServer(c, tmp)
defer cleanup()
// setup Python env
environ := os.Environ()
pythonEnv := make([]string, 0, len(environ)+1)
// remove any existing PYTHONPATH from the environment
for _, e := range environ {
if strings.HasPrefix(e, "PYTHONPATH=") {
continue
}
pythonEnv = append(pythonEnv, e)
}
pythonEnv = append(pythonEnv, "PYTHONPATH="+tufDir)
for _, consistentSnapshot := range []bool{false, true} {
// generate repository
name := fmt.Sprintf("consistent-snapshot-%t", consistentSnapshot)
dir := filepath.Join(tmp, name)
generateRepoFS(c, dir, files, consistentSnapshot)
// create initial files for Python client
clientDir := filepath.Join(dir, "client")
currDir := filepath.Join(clientDir, "metadata", "current")
prevDir := filepath.Join(clientDir, "metadata", "previous")
c.Assert(os.MkdirAll(currDir, 0755), IsNil)
c.Assert(os.MkdirAll(prevDir, 0755), IsNil)
rootJSON, err := ioutil.ReadFile(filepath.Join(dir, "repository", "root.json"))
c.Assert(err, IsNil)
c.Assert(ioutil.WriteFile(filepath.Join(currDir, "root.json"), rootJSON, 0644), IsNil)
// run Python client update
cmd := exec.Command("python", filepath.Join(cwd, "testdata", "client.py"), "--repo=http://"+addr+"/"+name)
cmd.Env = pythonEnv
cmd.Dir = clientDir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
c.Assert(cmd.Run(), IsNil)
// check the target files got downloaded
for path, expected := range files {
actual, err := ioutil.ReadFile(filepath.Join(clientDir, "targets", path))
c.Assert(err, IsNil)
c.Assert(actual, DeepEquals, expected)
}
}
}
func startFileServer(c *C, dir string) (string, func() error) {
l, err := net.Listen("tcp", "127.0.0.1:0")
c.Assert(err, IsNil)
addr := l.Addr().String()
go http.Serve(l, http.FileServer(http.Dir(dir)))
return addr, l.Close
}