Make sure V0 API doesn't return uploads information

uploads should only be included in the V1 API routes, add `api`
selection to the relevant helper functions and calls to them from v0.py

Add new V1 routes with api=1 to include the uploads information in the
results.

Also add tests to ensure that V0 requests do not include uploads.
This commit is contained in:
Brian C. Lane 2019-08-27 14:13:01 -07:00
parent 2692e8138c
commit 3a453eaad7
5 changed files with 373 additions and 49 deletions

View File

@ -305,11 +305,15 @@ def get_compose_type(results_dir):
raise RuntimeError("Cannot find ks template for build %s" % os.path.basename(results_dir))
return t[0]
def compose_detail(cfg, results_dir):
def compose_detail(cfg, results_dir, api=1):
"""Return details about the build.
:param cfg: Configuration settings (required for api=1)
:type cfg: ComposerConfig
:param results_dir: The directory containing the metadata and results for the build
:type results_dir: str
:param api: Select which api version of the dict to return (default 1)
:type api: int
:returns: A dictionary with details about the compose
:rtype: dict
:raises: IOError if it cannot read the directory, STATUS, or blueprint file.
@ -322,6 +326,7 @@ def compose_detail(cfg, results_dir):
* blueprint - Blueprint name
* version - Blueprint version
* image_size - Size of the image, if finished. 0 otherwise.
* uploads - For API v1 details about uploading the image are included
Various timestamps are also included in the dict. These are all Unix UTC timestamps.
It is possible for these timestamps to not always exist, in which case they will be
@ -345,26 +350,31 @@ def compose_detail(cfg, results_dir):
times = timestamp_dict(results_dir)
upload_uuids = uuid_get_uploads(cfg, build_id)
summaries = [upload.summary() for upload in get_uploads(cfg["upload"], upload_uuids)]
detail = {"id": build_id,
"queue_status": status,
"job_created": times.get(TS_CREATED),
"job_started": times.get(TS_STARTED),
"job_finished": times.get(TS_FINISHED),
"compose_type": compose_type,
"blueprint": blueprint["name"],
"version": blueprint["version"],
"image_size": image_size,
}
return {"id": build_id,
"queue_status": status,
"job_created": times.get(TS_CREATED),
"job_started": times.get(TS_STARTED),
"job_finished": times.get(TS_FINISHED),
"compose_type": compose_type,
"blueprint": blueprint["name"],
"version": blueprint["version"],
"image_size": image_size,
"uploads": summaries,
}
if api == 1:
# Get uploads for this build_id
upload_uuids = uuid_get_uploads(cfg, build_id)
summaries = [upload.summary() for upload in get_uploads(cfg["upload"], upload_uuids)]
detail["uploads"] = summaries
return detail
def queue_status(cfg):
def queue_status(cfg, api=1):
"""Return details about what is in the queue.
:param cfg: Configuration settings
:type cfg: ComposerConfig
:param api: Select which api version of the dict to return (default 1)
:type api: int
:returns: A list of the new composes, and a list of the running composes
:rtype: dict
@ -378,7 +388,7 @@ def queue_status(cfg):
new_details = []
for n in new_queue:
try:
d = compose_detail(cfg, n)
d = compose_detail(cfg, n, api)
except IOError:
continue
new_details.append(d)
@ -386,7 +396,7 @@ def queue_status(cfg):
run_details = []
for r in run_queue:
try:
d = compose_detail(cfg, r)
d = compose_detail(cfg, r, api)
except IOError:
continue
run_details.append(d)
@ -396,32 +406,36 @@ def queue_status(cfg):
"run": run_details
}
def uuid_status(cfg, uuid):
def uuid_status(cfg, uuid, api=1):
"""Return the details of a specific UUID compose
:param cfg: Configuration settings
:type cfg: ComposerConfig
:param uuid: The UUID of the build
:type uuid: str
:param api: Select which api version of the dict to return (default 1)
:type api: int
:returns: Details about the build
:rtype: dict or None
Returns the same dict as `compose_details()`
Returns the same dict as `compose_detail()`
"""
uuid_dir = joinpaths(cfg.get("composer", "lib_dir"), "results", uuid)
try:
return compose_detail(cfg, uuid_dir)
return compose_detail(cfg, uuid_dir, api)
except IOError:
return None
def build_status(cfg, status_filter=None):
def build_status(cfg, status_filter=None, api=1):
"""Return the details of finished or failed builds
:param cfg: Configuration settings
:type cfg: ComposerConfig
:param status_filter: What builds to return. None == all, "FINISHED", or "FAILED"
:type status_filter: str
:returns: A list of the build details (from compose_details)
:param api: Select which api version of the dict to return (default 1)
:type api: int
:returns: A list of the build details (from compose_detail)
:rtype: list of dicts
This returns a list of build details for each of the matching builds on the
@ -441,7 +455,7 @@ def build_status(cfg, status_filter=None):
try:
status = open(joinpaths(build, "STATUS"), "r").read().strip()
if status in status_filter:
results.append(compose_detail(cfg, build))
results.append(compose_detail(cfg, build, api))
except IOError:
pass
return results
@ -573,7 +587,7 @@ def uuid_delete(cfg, uuid):
shutil.rmtree(uuid_dir)
return True
def uuid_info(cfg, uuid):
def uuid_info(cfg, uuid, api=1):
"""Return information about the composition
:param cfg: Configuration settings
@ -614,17 +628,14 @@ def uuid_info(cfg, uuid):
raise RuntimeError("Missing deps.toml for %s" % uuid)
deps_dict = toml.loads(open(deps_path, "r").read())
details = compose_detail(cfg, uuid_dir)
details = compose_detail(cfg, uuid_dir, api)
commit_path = joinpaths(uuid_dir, "COMMIT")
if not os.path.exists(commit_path):
raise RuntimeError("Missing commit hash for %s" % uuid)
commit_id = open(commit_path, "r").read().strip()
upload_uuids = uuid_get_uploads(cfg, uuid)
summaries = [upload.summary() for upload in get_uploads(cfg["upload"], upload_uuids)]
return {"id": uuid,
info = {"id": uuid,
"config": cfg_dict,
"blueprint": frozen_dict,
"commit": commit_id,
@ -632,8 +643,12 @@ def uuid_info(cfg, uuid):
"compose_type": details["compose_type"],
"queue_status": details["queue_status"],
"image_size": details["image_size"],
"uploads": summaries,
}
if api == 1:
upload_uuids = uuid_get_uploads(cfg, uuid)
summaries = [upload.summary() for upload in get_uploads(cfg["upload"], upload_uuids)]
info["uploads"] = summaries
return info
def uuid_tar(cfg, uuid, metadata=False, image=False, logs=False):
"""Return a tar of the build data

View File

@ -91,6 +91,11 @@ server.register_blueprint(v0_api, url_prefix="/api/v0/")
# Use v0 routes by default
skip_rules = [
"/compose",
"/compose/queue",
"/compose/finished",
"/compose/failed",
"/compose/status/<uuids>",
"/compose/info/<uuid>",
"/projects/source/info/<source_names>",
"/projects/source/new",
]

View File

@ -1537,7 +1537,7 @@ def v0_compose_queue():
]
}
"""
return jsonify(queue_status(api.config["COMPOSER_CFG"]))
return jsonify(queue_status(api.config["COMPOSER_CFG"], api=0))
@v0_api.route("/compose/finished")
def v0_compose_finished():
@ -1572,7 +1572,7 @@ def v0_compose_finished():
]
}
"""
return jsonify(finished=build_status(api.config["COMPOSER_CFG"], "FINISHED"))
return jsonify(finished=build_status(api.config["COMPOSER_CFG"], "FINISHED", api=0))
@v0_api.route("/compose/failed")
def v0_compose_failed():
@ -1598,7 +1598,7 @@ def v0_compose_failed():
]
}
"""
return jsonify(failed=build_status(api.config["COMPOSER_CFG"], "FAILED"))
return jsonify(failed=build_status(api.config["COMPOSER_CFG"], "FAILED", api=0))
@v0_api.route("/compose/status", defaults={'uuids': ""})
@v0_api.route("/compose/status/<uuids>")
@ -1647,14 +1647,14 @@ def v0_compose_status(uuids):
errors = []
if uuids.strip() == '*':
queue_status_dict = queue_status(api.config["COMPOSER_CFG"])
queue_status_dict = queue_status(api.config["COMPOSER_CFG"], api=0)
queue_new = queue_status_dict["new"]
queue_running = queue_status_dict["run"]
candidates = queue_new + queue_running + build_status(api.config["COMPOSER_CFG"])
candidates = queue_new + queue_running + build_status(api.config["COMPOSER_CFG"], api=0)
else:
candidates = []
for uuid in [n.strip().lower() for n in uuids.split(",")]:
details = uuid_status(api.config["COMPOSER_CFG"], uuid)
details = uuid_status(api.config["COMPOSER_CFG"], uuid, api=0)
if details is None:
errors.append({"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid})
else:
@ -1695,7 +1695,7 @@ def v0_compose_cancel(uuid):
if VALID_API_STRING.match(uuid) is None:
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
status = uuid_status(api.config["COMPOSER_CFG"], uuid)
status = uuid_status(api.config["COMPOSER_CFG"], uuid, api=0)
if status is None:
return jsonify(status=False, errors=[{"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}]), 400
@ -1737,7 +1737,7 @@ def v0_compose_delete(uuids):
results = []
errors = []
for uuid in [n.strip().lower() for n in uuids.split(",")]:
status = uuid_status(api.config["COMPOSER_CFG"], uuid)
status = uuid_status(api.config["COMPOSER_CFG"], uuid, api=0)
if status is None:
errors.append({"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid})
elif status["queue_status"] not in ["FINISHED", "FAILED"]:
@ -1806,7 +1806,7 @@ def v0_compose_info(uuid):
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
try:
info = uuid_info(api.config["COMPOSER_CFG"], uuid)
info = uuid_info(api.config["COMPOSER_CFG"], uuid, api=0)
except Exception as e:
return jsonify(status=False, errors=[{"id": COMPOSE_ERROR, "msg": str(e)}]), 400
@ -1835,7 +1835,7 @@ def v0_compose_metadata(uuid):
if VALID_API_STRING.match(uuid) is None:
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
status = uuid_status(api.config["COMPOSER_CFG"], uuid)
status = uuid_status(api.config["COMPOSER_CFG"], uuid, api=0)
if status is None:
return jsonify(status=False, errors=[{"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}]), 400
if status["queue_status"] not in ["FINISHED", "FAILED"]:
@ -1865,7 +1865,7 @@ def v0_compose_results(uuid):
if VALID_API_STRING.match(uuid) is None:
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
status = uuid_status(api.config["COMPOSER_CFG"], uuid)
status = uuid_status(api.config["COMPOSER_CFG"], uuid, api=0)
if status is None:
return jsonify(status=False, errors=[{"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}]), 400
elif status["queue_status"] not in ["FINISHED", "FAILED"]:
@ -1893,7 +1893,7 @@ def v0_compose_logs(uuid):
if VALID_API_STRING.match(uuid) is None:
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
status = uuid_status(api.config["COMPOSER_CFG"], uuid)
status = uuid_status(api.config["COMPOSER_CFG"], uuid, api=0)
if status is None:
return jsonify(status=False, errors=[{"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}]), 400
elif status["queue_status"] not in ["FINISHED", "FAILED"]:
@ -1918,7 +1918,7 @@ def v0_compose_image(uuid):
if VALID_API_STRING.match(uuid) is None:
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
status = uuid_status(api.config["COMPOSER_CFG"], uuid)
status = uuid_status(api.config["COMPOSER_CFG"], uuid, api=0)
if status is None:
return jsonify(status=False, errors=[{"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}]), 400
elif status["queue_status"] not in ["FINISHED", "FAILED"]:
@ -1975,7 +1975,7 @@ def v0_compose_log_tail(uuid):
except ValueError as e:
return jsonify(status=False, errors=[{"id": COMPOSE_ERROR, "msg": str(e)}]), 400
status = uuid_status(api.config["COMPOSER_CFG"], uuid)
status = uuid_status(api.config["COMPOSER_CFG"], uuid, api=0)
if status is None:
return jsonify(status=False, errors=[{"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}]), 400
elif status["queue_status"] == "WAITING":

View File

@ -29,8 +29,10 @@ from pylorax.api.checkparams import checkparams
from pylorax.api.compose import start_build
from pylorax.api.errors import BAD_COMPOSE_TYPE, BUILD_FAILED, INVALID_CHARS, MISSING_POST, PROJECTS_ERROR
from pylorax.api.errors import SYSTEM_SOURCE, UNKNOWN_BLUEPRINT, UNKNOWN_SOURCE, UNKNOWN_UUID, UPLOAD_ERROR
from pylorax.api.errors import COMPOSE_ERROR
from pylorax.api.flask_blueprint import BlueprintSkip
from pylorax.api.queue import uuid_status, uuid_schedule_upload, uuid_remove_upload
from pylorax.api.queue import queue_status, build_status, uuid_status, uuid_schedule_upload, uuid_remove_upload
from pylorax.api.queue import uuid_info
from pylorax.api.projects import get_repo_sources, repo_to_source
from pylorax.api.projects import new_repo_source
from pylorax.api.regexes import VALID_API_STRING, VALID_BLUEPRINT_NAME
@ -115,7 +117,7 @@ def v1_projects_source_info(source_ids):
def v1_projects_source_new():
"""Add a new package source. Or change an existing one
**POST /api/v0/projects/source/new**
**POST /api/v1/projects/source/new**
Add (or change) a source for use when depsolving blueprints and composing images.
@ -184,7 +186,7 @@ def v1_compose_start():
compose_type - The type of output to create, from /compose/types
branch - Optional, defaults to master, selects the git branch to use for the blueprint.
**POST /api/v0/compose**
**POST /api/v1/compose**
Start a compose. The content type should be 'application/json' and the body of the POST
should look like this. The "upload" object is optional.
@ -212,7 +214,7 @@ def v1_compose_start():
}
Pass it the name of the blueprint, the type of output (from
'/api/v0/compose/types'), and the blueprint branch to use. 'branch' is
'/api/v1/compose/types'), and the blueprint branch to use. 'branch' is
optional and will default to master. It will create a new build and add
it to the queue. It returns the build uuid and a status if it succeeds.
If an "upload" is given, it will schedule an upload to run when the build
@ -222,6 +224,7 @@ def v1_compose_start():
{
"build_id": "e6fa6db4-9c81-4b70-870f-a697ca405cdf",
"upload_uuid": "572eb0d0-5348-4600-9666-14526ba628bb",
"status": true
}
"""
@ -294,8 +297,251 @@ def v1_compose_start():
image_name,
settings
)
else:
upload_uuid = ""
return jsonify(status=True, build_id=build_id)
return jsonify(status=True, build_id=build_id, upload_id=upload_uuid)
@v1_api.route("/compose/queue")
def v1_compose_queue():
"""Return the status of the new and running queues
**/api/v1/compose/queue**
Return the status of the build queue. It includes information about the builds waiting,
and the build that is running.
Example::
{
"new": [
{
"id": "45502a6d-06e8-48a5-a215-2b4174b3614b",
"blueprint": "glusterfs",
"queue_status": "WAITING",
"job_created": 1517362647.4570868,
"version": "0.0.6"
},
{
"id": "6d292bd0-bec7-4825-8d7d-41ef9c3e4b73",
"blueprint": "kubernetes",
"queue_status": "WAITING",
"job_created": 1517362659.0034983,
"version": "0.0.1"
}
],
"run": [
{
"id": "745712b2-96db-44c0-8014-fe925c35e795",
"blueprint": "glusterfs",
"queue_status": "RUNNING",
"job_created": 1517362633.7965999,
"job_started": 1517362633.8001345,
"version": "0.0.6"
}
]
}
"""
return jsonify(queue_status(api.config["COMPOSER_CFG"], api=1))
@v1_api.route("/compose/finished")
def v1_compose_finished():
"""Return the list of finished composes
**/api/v1/compose/finished**
Return the details on all of the finished composes on the system.
Example::
{
"finished": [
{
"id": "70b84195-9817-4b8a-af92-45e380f39894",
"blueprint": "glusterfs",
"queue_status": "FINISHED",
"job_created": 1517351003.8210032,
"job_started": 1517351003.8230415,
"job_finished": 1517359234.1003145,
"version": "0.0.6"
},
{
"id": "e695affd-397f-4af9-9022-add2636e7459",
"blueprint": "glusterfs",
"queue_status": "FINISHED",
"job_created": 1517362289.7193348,
"job_started": 1517362289.9751132,
"job_finished": 1517363500.1234567,
"version": "0.0.6"
}
]
}
"""
return jsonify(finished=build_status(api.config["COMPOSER_CFG"], "FINISHED", api=1))
@v1_api.route("/compose/failed")
def v1_compose_failed():
"""Return the list of failed composes
**/api/v1/compose/failed**
Return the details on all of the failed composes on the system.
Example::
{
"failed": [
{
"id": "8c8435ef-d6bd-4c68-9bf1-a2ef832e6b1a",
"blueprint": "http-server",
"queue_status": "FAILED",
"job_created": 1517523249.9301329,
"job_started": 1517523249.9314211,
"job_finished": 1517523255.5623411,
"version": "0.0.2"
}
]
}
"""
return jsonify(failed=build_status(api.config["COMPOSER_CFG"], "FAILED", api=1))
@v1_api.route("/compose/status", defaults={'uuids': ""})
@v1_api.route("/compose/status/<uuids>")
@checkparams([("uuids", "", "no UUIDs given")])
def v1_compose_status(uuids):
"""Return the status of the listed uuids
**/api/v1/compose/status/<uuids>[?blueprint=<blueprint_name>&status=<compose_status>&type=<compose_type>]**
Return the details for each of the comma-separated list of uuids. A uuid of '*' will return
details for all composes.
Example::
{
"uuids": [
{
"id": "8c8435ef-d6bd-4c68-9bf1-a2ef832e6b1a",
"blueprint": "http-server",
"queue_status": "FINISHED",
"job_created": 1517523644.2384307,
"job_started": 1517523644.2551234,
"job_finished": 1517523689.9864314,
"version": "0.0.2"
},
{
"id": "45502a6d-06e8-48a5-a215-2b4174b3614b",
"blueprint": "glusterfs",
"queue_status": "FINISHED",
"job_created": 1517363442.188399,
"job_started": 1517363442.325324,
"job_finished": 1517363451.653621,
"version": "0.0.6"
}
]
}
"""
if VALID_API_STRING.match(uuids) is None:
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
blueprint = request.args.get("blueprint", None)
status = request.args.get("status", None)
compose_type = request.args.get("type", None)
results = []
errors = []
if uuids.strip() == '*':
queue_status_dict = queue_status(api.config["COMPOSER_CFG"], api=1)
queue_new = queue_status_dict["new"]
queue_running = queue_status_dict["run"]
candidates = queue_new + queue_running + build_status(api.config["COMPOSER_CFG"], api=1)
else:
candidates = []
for uuid in [n.strip().lower() for n in uuids.split(",")]:
details = uuid_status(api.config["COMPOSER_CFG"], uuid, api=1)
if details is None:
errors.append({"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid})
else:
candidates.append(details)
for details in candidates:
if blueprint is not None and details['blueprint'] != blueprint:
continue
if status is not None and details['queue_status'] != status:
continue
if compose_type is not None and details['compose_type'] != compose_type:
continue
results.append(details)
return jsonify(uuids=results, errors=errors)
@v1_api.route("/compose/info", defaults={'uuid': ""})
@v1_api.route("/compose/info/<uuid>")
@checkparams([("uuid", "", "no UUID given")])
def v1_compose_info(uuid):
"""Return detailed info about a compose
**/api/v1/compose/info/<uuid>**
Get detailed information about the compose. The returned JSON string will
contain the following information:
* id - The uuid of the comoposition
* config - containing the configuration settings used to run Anaconda
* blueprint - The depsolved blueprint used to generate the kickstart
* commit - The (local) git commit hash for the blueprint used
* deps - The NEVRA of all of the dependencies used in the composition
* compose_type - The type of output generated (tar, iso, etc.)
* queue_status - The final status of the composition (FINISHED or FAILED)
Example::
{
"commit": "7078e521a54b12eae31c3fd028680da7a0815a4d",
"compose_type": "tar",
"config": {
"anaconda_args": "",
"armplatform": "",
"compress_args": [],
"compression": "xz",
"image_name": "root.tar.xz",
...
},
"deps": {
"packages": [
{
"arch": "x86_64",
"epoch": "0",
"name": "acl",
"release": "14.el7",
"version": "2.2.51"
}
]
},
"id": "c30b7d80-523b-4a23-ad52-61b799739ce8",
"queue_status": "FINISHED",
"blueprint": {
"description": "An example kubernetes master",
...
}
}
"""
if VALID_API_STRING.match(uuid) is None:
return jsonify(status=False, errors=[{"id": INVALID_CHARS, "msg": "Invalid characters in API path"}]), 400
try:
info = uuid_info(api.config["COMPOSER_CFG"], uuid, api=1)
except Exception as e:
return jsonify(status=False, errors=[{"id": COMPOSE_ERROR, "msg": str(e)}]), 400
if info is None:
return jsonify(status=False, errors=[{"id": UNKNOWN_UUID, "msg": "%s is not a valid build uuid" % uuid}]), 400
else:
return jsonify(**info)
@v1_api.route("/compose/uploads/schedule", defaults={'compose_uuid': ""}, methods=["POST"])
@v1_api.route("/compose/uploads/schedule/<compose_uuid>", methods=["POST"])

View File

@ -1252,6 +1252,10 @@ class ServerTestCase(unittest.TestCase):
ids = [e["id"] for e in data["new"] + data["run"]]
self.assertEqual(build_id in ids, True, "Failed to add build to the queue")
# V0 API should *not* have the uploads details in the results
uploads = any("uploads" in e for e in data["new"] + data["run"])
self.assertFalse(uploads, "V0 API should not include 'uploads' field")
# Wait for it to start
self.assertEqual(_wait_for_status(self, build_id, ["RUNNING"]), True, "Failed to start test compose")
@ -1270,6 +1274,11 @@ class ServerTestCase(unittest.TestCase):
ids = [e["id"] for e in data["failed"]]
self.assertEqual(build_id in ids, True, "Failed build not listed by /compose/failed")
# V0 API should *not* have the uploads details in the results
print(data)
uploads = any("uploads" in e for e in data["failed"])
self.assertFalse(uploads, "V0 API should not include 'uploads' field")
# Test the /api/v0/compose/finished route
resp = self.server.get("/api/v0/compose/finished")
data = json.loads(resp.data)
@ -1283,6 +1292,11 @@ class ServerTestCase(unittest.TestCase):
ids = [(e["id"], e["queue_status"]) for e in data["uuids"]]
self.assertEqual((build_id, "FAILED") in ids, True, "Failed build not listed by /compose/status")
# V0 API should *not* have the uploads details in the results
print(data)
uploads = any("uploads" in e for e in data["uuids"])
self.assertFalse(uploads, "V0 API should not include 'uploads' field")
# Test the /api/v0/compose/cancel/<uuid> route
resp = self.server.post("/api/v0/compose?test=1",
data=json.dumps(test_compose),
@ -1338,6 +1352,10 @@ class ServerTestCase(unittest.TestCase):
ids = [e["id"] for e in data["new"] + data["run"]]
self.assertEqual(build_id in ids, True, "Failed to add build to the queue")
# V0 API should *not* have the uploads details in the results
uploads = any("uploads" in e for e in data["new"] + data["run"])
self.assertFalse(uploads, "V0 API should not include 'uploads' field")
# Wait for it to start
self.assertEqual(_wait_for_status(self, build_id, ["RUNNING"]), True, "Failed to start test compose")
@ -1356,6 +1374,10 @@ class ServerTestCase(unittest.TestCase):
ids = [e["id"] for e in data["finished"]]
self.assertEqual(build_id in ids, True, "Finished build not listed by /compose/finished")
# V0 API should *not* have the uploads details in the results
uploads = any("uploads" in e for e in data["finished"])
self.assertFalse(uploads, "V0 API should not include 'uploads' field")
# Test the /api/v0/compose/failed route
resp = self.server.get("/api/v0/compose/failed")
data = json.loads(resp.data)
@ -1369,6 +1391,10 @@ class ServerTestCase(unittest.TestCase):
ids = [(e["id"], e["queue_status"]) for e in data["uuids"]]
self.assertEqual((build_id, "FINISHED") in ids, True, "Finished build not listed by /compose/status")
# V0 API should *not* have the uploads details in the results
uploads = any("uploads" in e for e in data["uuids"])
self.assertFalse(uploads, "V0 API should not include 'uploads' field")
# Test the /api/v0/compose/metadata/<uuid> route
resp = self.server.get("/api/v0/compose/metadata/%s" % build_id)
self.assertEqual(resp.status_code, 200)
@ -1439,6 +1465,10 @@ class ServerTestCase(unittest.TestCase):
ids = [e["id"] for e in data["new"] + data["run"]]
self.assertEqual(build_id_fail in ids, True, "Failed to add build to the queue")
# V0 API should *not* have the uploads details in the results
uploads = any("uploads" in e for e in data["new"] + data["run"])
self.assertFalse(uploads, "V0 API should not include 'uploads' field")
# Wait for it to start
self.assertEqual(_wait_for_status(self, build_id_fail, ["RUNNING"]), True, "Failed to start test compose")
@ -1461,6 +1491,10 @@ class ServerTestCase(unittest.TestCase):
ids = [e["id"] for e in data["new"] + data["run"]]
self.assertEqual(build_id_success in ids, True, "Failed to add build to the queue")
# V0 API should *not* have the uploads details in the results
uploads = any("uploads" in e for e in data["new"] + data["run"])
self.assertFalse(uploads, "V0 API should not include 'uploads' field")
# Wait for it to start
self.assertEqual(_wait_for_status(self, build_id_success, ["RUNNING"]), True, "Failed to start test compose")
@ -1475,6 +1509,10 @@ class ServerTestCase(unittest.TestCase):
self.assertIn(build_id_success, ids, "Finished build not listed by /compose/status/*")
self.assertIn(build_id_fail, ids, "Failed build not listed by /compose/status/*")
# V0 API should *not* have the uploads details in the results
uploads = any("uploads" in e for e in data["uuids"])
self.assertFalse(uploads, "V0 API should not include 'uploads' field")
# Filter by name
resp = self.server.get("/api/v0/compose/status/*?blueprint=%s" % test_compose_fail["blueprint_name"])
data = json.loads(resp.data)
@ -1483,6 +1521,10 @@ class ServerTestCase(unittest.TestCase):
self.assertIn(build_id_fail, ids, "Failed build not listed by /compose/status blueprint filter")
self.assertNotIn(build_id_success, ids, "Finished build listed by /compose/status blueprint filter")
# V0 API should *not* have the uploads details in the results
uploads = any("uploads" in e for e in data["uuids"])
self.assertFalse(uploads, "V0 API should not include 'uploads' field")
# Filter by type
resp = self.server.get("/api/v0/compose/status/*?type=tar")
data = json.loads(resp.data)
@ -1491,6 +1533,10 @@ class ServerTestCase(unittest.TestCase):
self.assertIn(build_id_fail, ids, "Failed build not listed by /compose/status type filter")
self.assertIn(build_id_success, ids, "Finished build not listed by /compose/status type filter")
# V0 API should *not* have the uploads details in the results
uploads = any("uploads" in e for e in data["uuids"])
self.assertFalse(uploads, "V0 API should not include 'uploads' field")
resp = self.server.get("/api/v0/compose/status/*?type=snakes")
data = json.loads(resp.data)
self.assertNotEqual(data, None)
@ -1505,6 +1551,10 @@ class ServerTestCase(unittest.TestCase):
self.assertIn(build_id_fail, ids, "Failed build not listed by /compose/status status filter")
self.assertNotIn(build_id_success, "Finished build listed by /compose/status status filter")
# V0 API should *not* have the uploads details in the results
uploads = any("uploads" in e for e in data["uuids"])
self.assertFalse(uploads, "V0 API should not include 'uploads' field")
def test_compose_14_kernel_append(self):
"""Test the /api/v0/compose with kernel append customization"""
test_compose = {"blueprint_name": "example-append",
@ -1527,6 +1577,10 @@ class ServerTestCase(unittest.TestCase):
ids = [e["id"] for e in data["new"] + data["run"]]
self.assertEqual(build_id in ids, True, "Failed to add build to the queue")
# V0 API should *not* have the uploads details in the results
uploads = any("uploads" in e for e in data["new"] + data["run"])
self.assertFalse(uploads, "V0 API should not include 'uploads' field")
# Wait for it to start
self.assertEqual(_wait_for_status(self, build_id, ["RUNNING"]), True, "Failed to start test compose")
@ -2177,6 +2231,10 @@ class GitRPMBlueprintTestCase(unittest.TestCase):
ids = [e["id"] for e in data["new"] + data["run"]]
self.assertEqual(build_id in ids, True, "Failed to add build to the queue")
# V0 API should *not* have the uploads details in the results
uploads = any("uploads" in e for e in data["new"] + data["run"])
self.assertFalse(uploads, "V0 API should not include 'uploads' field")
# Wait for it to start
self.assertEqual(_wait_for_status(self, build_id, ["RUNNING"]), True, "Failed to start test compose")