Skip to content

Commit 17f4bc6

Browse files
committed
reverted to old key_setup, add handling for private keys, refactored tests
1 parent 099878a commit 17f4bc6

5 files changed

Lines changed: 106 additions & 129 deletions

File tree

stackinator/mirror.py

Lines changed: 44 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,7 @@ def __init__(
3939
self._check_mirrors()
4040

4141
# Will hold a list of all the gpg keys (public and private)
42-
# self._keys: Optional[List[pathlib.Path]] = []
43-
self._keys = self._key_init()
42+
self._keys: Optional[List[pathlib.Path]] = []
4443

4544
def _load_mirrors(self, cmdline_cache: Optional[pathlib.Path]) -> Dict[str, Dict]:
4645
"""Load the mirrors file, if one exists."""
@@ -156,12 +155,14 @@ def _check_mirrors(self):
156155
f"Check the url listed in mirrors.yaml in system config. \n{err}"
157156
)
158157

159-
def key_files(self, config_root: pathlib.Path):
158+
@property
159+
def keys(self):
160160
"""Return the list of public and private key file paths."""
161+
161162
if self._keys is None:
162163
raise RuntimeError("The mirror.keys method was accessed before setup_configs() was called.")
163-
key_dir = config_root / self.KEY_STORE_DIR
164-
return [key_dir / info["path"] for info in self._keys]
164+
165+
return self._keys
165166

166167
def setup_configs(self, config_root: pathlib.Path):
167168
"""Setup all mirror configs in the given config_root."""
@@ -230,67 +231,64 @@ def _create_bootstrap_configs(self, config_root: pathlib.Path):
230231
with (config_root / "bootstrap.yaml").open("w") as file:
231232
yaml.dump(bootstrap_yaml, file, default_flow_style=False)
232233

233-
def _key_init(self):
234-
key_info = {}
235-
236-
key = self.mirrors["buildcache"].get("private_key")
234+
def _load_key(self, key: str, dest: pathlib.Path, name: str):
235+
"""Validate mirror keys, relocate to key_store, and update mirror config with new key paths."""
237236

238-
if key is None:
239-
return
237+
# key will be saved under key_store/mirror_name.[pub/priv].gpg
240238

241239
# if path, check if abs path, if not, append sys config path in front and check again
242240
path = pathlib.Path(os.path.expandvars(key))
243-
244241
if not path.is_absolute():
245242
# try prepending system config path
246243
path = self._system_config_root / path
247244

248245
if path.is_file():
249-
# use the user-provided file
250-
key_info = {"path": pathlib.Path(f"buildcache.pgp"), "source": path}
246+
with open(path, "rb") as reader:
247+
binary_key = reader.read()
248+
249+
# convert base64 key to binary
251250
else:
252-
# convert base64 key to binary
253251
try:
254-
binary_key = base64.b64decode(key, validate=True)
255-
print(binary_key)
252+
binary_key = base64.b64decode(key)
256253
except ValueError:
257254
raise MirrorError(
258-
f"Key for mirror 'buildcache' is not valid. \n"
255+
f"Key for mirror '{name}' is not valid: '{path}'. \n"
259256
f"Must be a path to a GPG public key or a base64 encoded GPG public key. \n"
260257
f"Check the key listed in mirrors.yaml in system config."
261258
)
262259

263-
file_type = magic.from_buffer(binary_key, mime=True)
264-
if file_type not in ("application/x-gnupg-keyring", "application/pgp-keys"):
265-
raise MirrorError(
266-
f"Key for mirror 'buildcache' is not a valid GPG key. \n"
267-
f"The file (or base64) was readable, but the data itself was not a PGP key.\n"
268-
f"Check the key listed in mirrors.yaml in system config."
269-
)
270-
271-
key_info = {"path": pathlib.Path("buildcache.pgp"), "source": binary_key}
260+
# private keys will evaluate as "application/octet-stream"
261+
file_type = magic.from_buffer(binary_key, mime=True)
262+
if file_type not in ("application/x-gnupg-keyring", "application/pgp-keys", "application/octet-stream"):
263+
raise MirrorError(
264+
f"Key for mirror {name} is not a valid GPG key. \n"
265+
f"The file (or base64) was readable, but the data itself was not a PGP key.\n"
266+
f"Check the key listed in mirrors.yaml in system config."
267+
)
272268

273-
return key_info
269+
# copy key to new destination in key store
270+
with open(dest, "wb") as writer:
271+
writer.write(binary_key)
274272

273+
self._keys.append(dest)
274+
275+
275276
def _key_setup(self, key_store: pathlib.Path):
276-
"""Validate mirror keys, relocate to key_store, and update mirror config with new key paths."""
277+
"""Iterate through mirror keys and load + relocate each one to key_store"""
277278

