Skip to content

Commit f2cff95

Browse files
author
Jussi Kukkonen
committed
MetadataBundle: Don't do any file IO
Remove file IO from MetadataBundle: * This make the bundle API very clear and easy to understand * This means caller must now read from and persist data to disk but initial prototypes suggest this won't make Updater too complex This change is something we can still back out from if it turns out to be the wrong decision: the file-persisting MetadataBundle has been tested and works fine. Signed-off-by: Jussi Kukkonen <[email protected]>
1 parent 6b53ac7 commit f2cff95

File tree

2 files changed

+133
-295
lines changed

2 files changed

+133
-295
lines changed

tests/test_metadata_bundle.py

Lines changed: 74 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -15,166 +15,108 @@
1515
logger = logging.getLogger(__name__)
1616

1717
class TestMetadataBundle(unittest.TestCase):
18-
@classmethod
19-
def setUpClass(cls):
20-
cls.temp_dir = tempfile.mkdtemp(dir=os.getcwd())
21-
22-
@classmethod
23-
def tearDownClass(cls):
24-
shutil.rmtree(cls.temp_dir)
25-
26-
def setUp(self):
27-
# copy metadata to "local repo"
28-
shutil.copytree(
29-
os.path.join(os.getcwd(), 'repository_data', 'repository', 'metadata'),
30-
self.temp_dir,
31-
dirs_exist_ok=True
32-
)
33-
34-
def test_local_load(self):
35-
36-
# test loading all local metadata succesfully
37-
bundle = MetadataBundle(self.temp_dir)
38-
bundle.root_update_finished()
39-
bundle.load_local_timestamp()
40-
bundle.load_local_snapshot()
41-
bundle.load_local_targets()
42-
bundle.load_local_delegated_targets('role1','targets')
43-
bundle.load_local_delegated_targets('role2','role1')
44-
45-
# Make sure loading metadata without its "dependencies" fails
46-
bundle = MetadataBundle(self.temp_dir)
47-
48-
with self.assertRaises(RuntimeError):
49-
bundle.load_local_timestamp()
50-
bundle.root_update_finished()
51-
with self.assertRaises(RuntimeError):
52-
bundle.load_local_snapshot()
53-
bundle.load_local_timestamp()
54-
with self.assertRaises(RuntimeError):
55-
bundle.load_local_targets()
56-
bundle.load_local_snapshot()
57-
with self.assertRaises(RuntimeError):
58-
bundle.load_local_delegated_targets('role1','targets')
59-
bundle.load_local_targets()
60-
with self.assertRaises(RuntimeError):
61-
bundle.load_local_delegated_targets('role2','role1')
62-
bundle.load_local_delegated_targets('role1','targets')
63-
bundle.load_local_delegated_targets('role2','role1')
6418

6519
def test_update(self):
66-
remote_dir = os.path.join(os.getcwd(), 'repository_data', 'repository', 'metadata')
67-
68-
# remove all but root.json from local repo
69-
os.remove(os.path.join(self.temp_dir, "timestamp.json"))
70-
os.remove(os.path.join(self.temp_dir, "snapshot.json"))
71-
os.remove(os.path.join(self.temp_dir, "targets.json"))
72-
os.remove(os.path.join(self.temp_dir, "role1.json"))
73-
os.remove(os.path.join(self.temp_dir, "role2.json"))
20+
repo_dir = os.path.join(os.getcwd(), 'repository_data', 'repository', 'metadata')
7421

75-
bundle = MetadataBundle(self.temp_dir)
22+
with open(os.path.join(repo_dir, "root.json"), "rb") as f:
23+
bundle = MetadataBundle(f.read())
7624
bundle.root_update_finished()
7725

