Skip to content

Commit bac6de1

Browse files
committed
config: branches as dict
Represent branches as dict rather than a list to avoid mapping each time. Also forward calls to /api/latest and /api/branches to /json/{latest,branches}.json to allow static hosting, future clients should request these files directly. Also add some more testcases Signed-off-by: Paul Spooren <mail@aparcar.org>
1 parent 59bcc75 commit bac6de1

File tree

9 files changed

+152
-63
lines changed

9 files changed

+152
-63
lines changed

asu/api.py

Lines changed: 13 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from uuid import uuid4
22

3-
from flask import Blueprint, current_app, g, request
3+
from flask import Blueprint, current_app, g, redirect, request
44
from rq import Connection, Queue
55

66
from .build import build
@@ -18,17 +18,6 @@ def get_distros() -> list:
1818
return ["openwrt"]
1919

2020

21-
@bp.route("/branches")
22-
def get_branches() -> dict:
23-
if "branches" not in g:
24-
g.branches = {}
25-
for branch in current_app.config["BRANCHES"]:
26-
if branch["enabled"]:
27-
g.branches[branch["name"]] = branch
28-
current_app.logger.info(f"Loaded {len(g.branches)} branches")
29-
return g.branches
30-
31-
3221
def get_redis():
3322
"""Return Redis connectio
3423
@@ -40,24 +29,14 @@ def get_redis():
4029
return g.redis
4130

4231

43-
def get_latest() -> dict:
44-
if "latest" not in g:
45-
g.latest = list(
46-
map(
47-
lambda b: b["versions"][0],
48-
filter(
49-
lambda b: b.get("enabled"),
50-
current_app.config["BRANCHES"],
51-
),
52-
)
53-
)
54-
55-
return g.latest
56-
57-
5832
@bp.route("/latest")
5933
def api_latest():
60-
return {"latest": get_latest()}
34+
return redirect("/json/latest.json")
35+
36+
37+
@bp.route("/branches")
38+
def api_branches():
39+
return redirect("/json/branches.json")
6140

6241

6342
def get_queue() -> Queue:
@@ -162,7 +141,7 @@ def validate_request(req):
162141
# e.g. snapshot, 21.02.0-rc1 or 19.07.7
163142
req["branch"] = req["version"].rsplit(".", maxsplit=1)[0]
164143

165-
if req["branch"] not in get_branches().keys():
144+
if req["branch"] not in current_app.config["BRANCHES"].keys():
166145
return (
167146
{
168147
"status": "bad_branch",
@@ -171,7 +150,7 @@ def validate_request(req):
171150
400,
172151
)
173152

174-
if req["version"] not in get_branches()[req["branch"]]["versions"]:
153+
if req["version"] not in current_app.config["BRANCHES"][req["branch"]]["versions"]:
175154
return (
176155
{
177156
"status": "bad_version",
@@ -196,7 +175,9 @@ def validate_request(req):
196175
400,
197176
)
198177

199-
req["arch"] = get_branches()[req["branch"]]["targets"][req["target"]]
178+
req["arch"] = current_app.config["BRANCHES"][req["branch"]]["targets"][
179+
req["target"]
180+
]
200181

201182
if req["target"] in ["x86/64", "x86/generic", "x86/geode", "x86/legacy"]:
202183
current_app.logger.debug("Use generic profile for {req['target']}")
@@ -331,7 +312,7 @@ def api_build():
331312

332313
req["store_path"] = current_app.config["STORE_PATH"]
333314
req["upstream_url"] = current_app.config["UPSTREAM_URL"]
334-
req["branch_data"] = get_branches()[req["branch"]]
315+
req["branch_data"] = current_app.config["BRANCHES"][req["branch"]]
335316

336317
if req["branch_data"]["snapshot"]:
337318
result_ttl = "15m"

asu/asu.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,30 @@ def store_path(path="index.html"):
6666
app.register_blueprint(api.bp)
6767

6868
(app.config["JSON_PATH"] / "branches.json").write_text(
69-
json.dumps(app.config["BRANCHES"])
69+
json.dumps(
70+
dict(
71+
map(
72+
lambda b: (b["name"], b),
73+
filter(lambda b: b.get("enabled"), app.config["BRANCHES"].values()),
74+
)
75+
)
76+
)
77+
)
78+
79+
(app.config["JSON_PATH"] / "latest.json").write_text(
80+
json.dumps(
81+
{
82+
"latest": list(
83+
map(
84+
lambda b: b["versions"][0],
85+
filter(
86+
lambda b: b.get("enabled"),
87+
app.config["BRANCHES"].values(),
88+
),
89+
)
90+
)
91+
}
92+
)
7093
)
7194

7295
return app

asu/common.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import base64
22
import hashlib
3+
import json
34
import struct
45
from pathlib import Path
56

@@ -55,7 +56,7 @@ def get_manifest_hash(manifest: dict) -> str:
5556
Returns:
5657
str: hash of `req`
5758
"""
58-
return str(hash(frozenset(sorted(manifest.items()))))
59+
return get_str_hash(json.dumps(manifest, sort_keys=True))
5960