279+
self._keys = []
278280
key_store.mkdir(exist_ok=True)
279281

280-
#for key_info in self._keys:
281-
282-
#path = key_store / key_info["path"]
283-
path = key_store / self._keys["path"]
284-
#source = key_info["source"]
285-
source = self._keys["source"]
286-
287-
match source:
288-
case pathlib.Path():
289-
# copy source -> path
290-
shutil.copy2(source, path)
291-
case bytes():
292-
# open path and copy in bytes
293-
with open(path, "wb") as writer:
294-
writer.write(source)
295-
case _:
296-
raise TypeError(f"Expected Path or bytes, got {type(source).__name__}")
282+
for name, mirror in self.mirrors.items():
283+
if name == "buildcache":
284+
if mirror.get("private_key"):
285+
key = mirror["private_key"]
286+
dest = pathlib.Path(key_store / f"{name}.priv.gpg")
287+
self._load_key(key, dest, name)
288+
289+
if mirror.get("public_key") is None:
290+
continue
291+
292+
key = mirror["public_key"]
293+
dest = pathlib.Path(key_store / f"{name}.pub.gpg")
294+
self._load_key(key, dest, name)

stackinator/schema/mirror.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"enabled": {
1111
"type": "boolean",
1212
"default": true
13-
}
13+
},
14+
"public_key": {"type": "string"}
1415
},
1516
"additionalProperties": false,
1617
"required": ["url"]
@@ -48,7 +49,8 @@
4849
"enabled": {
4950
"type": "boolean",
5051
"default": true
51-
}
52+
},
53+
"public_key": {"type": "string"}
5254
},
5355
"additionalProperties": false,
5456
"required": ["url"]

unittests/data/systems/mirror-ok-raw-key/mirrors.yaml

Lines changed: 0 additions & 59 deletions
This file was deleted.