78-
# test local load failure, then updating metadata succesfully
79-
with self.assertRaises(exceptions.RepositoryError):
80-
bundle.load_local_timestamp()
81-
with open(os.path.join(remote_dir, "timestamp.json"), "rb") as f:
26+
with open(os.path.join(repo_dir, "timestamp.json"), "rb") as f:
8227
bundle.update_timestamp(f.read())
83-
with open(os.path.join(remote_dir, "snapshot.json"), "rb") as f:
28+
with open(os.path.join(repo_dir, "snapshot.json"), "rb") as f:
8429
bundle.update_snapshot(f.read())
85-
with open(os.path.join(remote_dir, "targets.json"), "rb") as f:
30+
with open(os.path.join(repo_dir, "targets.json"), "rb") as f:
8631
bundle.update_targets(f.read())
87-
with open(os.path.join(remote_dir, "role1.json"), "rb") as f:
32+
with open(os.path.join(repo_dir, "role1.json"), "rb") as f:
8833
bundle.update_delegated_targets(f.read(), "role1", "targets")
89-
with open(os.path.join(remote_dir, "role2.json"), "rb") as f:
34+
with open(os.path.join(repo_dir, "role2.json"), "rb") as f:
9035
bundle.update_delegated_targets(f.read(), "role2", "role1")
9136

92-
# test loading the metadata (that should now be locally available)
93-
bundle = MetadataBundle(self.temp_dir)
94-
bundle.root_update_finished()
95-
bundle.load_local_timestamp()
96-
bundle.load_local_snapshot()
97-
bundle.load_local_targets()
98-
bundle.load_local_delegated_targets('role1','targets')
99-
bundle.load_local_delegated_targets('role2','role1')
100-
101-
# TODO test loading one version, then updating to new versions of each metadata
37+
def test_out_of_order_ops(self):
38+
repo_dir = os.path.join(os.getcwd(), 'repository_data', 'repository', 'metadata')
39+
data={}
40+
for md in ["root", "timestamp", "snapshot", "targets", "role1"]:
41+
with open(os.path.join(repo_dir, f"{md}.json"), "rb") as f:
42+
data[md] = f.read()
10243

103-
def test_local_load_with_invalid_data(self):
104-
# Test root and one of the top-level metadata files
44+
bundle = MetadataBundle(data["root"])
10545

106-
with tempfile.TemporaryDirectory() as tempdir:
107-
# Missing root.json
108-
with self.assertRaises(exceptions.RepositoryError):
109-
MetadataBundle(tempdir)
46+
# Update timestamp before root is finished
47+
with self.assertRaises(RuntimeError):
48+
bundle.update_timestamp(data["timestamp"])
11049

111-
# root.json not a json file at all
112-
with open(os.path.join(tempdir, "root.json"), "w") as f:
113-
f.write("")
114-
with self.assertRaises(exceptions.RepositoryError):
115-
MetadataBundle(tempdir)
50+
bundle.root_update_finished()
51+
with self.assertRaises(RuntimeError):
52+
bundle.root_update_finished()
11653

117-
# root.json does not validate
118-
md = Metadata.from_file(os.path.join(self.temp_dir, "root.json"))
119-
md.signed.version += 1
120-
md.to_file(os.path.join(tempdir, "root.json"))
121-
with self.assertRaises(exceptions.RepositoryError):
122-
MetadataBundle(tempdir)
54+
# Update snapshot before timestamp
55+
with self.assertRaises(RuntimeError):
56+
bundle.update_snapshot(data["snapshot"])
12357

124-
md.signed.version -= 1
125-
md.to_file(os.path.join(tempdir, "root.json"))
126-
bundle = MetadataBundle(tempdir)
127-
bundle.root_update_finished()
58+
bundle.update_timestamp(data["timestamp"])
12859

129-
# Missing timestamp.json
130-
with self.assertRaises(exceptions.RepositoryError):
131-
bundle.load_local_timestamp()
60+
# Update targets before snapshot
61+
with self.assertRaises(RuntimeError):
62+
bundle.update_targets(data["targets"])
13263

133-
# timestamp not a json file at all
134-
with open(os.path.join(tempdir, "timestamp.json"), "w") as f:
135-
f.write("")
136-
with self.assertRaises(exceptions.RepositoryError):
137-
bundle.load_local_timestamp()
64+
bundle.update_snapshot(data["snapshot"])
13865

