Source code for chaosmonkey.api.executors_blueprint

"""
**Base path**: /api/1/executors

Executors are scheduled jobs that are related with an attack, so in the given date
the job will execute the attack.
The only way to create executors is through :meth:`chaosmonkey.api.plans_blueprint`

Evey executor has 4 main properties:

1. **id**: unique identifier
2. **next_run_time**: The time and date that the executor is going to be executed
3. **plan_id**: id of the plan that created the executor
4. **executed**: if the executor has been executed

Example::

    {
        "id": "3b373155577b4d1bbc62216ffea013a4",
        "plan_id": "3ec72048cab04b76bdf2cfd4bc81cd1e",
        "next_run_time": "2017-01-25T10:12:1485339145",
        "executed": false
    }

"""
import arrow
from apscheduler.jobstores.base import JobLookupError
from apscheduler.triggers.date import DateTrigger
from flask import Blueprint, json, request
from chaosmonkey.api.hal import Document

from chaosmonkey.api.api_errors import APIError
from chaosmonkey.api.request_validator import validate_payload
from chaosmonkey.engine.cme_manager import manager
from chaosmonkey.api.utils import get_boolean

executors = Blueprint("executors", __name__)

executor_trigger_schema = {
    "type": "object",
    "properties": {
        "type": {"type": "string"},
        "args": {
            "type": "object",
            "properties": {
                "date": {"type": "string"}
            },
            "required": ["date"]
        }
    },
    "required": ["type", "args"]
}


@executors.route("/", methods=["GET"])
[docs]def get_executors(): """ Get a list of scheduled executors Example response:: { "executors":[ { "_links":{ "self":{ "href":"/api/1/executors/3b373155577b4d1bbc62216ffea013a4" }, "update":{ "href":"/api/1/executors/3b373155577b4d1bbc62216ffea013a4" }, "delete":{ "href":"/api/1/executors/3b373155577b4d1bbc62216ffea013a4" } }, "id":"3b373155577b4d1bbc62216ffea013a4", "plan_id":"3ec72048cab04b76bdf2cfd4bc81cd1e", "next_run_time":"2017-01-25T10:12:1485339145", "executed": false } ] } :param: executed. Control when to show all executors (true) or only not executed (false). Defaults to false :return: :meth:`chaosmonkey.api.hal.document` """ executed_query = request.args.get("executed", False) executed = get_boolean(executed_query) executors_list = [executor.to_dict() for executor in manager.get_executors(executed=executed)] return Document(embedded={"executors": executors_list})
@executors.route("/<string:executor_id>", methods=['PUT'])
[docs]def put_executor(executor_id): """ Update a executor to change its date. To provide a new date use the format in the example bellow. The format is used to create a `DateTrigger <https://github.com/agronholm/apscheduler/blob/master/apscheduler/triggers/date.py>`_ from the apscheduler. TODO: create more `Triggers <https://github.com/agronholm/apscheduler/blob/master/apscheduler/triggers>`_ Example request:: PUT /api/1/executors/3b373155577b4d1bbc62216ffea013a4 Body: { "type" : "date", "args" : { "date": "2017-10-23T19:19" } } Example response:: { "id": "3b373155577b4d1bbc62216ffea013a4", "plan_id": "3ec72048cab04b76bdf2cfd4bc81cd1e", "next_run_time": "2017-10-23T19:19:1508786354", "executed": false, "_links": { "self": { "href": "/api/1/executors/3b373155577b4d1bbc62216ffea013a4" }, "update":{ "href":"/api/1/executors/3b373155577b4d1bbc62216ffea013a4" }, "delete":{ "href":"/api/1/executors/3b373155577b4d1bbc62216ffea013a4" } } } :return: :meth:`chaosmonkey.api.hal.document` """ assert validate_payload(request, executor_trigger_schema) body = request.get_json() trigger = dict_to_trigger(body) try: executor = manager.update_executor_trigger(executor_id, trigger) except JobLookupError: return json.jsonify({"msg": "executor not found " + executor_id}), 404 else: return Document(data=executor.to_dict())
@executors.route("/<string:executor_id>", methods=['DELETE'])
[docs]def delete_executor(executor_id): """ Delete an executor Example request:: DEL /api/1/executors/6890192d8b6c40e5af16f13aa036c7dc """ manager.remove_executor(executor_id) return json.jsonify({ "msg": "Executor %s succesfully deleted" % executor_id })
######## # UTILS #######
[docs]def trigger_to_dict(trigger): """ Returns a dict version of the trigger """ if isinstance(trigger, DateTrigger): trigger_dict = {} trigger_dict["type"] = "date" trigger_dict["args"] = {"date": arrow.get(trigger.run_date).isoformat()} return trigger_dict
[docs]def dict_to_trigger(trigger_dict): """ Returns a trigger version of the trigger json """ if trigger_dict["type"] == "date": if "args" not in trigger_dict: raise APIError("Missing trigger args") if "date" not in trigger_dict["args"]: raise APIError("Invalid trigger args") try: trigger = DateTrigger(run_date=arrow.get(trigger_dict["args"]["date"]).datetime) except arrow.parser.ParserError: raise APIError("Invalid date format") return trigger else: raise APIError("Invalid trigger type")