|
|
@@ -70,7 +70,11 @@ WEB = "http://127.0.0.1:%d" % APP_PORT
|
|
|
def http(method, url, data=None, headers=None, timeout=30):
|
|
|
body = None
|
|
|
if data is not None:
|
|
|
- body = "&".join("%s=%s" % (k, urllib.parse.quote(str(v))) for k, v in data.items()).encode()
|
|
|
+ pairs = []
|
|
|
+ for k, v in data.items():
|
|
|
+ for item in (v if isinstance(v, (list, tuple)) else [v]):
|
|
|
+ pairs.append((k, item))
|
|
|
+ body = "&".join("%s=%s" % (k, urllib.parse.quote(str(item))) for k, item in pairs).encode()
|
|
|
request = urllib.request.Request(WEB + url if url.startswith("/") else url,
|
|
|
data=body, method=method)
|
|
|
if data is not None:
|
|
|
@@ -868,6 +872,45 @@ def main():
|
|
|
for c in saddle_card("fake-saddle")["capabilities"]),
|
|
|
"deleting a capability drops it from saddles")
|
|
|
|
|
|
+ print("== B4: required capabilities on the create form ==")
|
|
|
+ tasks_state = slot_public(auth, "/tasks", "tasks")
|
|
|
+ gpu_option = [o for o in tasks_state.get("capability_options", [])
|
|
|
+ if o["name"] == "gpu"][0]
|
|
|
+ check(len(tasks_state.get("capability_options", [])) >= 1,
|
|
|
+ "queue page offers capability options in the create form")
|
|
|
+
|
|
|
+ conn = db()
|
|
|
+ before = conn.execute("select count(*) from tasks").fetchone()[0]
|
|
|
+ conn.close()
|
|
|
+ act(auth, tasks_state["create"], {
|
|
|
+ "project_id": "1",
|
|
|
+ "title": "Bad cap task",
|
|
|
+ "capability_id": [gpu_option["id"], "999"],
|
|
|
+ })
|
|
|
+ conn = db()
|
|
|
+ after = conn.execute("select count(*) from tasks").fetchone()[0]
|
|
|
+ conn.close()
|
|
|
+ check(after == before, "unknown capability id rejects the create")
|
|
|
+
|
|
|
+ act(auth, tasks_state["create"], {
|
|
|
+ "project_id": "1",
|
|
|
+ "title": "Cap-on-create task",
|
|
|
+ "capability_id": [gpu_option["id"]],
|
|
|
+ })
|
|
|
+ cap_create = int([t["id"] for t in slot_public(auth, "/tasks", "tasks")["tasks"]
|
|
|
+ if t["title"] == "Cap-on-create task"][0])
|
|
|
+ detail = task_state(auth, cap_create)
|
|
|
+ check([c["name"] for c in detail["capabilities"]] == ["gpu"],
|
|
|
+ "capabilities from the create form land on the task")
|
|
|
+ queue = [t for t in slot_public(auth, "/tasks", "tasks")["tasks"]
|
|
|
+ if int(t["id"]) == cap_create][0]
|
|
|
+ check(queue["capabilities"] == ["gpu"],
|
|
|
+ "queue row shows the created task's requirements")
|
|
|
+ # The waiting flag must hold it back until the capable saddle frees
|
|
|
+ # up (the fake saddle still carries gpu from the routing checks).
|
|
|
+ check(queue.get("waiting_for_saddle") is not True,
|
|
|
+ "capable saddle available: no wait flag on the new task")
|
|
|
+
|
|
|
print("== retry + resume ==")
|
|
|
# A daemon-side failure fails the task with its explanation.
|
|
|
fake.fail_next = True
|