unittests/data/systems/mirror-ok/mirrors.yaml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,55 @@ bootstrap:
44
buildcache:
55
url: https://mirror.spack.io
66
enabled: true
7+
public_key: "\
8+
mQINBGm4GvsBEACTyzQFPfRUgo1Wmb9/KgrSr/EFVobRX3LlrcAMemo4nFRdS88aCcEhRWzYQ8ML\
9+
eGvcxFbzbAoEZACpThMspYOwFsVzIUc3lYQT7g9M/KNEPHztqaTWCqESYmzDFtaLYys6AQP52KMB\
10+
2x0ya8NNd9whd2a9Vc3yD3u9qW8iqkqxDjNYtTc9Lo7T8DHlhJ79TKm8f3w0QZowTxPYh5NA8GiF\
11+
kBN6J8hmg39LekC1kAWi2BwjDzRll19zVQtYfv+b4i/ripqmMNp4zcU93Ox1ReUFOmO3eiIq3GSK\
12+
IbXw7h/NGLAImIeuMzce5cqz65iVk7+iyKeXtx5dSUGskiLR2voX8xVOGVhNtxfiolviyUAhT4kb\
13+
WcWK6ipZ9h/nEyeZ9GYtoDkKMguet2a4bJCBsQmo4FR9Pf5c7qqs79obxLXzZe9zDnj1sbxAabJh\
14+
jLUdwJqIPKvdPNy3F3nOBeKY8Jf1D+Y3szxzJX8gwqrNSyCbZUeXsaQhMZWUVoztxUXW+qfC8jlA\
15+
TfoUQuQPC9Zr+8wU/3uUlKtChQo0prgwHAEHezwNpyEhZZc884RIt55DNKllH9UeqNwUWKpZYHG3\
16+
qVxV+oi7MenLeKvfsg6nVCL0CETtB88dOen7BFfBZj9NRszRqIlVudDFf+5gqKQ0f1H241w/n4nH\
17+
KEAzm7kma4cV8QARAQABtBtUZXN0IEtleSA8bm9wZUBub3doZXJlLmNvbT6JAlEEEwEKADsWIQSe\
18+
CE84hgwq8uroW+w4Qgxu0DsXiAUCabga+wIbAwULCQgHAgIiAgYVCgkICwIEFgIDAQIeBwIXgAAK\
19+
CRA4Qgxu0DsXiBbaD/4w7MlAf5SuOxrbH3fruN7k8NPxjwsvocB3VGo5AxzIy18C78IOYm5F2EQ+\
20+
LpjsK05t1ZPsHFsBaLduo0CODcF8m1gJLI8S0uMmVUywnDGJjMKYiZNeIqXDlohVciD2KPIxWL+i\
21+
qgho1BhFKlnQkXgEaxotUXFwHiKqcBwFcq21nu3PRVqsobMTk/UgeJhoSZf7ZIj5SyVHa6YVCoMr\
22+
HB0TqRz3xIW/TDl+oxyfEdhMZ8iU6IdohfMkCP+ayZEpdx1SS37S47SRxbgNblbfJ9PfJD/dQzx1\
23+
WnGVXxinSPjTTxIwNCyiEIrxqvLnk+O8fUHeEIrSqn9P0bZrCv02gAPUIfl7l4gN4boSgWEsfS5i\
24+
/KL8ZUDGZCb9Fux64zaM17lHfwGWCAKbi1KjYRL4W7zaVmps6MfdLOcJdQSdpQufs9vRbMhiDW1x\
25+
glsvz8JzPYiF2xRqJsx2odiufx4Mrrq5yxER07sDjKzUZYF6xD8qC5BAh6/xDUE+p8EOqc0HsqTE\
26+
PhqBLdqGLf8GaXj3I9F6ZCH5dtSmehB6Q77KJ1hWTm9OzDdYm2apExbMIB9Z2H5c8FLZfIb4lnpW\
27+
lhtP97eAix9JnBzoTe8QeaV5hcvQoqypTu5rD3ne2kQHlavmSeq5KVVWrIKGsfnZNRqDg7vKg/fw\
28+
kx1f2T8qMLVfGxogJrkCDQRpuBr7ARAA08MhBUQkj4nVaLmW+Xa5e+nTnE8nYoKPYJIpbIDYP2Pe\
29+
mD9Yca9HGWHnzUgsH4KEvdETrT/Uj9i3o+6xNZJ1Xz0GQkKW+2Vttl5ZBKwipHsn2iP+e78wAhwm\
30+
HqYZi/ymgLJDEXmrgyXNr+cKaAI2cEWOb+Vomgu+WFF4ENG/UiIJH6zP5JY0TcSC1Ao47y4qL6bH\
31+
Mfzz2zi3JpbvOi1/uY2TvMgitaL45tsukuEYMFfEEnd4Vyl0LSCFYv3zQx5JRSbu5ujlRdHnopS0\
32+
UeWZMV+iEXYZ6wKVFtQ6nvxxDpjMf8k3Q5Ss+z0F9oTRwdlaJhdhXZx8PleBu0Ah1Wxd9+V6tYgF\
33+
zrjsJB9eaXKIEeioZi8xNFB6RYUuNgjIfTtgtps685LeiGCy5q55MkC2FuKVAdv+YZcuJNdy/3gx\
34+
Kg5bLz/aVQRfxakDQHI7kp9fl3bDK5jbWeY8EKvtTI8x7fxthPZP6Az2g4zp+ZojgEUdUXveuw0Y\
35+
3MVaMu0ehG81nUHNU1tu7ELuhDWM6VkigokS1zptBsPbKojfp/oZJn6DGD1LZ+QyIdSUkjyfcs9H\
36+
sfuyAbrgzVCmbcNY42x3IOZZZjIfQ66bJZpGht6kHEfO896oB2d+a7KS25ZWa5G+SsWntv5nnclr\
37+
6DYFz3ThuYVmjQDLh8jN78/f45Jd6N8AEQEAAYkCNgQYAQoAIBYhBJ4ITziGDCry6uhb7DhCDG7Q\
38+
OxeIBQJpuBr7AhsMAAoJEDhCDG7QOxeIVx8P/1wxrmmYWhIMDObXIpCM3vxq8dO+84nTuBQbomKR\
39+
NURKOiCwgndEL3N38pf0gAToSIatrTF2VdkJsksyMEIzUaNmsYiHA9xYqhmCJ2pIqWeh2ONsNdmw\
40+
Fg/M5mwZpvwl28Z2MpJP+NY6u52a3jxkxpGY1Q4+KxgMRhqXe6faXQtYwwUiYVGPSznQPudYLZ2Z\
41+
+b8rGrz0AUVvvSWt3bVbUwZIMVSK0RVIWxG/sW7dWhkhtev+04fUlaxHnQ2b8G3h6AjONmLcIlpx\
42+
7p1dVvolEqV0YQUgosl47J3tLnzacsqNzIS1Dya0ukLrXAYmeQzQWvwhLpLqjMh3cqLl5SkjatB7\
43+
xU9Qu4IXENnvWSnqCRZzz6CbU/81FopTGgJfxbYok2v78O5qTdkbeszSHN8uCuvhpPKruHZgsFc6\
44+
lw+hYhtB8YXbB8lT2f1Fp0DeEnPM+OzRgjeRYl3gmE8/1PtKGuTCOJzTxTtLWorFYtV0DXiOq4Vd\
45+
eYkR+m3vNiYVkdALN5uIL8goYrPvs/fvq1wI49iyKw6B3pE5xIQSEjgPpwJ/7hvQUhenJTtrNRs8\
46+
eKXSnjHZjhJbgIReoXSQwG44RqNtiV8dJsdPu98P27keSigBB5kguB0gCWeFVHkLfpBR3aRxSacG\
47+
gllMF++N8+7T4/ehkA/hs2udYRkSCANLQ3I3"
748
private_key: ../../test-gpg-priv.asc
849
mount_specific: false
950
cmdline: false
1051
sourcecache:
1152
mirror1:
1253
url: https://github.com
1354
enabled: true
55+
public_key: ../../test-gpg-pub.asc
1456
mirror2:
1557
url: https://github.com/spack
1658
enabled: true

