1 package client
2
3 import (
4 "bytes"
5 "fmt"
6 "io"
7 "net"
8 "net/http"
9 "os"
10 "path/filepath"
11
12 "github.com/theupdateframework/go-tuf/util"
13 . "gopkg.in/check.v1"
14
15 goTufGenerator "github.com/theupdateframework/go-tuf/client/testdata/go-tuf/generator"
16 )
17
18 type InteropSuite struct{}
19
20 var _ = Suite(&InteropSuite{})
21
22 func (InteropSuite) TestGoClientIdentityConsistentSnapshotFalse(c *C) {
23 checkGoIdentity(c, false)
24 }
25
26 func (InteropSuite) TestGoClientIdentityConsistentSnapshotTrue(c *C) {
27 checkGoIdentity(c, true)
28 }
29
30 func checkGoIdentity(c *C, consistentSnapshot bool) {
31 cwd, err := os.Getwd()
32 c.Assert(err, IsNil)
33 testDataDir := filepath.Join(cwd, "testdata")
34
35 tempDir, err := os.MkdirTemp("", "")
36 c.Assert(err, IsNil)
37 defer os.RemoveAll(tempDir)
38
39
40 goTufGenerator.Generate(tempDir, filepath.Join(testDataDir, "keys.json"), consistentSnapshot)
41 hashes := computeHashes(c, tempDir)
42
43 snapshotDir := filepath.Join(testDataDir, "go-tuf", fmt.Sprintf("consistent-snapshot-%t", consistentSnapshot))
44 snapshotHashes := computeHashes(c, snapshotDir)
45
46 c.Assert(hashes, DeepEquals, snapshotHashes, Commentf("metadata out of date, regenerate by running client/testdata/go-tuf/regenerate-metadata.sh"))
47 }
48
49 func computeHashes(c *C, dir string) map[string]string {
50 hashes := make(map[string]string)
51
52 err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
53 if err != nil {
54 return err
55 }
56
57 if info.IsDir() {
58 return nil
59 }
60
61 bytes, err := os.ReadFile(path)
62 if err != nil {
63 return err
64 }
65
66 path, err = filepath.Rel(dir, path)
67 if err != nil {
68 return err
69 }
70 hashes[path] = string(bytes)
71
72 return nil
73 })
74 c.Assert(err, IsNil)
75
76 return hashes
77 }
78
79 func (InteropSuite) TestGoClientCompatibility(c *C) {
80 names := []string{
81 "go-tuf",
82 "go-tuf-transition-M3",
83 "go-tuf-transition-M4",
84 }
85 options := &HTTPRemoteOptions{MetadataPath: "", TargetsPath: "targets"}
86
87 for _, name := range names {
88 for _, consistentSnapshot := range []bool{false, true} {
89 t := newTestCase(c, name, consistentSnapshot, options)
90 t.run(c)
91 }
92 }
93 }
94
95 type testCase struct {
96 name string
97 consistentSnapshot bool
98 options *HTTPRemoteOptions
99 local LocalStore
100 targets map[string][]byte
101 testDir string
102 testSteps []string
103 }
104
105 func newTestCase(c *C, name string, consistentSnapshot bool, options *HTTPRemoteOptions) testCase {
106 cwd, err := os.Getwd()
107 c.Assert(err, IsNil)
108 testDir := filepath.Join(cwd, "testdata", name, fmt.Sprintf("consistent-snapshot-%t", consistentSnapshot))
109
110 dirEntries, err := os.ReadDir(testDir)
111 c.Assert(err, IsNil)
112 c.Assert(dirEntries, Not(HasLen), 0)
113
114 testSteps := []string{}
115 for _, dirEntry := range dirEntries {
116 if dirEntry.IsDir() {
117 testSteps = append(testSteps, dirEntry.Name())
118 }
119 }
120
121 return testCase{
122 name: name,
123 consistentSnapshot: consistentSnapshot,
124 options: options,
125 local: MemoryLocalStore(),
126 targets: make(map[string][]byte),
127 testDir: testDir,
128 testSteps: testSteps,
129 }
130 }
131
132 func (t *testCase) run(c *C) {
133 c.Logf("test case: %s consistent-snapshot: %t", t.name, t.consistentSnapshot)
134
135 for _, stepName := range t.testSteps {
136 t.runStep(c, stepName)
137 }
138 }
139
140 func (t *testCase) runStep(c *C, stepName string) {
141 c.Logf("step: %s", stepName)
142
143 addr, cleanup := startFileServer(c, t.testDir)
144 defer cleanup()
145
146 remote, err := HTTPRemoteStore(fmt.Sprintf("http://%s/%s/repository", addr, stepName), t.options, nil)
147 c.Assert(err, IsNil)
148
149 client := NewClient(t.local, remote)
150
151 ioReader, _, err := remote.GetMeta("root.json")
152 c.Assert(err, IsNil)
153 rootJsonBytes, err := io.ReadAll(ioReader)
154 c.Assert(err, IsNil)
155 c.Assert(client.Init(rootJsonBytes), IsNil)
156
157
158 files, err := client.Update()
159 c.Assert(err, IsNil)
160 if stepName != "2" {
161
162
163 c.Assert(files, HasLen, 1)
164 } else {
165
166
167
168
169 c.Assert(files, HasLen, 3)
170 }
171 targetName := stepName
172 t.targets[targetName] = []byte(targetName)
173
174 file, ok := files[targetName]
175 if !ok {
176 c.Fatalf("expected updated targets to contain %s", targetName)
177 }
178
179 data := t.targets[targetName]
180 meta, err := util.GenerateTargetFileMeta(bytes.NewReader(data), file.HashAlgorithms()...)
181 c.Assert(err, IsNil)
182 c.Assert(util.TargetFileMetaEqual(file, meta), IsNil)
183
184
185 for name, data := range t.targets {
186 for _, prefix := range []string{"", "/"} {
187 var dest testDestination
188 c.Assert(client.Download(prefix+name, &dest), IsNil)
189 c.Assert(dest.deleted, Equals, false)
190 c.Assert(dest.String(), Equals, string(data))
191 }
192 }
193 }
194
195 func startFileServer(c *C, dir string) (string, func() error) {
196 l, err := net.Listen("tcp", "127.0.0.1:0")
197 c.Assert(err, IsNil)
198 addr := l.Addr().String()
199 go http.Serve(l, http.FileServer(http.Dir(dir)))
200 return addr, l.Close
201 }
202
View as plain text