Skip to content

Commit

Permalink
add /api/build/<request_hash> API
Browse files Browse the repository at this point in the history
Now it is possible to directly request a known `request_hash`.

This should be used by all clients to run the verification function of
the server less often. Meaning a client fires the first request,
receives a `reuqest_hash` and can continue polling the hash.

Also usable for front ends to share static links of build images.

Signed-off-by: Paul Spooren <mail@aparcar.org>
  • Loading branch information
aparcar committed Feb 26, 2020
1 parent 407ea4a commit 903a264
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 45 deletions.
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,16 +101,20 @@ responded JSON contains a newer version.
| `profile` | `netgear_wndr4300-v2` | `board_name` of `ubus call system board` |
| `packages` | `["luci", "vim"]` | Extra packages for the new image |

Each valid request returns a `request_hash` which can be used for future
polling via `/api/build/<request_hash>`.

### Response `status 200`

A `200` response means the image was sucessfully created. The response is JSON
encoded containing build information.

| key | information |
| ---------- | ------------------------------------------- |
| `bin_dir` | relative path to created files |
| `buildlog` | boolean if buildlog.txt was created |
| `manifest` | dict of all installed packages plus version |
| key | information |
| -------------- | ------------------------------------------- |
| `bin_dir` | relative path to created files |
| `buildlog` | boolean if buildlog.txt was created |
| `manifest` | dict of all installed packages plus version |
| `request_hash` | hashed request data stored by the server |

{
"build_at": "Tue, 25 Feb 2020 08:49:48 GMT",
Expand Down Expand Up @@ -147,6 +151,7 @@ encoded containing build information.
"zlib": "1.2.11-3"
},
"metadata_version": 1,
"request_hash": "5bac6cb8321f",
"supported_devices": [
"avm,fritzbox-4040"
],
Expand Down
94 changes: 54 additions & 40 deletions asu/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,46 @@ def validate_request(request_data):
def api_versions():
return get_versions()

def return_job(job):
response = {}
if job.meta:
response.update(job.meta)

if job.is_failed:
status = 500
response["message"] = job.exc_info.strip().split("\n")[-1]

if job.is_queued or job.is_started:
status = 202
response = {"status": job.get_status()}

if job.is_finished:
status = 200
response.update(job.result)
response["build_at"] = job.ended_at

response["enqueued_at"] = job.enqueued_at
response["request_hash"] = job.id

current_app.logger.debug(f"Response {response} with status {status}")
return response, status

@bp.route("/build/<request_hash>", methods=["GET"])
def api_build_get(request_hash):
job = get_queue().fetch_job(request_hash)
if not job:
return {"status": "not_found"}, 404

return return_job(job)


@bp.route("/build", methods=["POST"])
def api_build():
request_data = request.get_json()
current_app.logger.debug("rerequest_data {request_data}")
if not request_data:
return {"status": "bad_request"}, 400

current_app.logger.debug(request_data)
request_hash = get_request_hash(request_data)
job = get_queue().fetch_job(request_hash)
response = {}
Expand All @@ -140,44 +172,26 @@ def api_build():

if job is None:
response, status = validate_request(request_data)
if not response:
status = 202
request_data["store_path"] = current_app.config["STORE_PATH"]
request_data["upstream_url"] = current_app.config["UPSTREAM_URL"]
request_data["version_data"] = current_app.config["VERSIONS"][
request_data["version"]
]
if "packages" in request_data:
request_data["packages"] = set(request_data["packages"])
else:
request_data["packages"] = set()

job = get_queue().enqueue(
build,
request_data,
job_id=request_hash,
result_ttl=result_ttl,
failure_ttl=failure_ttl,
)

if job:
if job.meta:
response.update(job.meta)

if job.is_failed:
status = 500
response["message"] = job.exc_info.strip().split("\n")[-1]

if job.is_queued or job.is_started:
status = 202
response = {"status": job.get_status()}

if job.is_finished:
response.update(job.result)
response["build_at"] = job.ended_at
if response:
return response, status

request_data["store_path"] = current_app.config["STORE_PATH"]
request_data["upstream_url"] = current_app.config["UPSTREAM_URL"]
request_data["version_data"] = current_app.config["VERSIONS"][
request_data["version"]
]
if "packages" in request_data:
request_data["packages"] = set(request_data["packages"])
else:
request_data["packages"] = set()

job = get_queue().enqueue(
build,
request_data,
job_id=request_hash,
result_ttl=result_ttl,
failure_ttl=failure_ttl,
)

response["enqueued_at"] = job.enqueued_at
response["request_hash"] = request_hash
return return_job(job)

current_app.logger.debug(f"Response {response} with status {status}")
return response, status
24 changes: 24 additions & 0 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,30 @@ def test_api_build(client):
assert response.json.get("request_hash") == "0222f0cd9290"


def test_api_build_get(client):
client.post(
"/api/build",
json=dict(
version="SNAPSHOT",
profile="8devices_carambola",
packages=["test1", "test2"],
),
)
response = client.get("/api/build/0222f0cd9290")
assert response.status == "202 ACCEPTED"
assert response.json.get("status") == "queued"
assert response.json.get("request_hash") == "0222f0cd9290"

def test_api_build_get_not_found(client):
response = client.get("/api/build/testtesttest")
assert response.status == "404 NOT FOUND"


def test_api_build_get_no_post(client):
response = client.post("/api/build/0222f0cd9290")
assert response.status == "405 METHOD NOT ALLOWED"


def test_api_build_empty_packages(client):
response = client.post(
"/api/build", json=dict(version="SNAPSHOT", profile="8devices_carambola")
Expand Down

0 comments on commit 903a264

Please sign in to comment.