diff --git a/ara/cli/expire.py b/ara/cli/expire.py new file mode 100644 index 00000000..8e550f94 --- /dev/null +++ b/ara/cli/expire.py @@ -0,0 +1,101 @@ +# Copyright (c) 2020 The ARA Records Ansible authors +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +# See https://github.com/ansible-community/ara/issues/26 for rationale on expiring + +import logging +import os +import sys +from datetime import datetime, timedelta + +from cliff.command import Command + +from ara.cli.base import global_arguments +from ara.clients.utils import get_client + + +class ExpireObjects(Command): + """ Expires objects that have been in the running state for too long """ + + log = logging.getLogger(__name__) + expired = 0 + + def get_parser(self, prog_name): + parser = super(ExpireObjects, self).get_parser(prog_name) + parser = global_arguments(parser) + # fmt: off + parser.add_argument( + "--hours", + type=int, + default=24, + help="Expires objects that have been running state for this many hours (default: 24)" + ) + parser.add_argument( + "--order", + metavar="", + default="started", + help=( + "Orders objects by a field ('id', 'created', 'updated', 'started', 'ended')\n" + "Defaults to 'started' descending so the oldest objects would be expired first.\n" + "The order can be reversed by using '-': ara expire --order=-started" + ), + ) + parser.add_argument( + "--limit", + metavar="", + default=os.environ.get("ARA_CLI_LIMIT", 200), + help=("Only expire the first determined by the ordering. Defaults to ARA_CLI_LIMIT or 200.") + ) + parser.add_argument( + "--confirm", + action="store_true", + help="Confirm expiration of objects, otherwise runs without expiring any objects", + ) + # fmt: on + return parser + + def take_action(self, args): + client = get_client( + client=args.client, + endpoint=args.server, + timeout=args.timeout, + username=args.username, + password=args.password, + verify=False if args.insecure else True, + run_sql_migrations=False, + ) + + if not args.confirm: + self.log.info("--confirm was not specified, no objects will be expired") + + query = dict(status="running") + # generate a timestamp from n days ago in a format we can query the API with + # ex: 2019-11-21T00:57:41.702229 + query["updated_before"] = (datetime.now() - timedelta(hours=args.hours)).isoformat() + query["order"] = args.order + query["limit"] = args.limit + + endpoints = ["/api/v1/playbooks", "/api/v1/plays", "/api/v1/tasks"] + for endpoint in endpoints: + objects = client.get(endpoint, **query) + self.log.info("Found %s objects matching query on %s" % (objects["count"], endpoint)) + # TODO: Improve client validation and exception handling + if "count" not in objects: + # If we didn't get an answer we can parse, it's probably due to an error 500, 403, 401, etc. + # The client would have logged the error. + self.log.error( + "Client failed to retrieve results, see logs for ara.clients.offline or ara.clients.http." + ) + sys.exit(1) + + for obj in objects["results"]: + link = "%s/%s" % (endpoint, obj["id"]) + if not args.confirm: + self.log.info( + "Dry-run: %s would have been expired, status is running since %s" % (link, obj["updated"]) + ) + else: + self.log.info("Expiring %s, status is running since %s" % (link, obj["updated"])) + client.patch(link, status="expired") + self.expired += 1 + + self.log.info("%s objects expired" % self.expired) diff --git a/doc/source/cli.rst b/doc/source/cli.rst index 44258acb..d153fd2c 100644 --- a/doc/source/cli.rst +++ b/doc/source/cli.rst @@ -9,6 +9,26 @@ ara .. command-output:: ara --help +ara expire +---------- + +.. note:: + + This command requires write privileges. + You can read more about read and write permissions :ref:`here `. + +.. command-output:: ara expire --help + +Examples: + +.. code-block:: bash + + # Return which objects would be expired by ommitting --confirm + ara expire + + # Expire running objects without updates faster than the default + ara expire --hours 4 --confirm + ara playbook list ----------------- diff --git a/setup.cfg b/setup.cfg index c01b3fdd..290f2c8e 100644 --- a/setup.cfg +++ b/setup.cfg @@ -35,6 +35,7 @@ console_scripts = ara-manage = ara.server.__main__:main ara.cli = + expire = ara.cli.expire:ExpireObjects playbook list = ara.cli.playbook:PlaybookList playbook show = ara.cli.playbook:PlaybookShow playbook delete = ara.cli.playbook:PlaybookDelete