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:
parent
2692e8138c
commit
3a453eaad7
@ -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
|
||||
|
@ -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",
|
||||
]
|
||||
|
@ -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":
|
||||
|
@ -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"])
|
||||
|
@ -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")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user