From b99a7756de141014e2ea5cb07afbb7985c535449 Mon Sep 17 00:00:00 2001 From: "Brian C. Lane" Date: Thu, 1 Feb 2018 17:35:24 -0800 Subject: [PATCH] Add DELETE /compose/delete/ API route This will delete all of the build's results and cannot be undone. --- src/pylorax/api/queue.py | 26 +++++++++++++++++++++++++ src/pylorax/api/v0.py | 41 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/pylorax/api/queue.py b/src/pylorax/api/queue.py index 2729396d..5183e03e 100644 --- a/src/pylorax/api/queue.py +++ b/src/pylorax/api/queue.py @@ -18,8 +18,12 @@ import logging log = logging.getLogger("pylorax") import os +import grp from glob import glob import pytoml as toml +import pwd +import shutil +import subprocess import time from pykickstart.version import makeVersion, RHEL7 from pykickstart.parser import KickstartParser @@ -112,6 +116,12 @@ def make_compose(cfg, results_dir): log.debug("repo_url = %s, cfg = %s", repo_url, install_cfg) novirt_install(install_cfg, joinpaths(results_dir, install_cfg.image_name), None, repo_url) + # Make sure that everything under the results directory is owned by the user + user = pwd.getpwuid(cfg.uid).pw_name + group = grp.getgrgid(cfg.gid).gr_name + log.debug("Install finished, chowning results to %s:%s", user, group) + subprocess.call(["chown", "-R", "%s:%s" % (user, group), results_dir]) + def compose_detail(results_dir): """ Return details about the build.""" @@ -192,3 +202,19 @@ def build_status(cfg, status_filter=None): if status in status_filter: results.append(compose_detail(build)) return results + +def uuid_delete(cfg, uuid): + """Delete all of the results from a compose + + :param cfg: Configuration settings + :type cfg: ComposerConfig + :param uuid: The UUID of the build + :type uuid: str + :returns: True if it was deleted + :rtype: bool + """ + uuid_dir = joinpaths(cfg.get("composer", "lib_dir"), "results", uuid) + if not uuid_dir or len(uuid_dir) < 10: + raise RuntimeError("Directory length is too short: %s" % uuid_dir) + shutil.rmtree(uuid_dir) + return True diff --git a/src/pylorax/api/v0.py b/src/pylorax/api/v0.py index 94d60886..4458c800 100644 --- a/src/pylorax/api/v0.py +++ b/src/pylorax/api/v0.py @@ -718,6 +718,24 @@ POST `/api/v0/recipes/tag/` } ] } + +DELETE `/api/v0/compose/delete/` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + Delete the list of comma-separated uuids from the compose results. + + Example:: + + { + "errors": [], + "uuids": [ + { + "status": true, + "uuid": "ae1bf7e3-7f16-4c9f-b36e-3726a1093fd0" + } + ] + } + """ import logging @@ -729,7 +747,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_status +from pylorax.api.queue import queue_status, build_status, uuid_delete, uuid_status 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 @@ -1257,7 +1275,7 @@ def v0_api(api): @api.route("/api/v0/compose/status/") @crossdomain(origin="*") - def v0_compose_status(uuids=None): + def v0_compose_status(uuids): """Return the status of the listed uuids""" results = [] for uuid in [n.strip().lower() for n in uuids.split(",")]: @@ -1266,3 +1284,22 @@ def v0_api(api): results.append(details) return jsonify(uuids=results) + + @api.route("/api/v0/compose/delete/", methods=["DELETE"]) + @crossdomain(origin="*") + def v0_compose_delete(uuids): + """Delete the compose results for the listed uuids""" + results = [] + errors = [] + for uuid in [n.strip().lower() for n in uuids.split(",")]: + status = uuid_status(api.config["COMPOSER_CFG"], uuid) + if status["status"] not in ["FINISHED", "FAILED"]: + errors.append({"uuid":uuid, "msg":"Build not in FINISHED or FAILED."}) + else: + try: + uuid_delete(api.config["COMPOSER_CFG"], uuid) + except Exception as e: + errors.append({"uuid":uuid, "msg":str(e)}) + else: + results.append({"uuid":uuid, "status":True}) + return jsonify(uuids=results, errors=errors)