139-
# timestamp does not validate
140-
md = Metadata.from_file(os.path.join(self.temp_dir, "timestamp.json"))
141-
md.signed.version += 1
142-
md.to_file(os.path.join(tempdir, "timestamp.json"))
143-
with self.assertRaises(exceptions.RepositoryError):
144-
bundle.load_local_timestamp()
66+
#update timestamp after snapshot
67+
with self.assertRaises(RuntimeError):
68+
bundle.update_timestamp(data["timestamp"])
14569

146-
md.signed.version -= 1
147-
md.to_file(os.path.join(tempdir, "timestamp.json"))
148-
bundle.load_local_timestamp()
70+
# Update delegated targets before targets
71+
with self.assertRaises(RuntimeError):
72+
bundle.update_delegated_targets(data["role1"], "role1", "targets")
14973

150-
def test_update_with_invalid_data(self):
151-
# Test on of the top level metadata files
74+
bundle.update_targets(data["targets"])
75+
bundle.update_delegated_targets(data["role1"], "role1", "targets")
15276

153-
timestamp_md = Metadata.from_file(os.path.join(self.temp_dir, "timestamp.json"))
77+
def test_update_with_invalid_json(self):
78+
repo_dir = os.path.join(os.getcwd(), 'repository_data', 'repository', 'metadata')
79+
data={}
80+
for md in ["root", "timestamp", "snapshot", "targets", "role1"]:
81+
with open(os.path.join(repo_dir, f"{md}.json"), "rb") as f:
82+
data[md] = f.read()
15483

155-
# remove all but root.json from local repo
156-
os.remove(os.path.join(self.temp_dir, "timestamp.json"))
157-
os.remove(os.path.join(self.temp_dir, "snapshot.json"))
158-
os.remove(os.path.join(self.temp_dir, "targets.json"))
159-
os.remove(os.path.join(self.temp_dir, "role1.json"))
160-
os.remove(os.path.join(self.temp_dir, "role2.json"))
84+
# root.json not a json file at all
85+
with self.assertRaises(exceptions.RepositoryError):
86+
MetadataBundle(b"")
87+
# root.json is invalid
88+
root = Metadata.from_bytes(data["root"])
89+
root.signed.version += 1
90+
with self.assertRaises(exceptions.RepositoryError):
91+
MetadataBundle(json.dumps(root.to_dict()).encode())
16192

162-
bundle = MetadataBundle(self.temp_dir)
93+
bundle = MetadataBundle(data["root"])
16394
bundle.root_update_finished()
16495

165-
# timestamp not a json file at all
166-
with self.assertRaises(exceptions.RepositoryError):
167-
bundle.update_timestamp(b"")
96+
top_level_md = [
97+
(data["timestamp"], bundle.update_timestamp),
98+
(data["snapshot"], bundle.update_snapshot),
99+
(data["targets"], bundle.update_targets),
100+
]
101+
for metadata, update_func in top_level_md:
102+
# metadata is not json
103+
with self.assertRaises(exceptions.RepositoryError):
104+
update_func(b"")
105+
# metadata is invalid
106+
md = Metadata.from_bytes(metadata)
107+
md.signed.version += 1
108+
with self.assertRaises(exceptions.RepositoryError):
109+
update_func(json.dumps(md.to_dict()).encode())
110+
111+
# metadata is of wrong type
112+
with self.assertRaises(exceptions.RepositoryError):
113+
update_func(data["root"])
114+
115+
update_func(metadata)
168116

169-
# timestamp does not validate
170-
timestamp_md.signed.version += 1
171-
data = timestamp_md.to_dict()
172-
with self.assertRaises(exceptions.RepositoryError):
173-
bundle.update_timestamp(json.dumps(data).encode())
174117

175-
timestamp_md.signed.version -= 1
176-
data = timestamp_md.to_dict()
177-
bundle.update_timestamp(json.dumps(data).encode())
118+
# TODO test updating over initial metadata (new keys, newer timestamp, etc)
119+
# TODO test the actual specification checks
178120

179121

180122
if __name__ == '__main__':

0 commit comments

Comments
 (0)