Add /compose/info route to retrieve details about a compose
The results is a JSON string with the following information: * id - The uuid of the comoposition * config - containing the configuration settings used to run Anaconda * recipe - The depsolved recipe used to generate the kickstart * commit - The (local) git commit hash for the recipe 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)
This commit is contained in:
parent
d3a9ec3002
commit
ed03ac7524
@ -41,6 +41,7 @@ from uuid import uuid4
|
||||
|
||||
from pylorax.api.projects import projects_depsolve, dep_nevra
|
||||
from pylorax.api.projects import ProjectsError
|
||||
from pylorax.api.recipes import read_recipe_and_id
|
||||
from pylorax.imgutils import default_image_name
|
||||
from pylorax.sysutils import joinpaths
|
||||
|
||||
@ -75,7 +76,7 @@ def repo_to_ks(r, url="url"):
|
||||
|
||||
return cmd
|
||||
|
||||
def start_build(cfg, yumlock, recipe, compose_type):
|
||||
def start_build(cfg, yumlock, gitlock, branch, recipe_name, compose_type):
|
||||
""" Start the build
|
||||
|
||||
:param cfg: Configuration object
|
||||
@ -96,6 +97,9 @@ def start_build(cfg, yumlock, recipe, compose_type):
|
||||
if compose_type not in compose_types(share_dir):
|
||||
raise RuntimeError("Invalid compose type (%s), must be one of %s" % (compose_type, compose_types(share_dir)))
|
||||
|
||||
with gitlock.lock:
|
||||
(commit_id, recipe) = read_recipe_and_id(gitlock.repo, branch, recipe_name)
|
||||
|
||||
# Combine modules and packages and depsolve the list
|
||||
# TODO include the version/glob in the depsolving
|
||||
module_names = map(lambda m: m["name"], recipe["modules"] or [])
|
||||
@ -114,6 +118,11 @@ def start_build(cfg, yumlock, recipe, compose_type):
|
||||
results_dir = joinpaths(lib_dir, "results", build_id)
|
||||
os.makedirs(results_dir)
|
||||
|
||||
# Write the recipe commit hash
|
||||
commit_path = joinpaths(results_dir, "COMMIT")
|
||||
with open(commit_path, "w") as f:
|
||||
f.write(commit_id)
|
||||
|
||||
# Write the original recipe
|
||||
recipe_path = joinpaths(results_dir, "recipe.toml")
|
||||
with open(recipe_path, "w") as f:
|
||||
@ -131,10 +140,9 @@ def start_build(cfg, yumlock, recipe, compose_type):
|
||||
ks_template = open(ks_template_path, "r").read()
|
||||
|
||||
# Write out the dependencies to the results dir
|
||||
deps_path = joinpaths(results_dir, "deps.txt")
|
||||
deps_path = joinpaths(results_dir, "deps.toml")
|
||||
with open(deps_path, "w") as f:
|
||||
for d in deps:
|
||||
f.write(dep_nevra(d)+"\n")
|
||||
f.write(toml.dumps({"packages":deps}).encode("UTF-8"))
|
||||
|
||||
# Create the final kickstart with repos and package list
|
||||
ks_path = joinpaths(results_dir, "final-kickstart.ks")
|
||||
|
@ -122,6 +122,17 @@ def make_compose(cfg, results_dir):
|
||||
log.debug("Install finished, chowning results to %s:%s", user, group)
|
||||
subprocess.call(["chown", "-R", "%s:%s" % (user, group), results_dir])
|
||||
|
||||
def get_compose_type(results_dir):
|
||||
""" Return the type of composition.
|
||||
|
||||
"""
|
||||
# Should only be 2 kickstarts, the final-kickstart.ks and the template
|
||||
t = [os.path.basename(ks)[:-3] for ks in glob(joinpaths(results_dir, "*.ks"))
|
||||
if "final-kickstart" not in ks]
|
||||
if len(t) != 1:
|
||||
raise RuntimeError("Cannot find ks template for build %s" % os.path.basename(results_dir))
|
||||
return t[0]
|
||||
|
||||
def compose_detail(results_dir):
|
||||
""" Return details about the build."""
|
||||
|
||||
@ -134,17 +145,14 @@ def compose_detail(results_dir):
|
||||
mtime = os.stat(joinpaths(results_dir, "STATUS")).st_mtime
|
||||
recipe = recipe_from_file(joinpaths(results_dir, "recipe.toml"))
|
||||
|
||||
# Should only be 2 kickstarts, the final-kickstart.ks and the template
|
||||
types = [os.path.basename(ks)[:-3] for ks in glob(joinpaths(results_dir, "*.ks"))
|
||||
if "final-kickstart" not in ks]
|
||||
if len(types) != 1:
|
||||
raise RuntimeError("Cannot find ks template for build %s" % build_id)
|
||||
compose_type = get_compose_type(results_dir)
|
||||
|
||||
return {"id": build_id,
|
||||
"status": status,
|
||||
"timestamp":mtime,
|
||||
"recipe": recipe["name"],
|
||||
"version": recipe["version"]
|
||||
return {"id": build_id,
|
||||
"queue_status": status,
|
||||
"timestamp": mtime,
|
||||
"compose_type": compose_type,
|
||||
"recipe": recipe["name"],
|
||||
"version": recipe["version"]
|
||||
}
|
||||
|
||||
def queue_status(cfg):
|
||||
@ -218,3 +226,58 @@ def uuid_delete(cfg, uuid):
|
||||
raise RuntimeError("Directory length is too short: %s" % uuid_dir)
|
||||
shutil.rmtree(uuid_dir)
|
||||
return True
|
||||
|
||||
def uuid_info(cfg, uuid):
|
||||
"""Return information about the composition
|
||||
|
||||
:param cfg: Configuration settings
|
||||
:type cfg: ComposerConfig
|
||||
:param uuid: The UUID of the build
|
||||
:type uuid: str
|
||||
:returns: dictionary of information about the composition
|
||||
:rtype: dict
|
||||
|
||||
This will return a dict with the following fields populated:
|
||||
|
||||
* id - The uuid of the comoposition
|
||||
* config - containing the configuration settings used to run Anaconda
|
||||
* recipe - The depsolved recipe used to generate the kickstart
|
||||
* commit - The (local) git commit hash for the recipe 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)
|
||||
"""
|
||||
uuid_dir = joinpaths(cfg.get("composer", "lib_dir"), "results", uuid)
|
||||
if not os.path.exists(uuid_dir):
|
||||
raise RuntimeError("%s is not a valid build_id" % uuid)
|
||||
|
||||
# Load the compose configuration
|
||||
cfg_path = joinpaths(uuid_dir, "config.toml")
|
||||
if not os.path.exists(cfg_path):
|
||||
raise RuntimeError("Missing config.toml for %s" % uuid)
|
||||
cfg_dict = toml.loads(open(cfg_path, "r").read())
|
||||
|
||||
frozen_path = joinpaths(uuid_dir, "frozen.toml")
|
||||
if not os.path.exists(frozen_path):
|
||||
raise RuntimeError("Missing frozen.toml for %s" % uuid)
|
||||
frozen_dict = toml.loads(open(frozen_path, "r").read())
|
||||
|
||||
deps_path = joinpaths(uuid_dir, "deps.toml")
|
||||
if not os.path.exists(deps_path):
|
||||
raise RuntimeError("Missing deps.toml for %s" % uuid)
|
||||
deps_dict = toml.loads(open(deps_path, "r").read())
|
||||
|
||||
compose_type = get_compose_type(uuid_dir)
|
||||
status = open(joinpaths(uuid_dir, "STATUS")).read().strip()
|
||||
|
||||
commit_path = joinpaths(uuid_dir, "COMMIT")
|
||||
commit_id = open(commit_path, "r").read().strip()
|
||||
|
||||
return {"id": uuid,
|
||||
"config": cfg_dict,
|
||||
"recipe": frozen_dict,
|
||||
"commit": commit_id,
|
||||
"deps": deps_dict,
|
||||
"compose_type": compose_type,
|
||||
"queue_status": status
|
||||
}
|
||||
|
@ -736,6 +736,54 @@ DELETE `/api/v0/compose/delete/<uuids>`
|
||||
]
|
||||
}
|
||||
|
||||
`/api/v0/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
|
||||
* recipe - The depsolved recipe used to generate the kickstart
|
||||
* commit - The (local) git commit hash for the recipe 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",
|
||||
"recipe": {
|
||||
"description": "An example kubernetes master",
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
import logging
|
||||
@ -747,7 +795,7 @@ from pylorax.api.compose import start_build, compose_types
|
||||
from pylorax.api.crossdomain import crossdomain
|
||||
from pylorax.api.projects import projects_list, projects_info, projects_depsolve
|
||||
from pylorax.api.projects import modules_list, modules_info, ProjectsError
|
||||
from pylorax.api.queue import queue_status, build_status, uuid_delete, uuid_status
|
||||
from pylorax.api.queue import queue_status, build_status, uuid_delete, uuid_status, uuid_info
|
||||
from pylorax.api.recipes import list_branch_files, read_recipe_commit, recipe_filename, list_commits
|
||||
from pylorax.api.recipes import recipe_from_dict, recipe_from_toml, commit_recipe, delete_recipe, revert_recipe
|
||||
from pylorax.api.recipes import tag_recipe_commit, recipe_diff
|
||||
@ -1231,15 +1279,9 @@ def v0_api(api):
|
||||
if errors:
|
||||
return jsonify(status=False, error={"msg":"\n".join(errors)}), 400
|
||||
|
||||
# Get the git version (if it exists)
|
||||
try:
|
||||
with api.config["GITLOCK"].lock:
|
||||
recipe = read_recipe_commit(api.config["GITLOCK"].repo, branch, recipe_name)
|
||||
except Exception as e:
|
||||
log.error("Problem reading recipe %s: %s", recipe_name, str(e))
|
||||
return jsonify(status=False, error={"msg":str(e)}), 400
|
||||
try:
|
||||
build_id = start_build(api.config["COMPOSER_CFG"], api.config["YUMLOCK"], recipe, compose_type)
|
||||
build_id = start_build(api.config["COMPOSER_CFG"], api.config["YUMLOCK"], api.config["GITLOCK"],
|
||||
branch, recipe_name, compose_type)
|
||||
except Exception as e:
|
||||
return jsonify(status=False, error={"msg":str(e)}), 400
|
||||
|
||||
@ -1303,3 +1345,14 @@ def v0_api(api):
|
||||
else:
|
||||
results.append({"uuid":uuid, "status":True})
|
||||
return jsonify(uuids=results, errors=errors)
|
||||
|
||||
@api.route("/api/v0/compose/info/<uuid>")
|
||||
@crossdomain(origin="*")
|
||||
def v0_compose_info(uuid):
|
||||
"""Return detailed info about a compose"""
|
||||
try:
|
||||
info = uuid_info(api.config["COMPOSER_CFG"], uuid)
|
||||
except Exception as e:
|
||||
return jsonify(status=False, msg=str(e))
|
||||
|
||||
return jsonify(**info)
|
||||
|
Loading…
Reference in New Issue
Block a user