unittests/test_mirrors.py

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,17 @@ def test_mirror_init(systems_path):
3535
"mirror1": {
3636
"url": "https://github.com",
3737
"enabled": True,
38+
"public_key": "../../test-gpg-pub.asc"
3839
},
3940
"mirror2": {
4041
"url": "https://github.com/spack",
4142
"enabled": True,
4243
}
4344
}
4445

45-
# with (systems_path / "../test-gpg-pub.asc").open("rb") as pub_key_file:
46-
# key = base64.b64encode(pub_key_file.read()).decode()
47-
# valid_mirrors["buildcache"]["public_key"] = key
46+
with (systems_path / "../test-gpg-pub.asc").open("rb") as pub_key_file:
47+
key = base64.b64encode(pub_key_file.read()).decode()
48+
valid_mirrors["buildcache"]["public_key"] = key
4849

4950
assert mirrors_obj.mirrors == valid_mirrors
5051

@@ -149,8 +150,7 @@ def test_create_bootstrap_configs(tmp_path, systems_path):
149150

150151
with (tmp_path / "bootstrap.yaml").open() as f:
151152
bs_data = yaml.safe_load(f)
152-
print(bs_data)
153-
print(valid_yaml)
153+
154154
assert bs_data == valid_yaml
155155

156156
with (tmp_path / "bootstrap/bootstrap-mirror/metadata.yaml").open() as f:
@@ -161,26 +161,19 @@ def test_create_bootstrap_configs(tmp_path, systems_path):
161161
def test_key_setup(systems_path, tmp_path):
162162
"""Check that public keys are set up properly."""
163163

164-
mirrors_key_file = mirror.Mirrors(systems_path / "mirror-ok")
165-
key_dir = tmp_path / "key_dir"
166-
mirrors_raw_key = mirror.Mirrors(systems_path / "mirror-ok-raw-key")
167-
raw_dir = tmp_path / "raw_dir"
168-
169-
mirrors_key_file._key_setup(key_dir)
170-
mirrors_raw_key._key_setup(raw_dir)
164+
mirrors = mirror.Mirrors(systems_path / "mirror-ok")
171165

172-
key_file, = (p for p in key_dir.iterdir() if p.is_file())
173-
assert key_file.name == "buildcache.pgp"
166+
mirrors._key_setup(tmp_path)
174167

175-
raw_key_file, = (p for p in key_dir.iterdir() if p.is_file())
176-
assert raw_key_file.name == "buildcache.pgp"
168+
pub_files = sorted(f for f in tmp_path.iterdir() if f.name.endswith(".pub.gpg"))
169+
assert {pub_file.name for pub_file in pub_files} == {"buildcache.pub.gpg", "mirror1.pub.gpg"}
177170

178171
# The two files should be identical in content
179-
with key_file.open("rb") as file:
180-
key_file_data = file.read()
181-
with raw_key_file.open("rb") as file:
182-
raw_key_file_data = file.read()
183-
assert key_file_data == raw_key_file_data
172+
pub_file_data = []
173+
for pub_file in pub_files:
174+
with pub_file.open("rb") as file:
175+
pub_file_data.append(file.read())
176+
assert pub_file_data[0] == pub_file_data[1]
184177

185178

186179
@pytest.mark.parametrize(
@@ -193,5 +186,6 @@ def test_key_setup(systems_path, tmp_path):
193186
def test_key_setup_bad_key(tmp_path, systems_path, system_name):
194187
"""Check that MirrorError is raised for bad keys"""
195188

189+
mirrors = mirror.Mirrors(systems_path / system_name)
196190
with pytest.raises(mirror.MirrorError):
197-
mirrors = mirror.Mirrors(systems_path / system_name)
191+
mirrors._key_setup(tmp_path)

0 commit comments

Comments
 (0)