6061

6162
def get_request_hash(req: dict) -> str:

asu/janitor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ def update(interval):
336336
"""
337337
current_app.logger.info("Init ASU janitor")
338338
while True:
339-
for branch in current_app.config["BRANCHES"]:
339+
for branch in current_app.config["BRANCHES"].values():
340340
if not branch.get("enabled"):
341341
current_app.logger.info(f"Skip disabled version {branch['name']}")
342342
continue

tests/conftest.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ def redis():
3939
)
4040
r.sadd("targets-SNAPSHOT", "testtarget/testsubtarget", "x86/64")
4141
r.sadd("targets-21.02", "testtarget/testsubtarget")
42+
r.hset("mapping-abi", mapping={"test1-1": "test1"})
4243
yield r
4344

4445

@@ -52,8 +53,8 @@ def app(redis):
5253
"STORE_PATH": test_path + "/store",
5354
"TESTING": True,
5455
"UPSTREAM_URL": "http://localhost:8001",
55-
"BRANCHES": [
56-
{
56+
"BRANCHES": {
57+
"SNAPSHOT": {
5758
"name": "SNAPSHOT",
5859
"enabled": True,
5960
"snapshot": True,
@@ -69,7 +70,7 @@ def app(redis):
6970
"x86/64": "x86_64",
7071
},
7172
},
72-
{
73+
"21.02": {
7374
"name": "21.02",
7475
"enabled": True,
7576
"snapshot": True,
@@ -82,7 +83,7 @@ def app(redis):
8283
"updates": "rc",
8384
"targets": {"testtarget/testsubtarget": "testarch"},
8485
},
85-
{
86+
"19.07": {
8687
"name": "19.07",
8788
"enabled": True,
8889
"versions": ["19.07.7", "19.07.6"],
@@ -94,7 +95,7 @@ def app(redis):
9495
"updates": "stable",
9596
"targets": {"testtarget/testsubtarget": "testarch"},
9697
},
97-
],
98+
},
9899
}
99100
)
100101

tests/test_api.py

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
def test_api_version(client, app):
22
response = client.get("/api/branches")
3-
branches = {}
4-
for branch in app.config["BRANCHES"]:
5-
if branch["enabled"]:
6-
branches[branch["name"]] = branch
7-
assert response.json == branches
3+
assert response.status == "302 FOUND"
84

95

106
def test_api_build(client):
@@ -19,15 +15,12 @@ def test_api_build(client):
1915
)
2016
assert response.status == "202 ACCEPTED"
2117
assert response.json.get("status") == "queued"
22-
assert response.json.get("request_hash") == "3128aff5c6db"
18+
assert response.json.get("request_hash") == "e360f833a191"
2319

2420

2521
def test_api_latest_default(client):
2622
response = client.get("/api/latest")
27-
assert "19.07.7" in response.json["latest"]
28-
assert "21.02.0-rc1" in response.json["latest"]
29-
assert "SNAPSHOT" in response.json["latest"]
30-
assert response.status == "200 OK"
23+
assert response.status == "302 FOUND"
3124

3225

3326
def test_api_build_mapping(client):
@@ -42,11 +35,44 @@ def test_api_build_mapping(client):
4235
)
4336
assert response.status == "202 ACCEPTED"
4437
assert response.json.get("status") == "queued"
45-
assert response.json.get("request_hash") == "d0318d0bba8d"
38+
assert response.json.get("request_hash") == "19bb42f198c9"
39+
40+
41+
def test_api_build_mapping_abi(client):
42+
response = client.post(
43+
"/api/build",
44+
json=dict(
45+
version="SNAPSHOT",
46+
target="testtarget/testsubtarget",
47+
profile="testvendor,testprofile",
48+
packages=["test1-1", "test2"],
49+
),
50+
)
51+
assert response.status == "202 ACCEPTED"
52+
assert response.json.get("status") == "queued"
53+
assert response.json.get("request_hash") == "7d099fb07fb3"
54+
55+
56+
def test_api_build_bad_target(client):
57+
response = client.post(
58+
"/api/build",
59+
json=dict(
60+
version="SNAPSHOT",
61+
target="testtarget/testsubtargetbad",
62+
profile="testvendor,testprofile",
63+
packages=["test1", "test2"],
64+
),
65+
)
66+
assert response.status == "400 BAD REQUEST"
67+
assert (
68+
response.json.get("message")
69+
== "Unsupported target: testtarget/testsubtargetbad"
70+
)
71+
assert response.json.get("status") == "bad_target"
4672

4773

4874
def test_api_build_get(client):
49-
client.post(
75+
response = client.post(
5076
"/api/build",
5177
json=dict(
5278
version="SNAPSHOT",
@@ -55,10 +81,42 @@ def test_api_build_get(client):
5581
packages=["test1", "test2"],
5682
),
5783
)
58-
response = client.get("/api/build/3128aff5c6db")
84+
assert response.json["request_hash"] == "e360f833a191"
85+
response = client.get("/api/build/e360f833a191")
5986
assert response.status == "202 ACCEPTED"
6087
assert response.json.get("status") == "queued"
61-
assert response.json.get("request_hash") == "3128aff5c6db"
88+
assert response.json.get("request_hash") == "e360f833a191"
89+
90+
91+
def test_api_build_packages_versions(client):
92+
response = client.post(
93+
"/api/build",
94+
json=dict(
95+
version="SNAPSHOT",
96+
target="testtarget/testsubtarget",
97+
profile="testprofile",
98+
packages_versions={"test1": "1.0", "test2": "2.0"},
99+
),
100+
)
101+
assert response.json["request_hash"] == "552b9e328888"
102+
response = client.get("/api/build/552b9e328888")
103+
assert response.status == "202 ACCEPTED"
104+
assert response.json.get("status") == "queued"
105+
assert response.json.get("request_hash") == "552b9e328888"
106+
107+
108+
def test_api_build_packages_duplicate(client):
109+
response = client.post(
110+
"/api/build",
111+
json=dict(
112+
version="SNAPSHOT",
113+
target="testtarget/testsubtarget",
114+
profile="testprofile",
115+
packages=["test1", "test2"],
116+
packages_versions={"test1": "1.0", "test2": "2.0"},
117+
),
118+
)
119+
assert response.status == "400 BAD REQUEST"
62120

63121

64122
def test_api_build_get_not_found(client):
@@ -83,7 +141,7 @@ def test_api_build_empty_packages_list(client):
83141
)
84142
assert response.status == "202 ACCEPTED"
85143
assert response.json.get("status") == "queued"
86-
assert response.json.get("request_hash") == "c6022275d623"
144+
assert response.json.get("request_hash") == "66cb932c37a4"
87145

88146

89147
def test_api_build_withouth_packages_list(client):
@@ -97,7 +155,7 @@ def test_api_build_withouth_packages_list(client):
97155
)
98156
assert response.status == "202 ACCEPTED"
99157
assert response.json.get("status") == "queued"
100-
assert response.json.get("request_hash") == "c6022275d623"
158+
assert response.json.get("request_hash") == "66cb932c37a4"
101159

102160

103161
def test_api_build_prerelease_snapshot(client):
@@ -162,7 +220,7 @@ def test_api_build_x86(client):
162220

163221
assert response.status == "202 ACCEPTED"
164222
assert response.json.get("status") == "queued"
165-
assert response.json.get("request_hash") == "a30a7dc8e19b"
223+
assert response.json.get("request_hash") == "1fda145d439f"
166224

167225

168226
def test_api_build_needed(client):

tests/test_common.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def test_get_request_hash():
3232
"package_hash": get_packages_hash(["test"]),
3333
}
3434

35-
assert get_request_hash(request) == "e8dc1720525b"
35+
assert get_request_hash(request) == "c753cbe6356a"
3636

3737

3838
def test_get_request_hash_diff_packages():
@@ -44,7 +44,12 @@ def test_get_request_hash_diff_packages():
4444
"diff_packages": True,
4545
}
4646

47-
assert get_request_hash(request) == "75967363f440"
47+
assert get_request_hash(request) == "a7ff5158d58b"
48+
49+
50+
def test_fingerprint_pubkey_usign():
51+
pub_key = "RWSrHfFmlHslUcLbXFIRp+eEikWF9z1N77IJiX5Bt/nJd1a/x+L+SU89"
52+
assert fingerprint_pubkey_usign(pub_key) == "ab1df166947b2551"
4853

4954

5055
def test_verify_usign():

tests/test_factory.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,23 @@ def test_pathlib(app):
1111

1212
def test_other(app):
1313
assert app.config["UPSTREAM_URL"] == "http://localhost:8001"
14+
15+
16+
def test_json_path_latest(client):
17+
response = client.get("/json/latest.json")
18+
assert "19.07.7" in response.json["latest"]
19+
assert "21.02.0-rc1" in response.json["latest"]
20+
assert "SNAPSHOT" in response.json["latest"]
21+
assert response.status == "200 OK"
22+
23+
24+
def test_json_path_branches(client):
25+
response = client.get("/json/branches.json")
26+
assert "19.07" in response.json.keys()
27+
assert "SNAPSHOT" in response.json.keys()
28+
assert response.status == "200 OK"
29+
30+
31+
def test_json_store(client):
32+
response = client.get("/store/")
33+
assert response.status == "404 NOT FOUND"

0 commit comments

Comments
 (0)