Ejemplo n.º 1
0
    def __init__(self, event, context):
        """
        Initializes handler.
        :param event: Event to handle
        :param context: Context if run within Lambda environment
        """
        self._context = context
        self._event = event
        self._action_tracking = TaskTrackingTable(context)

        self.action_id = self._event[tracking.TASK_TR_ID]
        self.task = self._event[tracking.TASK_TR_NAME]
        self.action = self._event[tracking.TASK_TR_ACTION]
        self.test_completion_method = getattr(
            actions.get_action_class(self.action), handlers.COMPLETION_METHOD,
            None)
        self.action_parameters = json.loads(
            self._event.get(tracking.TASK_TR_PARAMETERS, "{}"))
        self.action_resources = json.loads(
            self._event.get(tracking.TASK_TR_RESOURCES, "{}"))
        self.dryrun = self._event.get(tracking.TASK_TR_DRYRUN)
        self.debug = self._event.get(tracking.TASK_TR_DEBUG)
        self.started_at = float(self._event.get(tracking.TASK_TR_STARTED_TS,
                                                0))
        self.start_result = self._event.get(tracking.TASK_TR_START_RESULT,
                                            None)
        self.session = AwsService.get_session(
            self._event.get(tracking.TASK_TR_ASSUMED_ROLE))
        self.stack_name = os.getenv(handlers.ENV_STACK_NAME)
        self.stack_id = os.getenv(handlers.ENV_STACK_ID)
        self.action = event[tracking.TASK_TR_ACTION]
        self.action_properties = actions.get_action_properties(self.action)
        self.action_class = actions.get_action_class(self.action)
        self._stack_resources = None
        self.timeout = self._event.get(tracking.TASK_TR_TIMEOUT)
        self.execution_log_stream = self._event.get(
            tracking.TASK_TR_EXECUTION_LOGSTREAM)

        # setup logging
        if self.execution_log_stream is None:
            dt = datetime.utcnow()
            self.execution_log_stream = LOG_STREAM.format(
                self._event[tracking.TASK_TR_NAME], dt.year, dt.month, dt.day,
                dt.hour, dt.minute, self.action_id)
        else:
            self.execution_log_stream = self.execution_log_stream

        debug = event[tracking.TASK_TR_DEBUG]

        self._logger = Logger(logstream=self.execution_log_stream,
                              buffersize=40 if debug else 20,
                              context=context,
                              debug=debug)
Ejemplo n.º 2
0
    def _get_action_concurrency_key(self, item):
        """
        Gets the concurrency key for a tasks action
        :param item: The task item
        :return: The concurrency key for the tasks action
        """
        action = item[handlers.TASK_TR_ACTION]
        # get the name of the optional method to return the concurrency key
        action_class = actions.get_action_class(action)
        concurrency_key_method = getattr(
            action_class, handlers.ACTION_CONCURRENCY_KEY_METHOD, None)

        # prepare parameters for calling static function that returns the concurrency key
        if concurrency_key_method is not None:
            get_key_params = {
                actions.ACTION_PARAM_RESOURCES:
                handlers.get_item_resource_data(item, self._context),
                actions.ACTION_PARAM_ACCOUNT:
                item[handlers.TASK_TR_ACCOUNT],
                actions.ACTION_PARAM_STACK:
                os.getenv(handlers.ENV_STACK_NAME),
                actions.ACTION_PARAM_STACK_ID:
                os.getenv(handlers.ENV_STACK_ID),
                actions.ACTION_PARAM_TASK_ID:
                item[handlers.TASK_TR_ID],
                actions.ACTION_PARAM_TASK:
                item[handlers.TASK_TR_NAME]
            }
            get_key_params.update(item.get(handlers.TASK_TR_PARAMETERS))
            return concurrency_key_method(get_key_params)
        else:
            # if this method is not available for action then use the name of the action as the key
            return action
    def build_template(self, action_name):
        """
        Build a cloudformation template for the action to create tasks for that action
        :param action_name: name of the action
        :return: template as dictionary
        """
        self.action_name = action_name
        self.action_properties = actions.get_action_properties(self.action_name)
        self.action_class = actions.get_action_class(self.action_name)

        self.has_completion_logic = getattr(self.action_class, handlers.COMPLETION_METHOD, None)
        self.has_cross_account_parameter = self.action_properties.get(actions.ACTION_CROSS_ACCOUNT, True)
        self.is_regional_service = services.get_service_class(self.action_properties[actions.ACTION_SERVICE]).is_regional()
        self.has_regions_parameter = self.is_regional_service and self.action_properties.get(actions.ACTION_MULTI_REGION, True)
        self.use_intervals = actions.ACTION_TRIGGER_INTERVAL[0] in self.action_properties.get(actions.ACTION_TRIGGERS,
                                                                                              actions.ACTION_TRIGGER_BOTH)
        self.interval_min = self.action_properties.get(actions.ACTION_MIN_INTERVAL_MIN, 0)
        self.use_events = actions.ACTION_TRIGGER_EVENTS[0] in self.action_properties.get(actions.ACTION_TRIGGERS,
                                                                                         actions.ACTION_TRIGGER_BOTH)

        self._setup_template()
        self._setup_common_parameters()
        self._setup_action_parameters()
        self._setup_resources()
        self._setup_outputs()

        for p in self._template_parameters:
            description = self._template_parameters[p]["Description"]
            if description not in ["", None] and not description.endswith("."):
                self._template_parameters[p]["Description"] += "."

        return self._template
 def action_class_parameter_check(parameters, name):
     # get the class that implements the action and test if there is a static method for additional checks of the parameters
     action_class = actions.get_action_class(name)
     validate_params_method = getattr(
         action_class, actions.ACTION_VALIDATE_PARAMETERS_METHOD, None)
     # if the method exists then validate the parameters using the business logic for that class
     if validate_params_method is not None:
         return validate_params_method(parameters)
     return parameters
    def __init__(self, event, context):
        """
        Initializes handler.
        :param event: Event to handle
        :param context: Context if run within Lambda environment
        """
        self._context = context
        self._event = event

        self.action_id = self._event[handlers.TASK_TR_ID]
        self.task = self._event[handlers.TASK_TR_NAME]
        self.task_timezone = self._event.get(handlers.TASK_TR_TIMEZONE, None)
        self.has_completion = self._event[handlers.TASK_TR_HAS_COMPLETION]
        self.action_parameters = self._event.get(handlers.TASK_TR_PARAMETERS,
                                                 {})
        self.dryrun = self._event.get(handlers.TASK_TR_DRYRUN)
        self.interval = self._event.get(handlers.TASK_TR_INTERVAL, None)
        self.metrics = self._event.get(handlers.TASK_TR_METRICS, False)
        self.debug = self._event.get(handlers.TASK_TR_DEBUG)
        self.started_at = int(self._event.get(handlers.TASK_TR_STARTED_TS, 0))
        self.start_result = self._event.get(handlers.TASK_TR_START_RESULT,
                                            None)
        self.session = services.get_session(
            self._event.get(handlers.TASK_TR_ASSUMED_ROLE))
        self.stack_name = os.getenv(handlers.ENV_STACK_NAME)
        self.stack_id = os.getenv(handlers.ENV_STACK_ID)
        self.action = event[handlers.TASK_TR_ACTION]
        self.tagfilter = event.get(handlers.TASK_TR_TAGFILTER, "")
        self.action_properties = actions.get_action_properties(self.action)
        self.action_class = actions.get_action_class(self.action)
        self._stack_resources = None
        self.timeout = int(
            self._event[handlers.TASK_TR_TIMEOUT]) * 60 if self._event.get(
                handlers.TASK_TR_TIMEOUT, None) not in [None, "None"] else 0
        self.execution_log_stream = self._event.get(
            handlers.TASK_TR_EXECUTION_LOGSTREAM)
        self.assumed_role = self._event.get(handlers.TASK_TR_ASSUMED_ROLE,
                                            None)
        self.events = self._event.get(handlers.TASK_TR_EVENTS, {})
        if isinstance(self.events, str):
            self.events = json.loads(
                self._event.get(handlers.TASK_TR_EVENTS,
                                "{}").replace("u'", '"').replace("'", '"'))

        self._action_resources = None
        self._s3_client = None
        self._action_instance = None
        self._action_class = None
        self._action_arguments = None
        self._timer = None
        self._timeout_event = None

        self.__logger = None
        self.__action_tracking = None
Ejemplo n.º 6
0
def setup_tasks_metrics(task,
                        action_name,
                        task_level_metrics,
                        logger=None,
                        context=None):
    with TaskMetrics(dt=datetime.utcnow(), logger=logger,
                     context=context) as metrics:

        task_class = actions.get_action_class(action_name)

        # number of submitted task instances for task
        metrics.put_task_state_metrics(
            task_name=task,
            metric_state_name=METRICS_STATUS_NAMES[handlers.STATUS_PENDING],
            count=0,
            task_level=task_level_metrics)

        # init metrics for results
        for s in [
                handlers.STATUS_STARTED, handlers.STATUS_COMPLETED,
                handlers.STATUS_FAILED
        ]:
            metrics.put_task_state_metrics(
                task_name=task,
                metric_state_name=METRICS_STATUS_NAMES[s],
                count=0,
                task_level=task_level_metrics)

        # init metrics for tasks with completion handling
        if getattr(task_class, handlers.COMPLETION_METHOD, None) is not None:
            metrics.put_task_state_metrics(
                task_name=task,
                metric_state_name=METRICS_STATUS_NAMES[
                    handlers.STATUS_WAIT_FOR_COMPLETION],
                count=0,
                task_level=task_level_metrics)
            metrics.put_task_state_metrics(
                task_name=task,
                metric_state_name=METRICS_STATUS_NAMES[
                    handlers.STATUS_TIMED_OUT],
                count=0,
                task_level=task_level_metrics)

        # init metrics for tasks with concurrency handling
        if getattr(task_class, handlers.ACTION_CONCURRENCY_KEY_METHOD,
                   None) is not None:
            metrics.put_task_state_metrics(
                task_name=task,
                metric_state_name=METRICS_STATUS_NAMES[
                    handlers.STATUS_WAITING],
                count=0,
                task_level=task_level_metrics)
Ejemplo n.º 7
0
    def build_template(self, action_name):
        """
        Build a cloudformation template for the action to create tasks for that action
        :param action_name: name of the action
        :return: template as dictionary
        """
        self.action_name = action_name
        self.action_properties = actions.get_action_properties(self.action_name)
        self.action_class = actions.get_action_class(self.action_name)

        self.has_timeout_parameter = getattr(self.action_class, handlers.COMPLETION_METHOD, None)
        self.has_cross_account_parameter = self.action_properties.get(actions.ACTION_CROSS_ACCOUNT, True)
        self.is_regional_service = services.get_service_class(self.action_properties[actions.ACTION_SERVICE]).is_regional()
        self.has_regions_parameter = self.is_regional_service and self.action_properties.get(actions.ACTION_MULTI_REGION, True)

        self._setup_template()
        self._setup_common_parameters()
        self._setup_action_parameters()
        self._setup_resources()

        return self._template
    def __init__(self, event, context):

        self._context = context
        self._event = event
        self.task = event[handlers.HANDLER_EVENT_TASK]

        # setup logging
        classname = self.__class__.__name__
        dt = datetime.utcnow()
        logstream = LOG_STREAM.format(classname, self.task[handlers.TASK_NAME],
                                      dt.year, dt.month, dt.day)
        debug = event[handlers.HANDLER_EVENT_TASK].get(handlers.TASK_DEBUG,
                                                       False)
        self._logger = Logger(logstream=logstream,
                              context=context,
                              buffersize=40 if debug else 20,
                              debug=debug)

        self._sts = None
        self._dynamodb = boto3.client("dynamodb")

        self.select_args = event.get(handlers.HANDLER_SELECT_ARGUMENTS, {})
        self.task_dt = event[handlers.HANDLER_EVENT_TASK_DT]

        self.action_properties = actions.get_action_properties(
            self.task[handlers.TASK_ACTION])
        self.action_class = actions.get_action_class(
            self.task[handlers.TASK_ACTION])
        self.task_parameters = self.task.get(handlers.TASK_PARAMETERS, {})
        self.aggregation_level = self.action_properties.get(
            actions.ACTION_AGGREGATION, actions.ACTION_AGGREGATION_RESOURCE)
        self.service = self.action_properties[actions.ACTION_SERVICE]
        self.resource_name = self.action_properties[actions.ACTION_RESOURCES]
        self.keep_tags = self.action_properties.get(
            actions.ACTION_KEEP_RESOURCE_TAGS, True)

        self.source = self._event.get(handlers.HANDLER_EVENT_SOURCE,
                                      handlers.UNKNOWN_SOURCE)
    def verify_timeout(action_name, timeout):
        completion_method = getattr(actions.get_action_class(action_name),
                                    handlers.COMPLETION_METHOD, None)
        if completion_method is None and timeout is not None:
            raise ValueError(ERR_TIMEOUT_NOT_ALLOWED.format(action_name))

        if completion_method is None:
            return None

        if timeout is None:
            action_properties = actions.get_action_properties(action_name)
            return action_properties.get(
                actions.ACTION_COMPLETION_TIMEOUT_MINUTES,
                actions.DEFAULT_COMPLETION_TIMEOUT_MINUTES_DEFAULT)

        try:
            result = int(str(timeout).partition(".")[0])
            if result > 0:
                return result
            else:
                raise ValueError(ERR_TIMEOUT_MUST_BE_GREATER_0.format(result))
        except ValueError as ex:
            raise ValueError(ERR_INVALID_NUMERIC_TIMEOUT.format(timeout, ex))
Ejemplo n.º 10
0
    def add_task_action(self,
                        task,
                        assumed_role,
                        action_resources,
                        task_datetime,
                        source,
                        task_group=None):

        item = {
            handlers.TASK_TR_ID:
            str(uuid.uuid4()),
            handlers.TASK_TR_NAME:
            task[handlers.TASK_NAME],
            handlers.TASK_TR_ACTION:
            task[handlers.TASK_ACTION],
            handlers.TASK_TR_CREATED:
            datetime.now().isoformat(),
            handlers.TASK_TR_CREATED_TS:
            time.time(),
            handlers.TASK_TR_SOURCE:
            source,
            handlers.TASK_TR_DT:
            task_datetime,
            handlers.TASK_TR_STATUS:
            handlers.STATUS_PENDING,
            handlers.TASK_TR_DEBUG:
            task[handlers.TASK_DEBUG],
            handlers.TASK_TR_NOTIFICATIONS:
            task[handlers.TASK_NOTIFICATIONS],
            handlers.TASK_TR_HAS_COMPLETION:
            getattr(actions.get_action_class(task[handlers.TASK_ACTION]),
                    handlers.COMPLETION_METHOD, None) is not None,
            handlers.TASK_TR_METRICS:
            task[handlers.TASK_METRICS],
            handlers.TASK_TR_DRYRUN:
            task[handlers.TASK_DRYRUN],
            handlers.TASK_TR_INTERNAL:
            task[handlers.TASK_INTERNAL],
            handlers.TASK_INTERVAL:
            task[handlers.TASK_INTERVAL],
            handlers.TASK_TR_TIMEOUT:
            task[handlers.TASK_TIMEOUT],
            handlers.TASK_TR_TIMEZONE:
            task[handlers.TASK_TIMEZONE],
            handlers.TASK_TR_STARTED_TS:
            int(time.time()),
            handlers.TASK_TR_EXECUTE_SIZE:
            task[handlers.TASK_EXECUTE_SIZE],
            handlers.TASK_TR_SELECT_SIZE:
            task[handlers.TASK_SELECT_SIZE],
            handlers.TASK_TR_COMPLETION_SIZE:
            task[handlers.TASK_COMPLETION_SIZE],
            handlers.TASK_TR_TAGFILTER:
            task[handlers.TASK_TAG_FILTER],
            handlers.TASK_TR_EVENTS:
            task.get(handlers.TASK_EVENTS, {}),
            handlers.TASK_TR_RUN_LOCAL:
            True,
            handlers.TASK_TR_GROUP:
            task_group
        }
        if assumed_role not in [None, ""]:
            item[handlers.TASK_TR_ASSUMED_ROLE] = assumed_role
            item[handlers.TASK_TR_ACCOUNT] = services.account_from_role_arn(
                assumed_role)
        else:
            item[handlers.TASK_TR_ACCOUNT] = os.getenv(
                handlers.ENV_OPS_AUTOMATOR_ACCOUNT)

        if len(task[handlers.TASK_PARAMETERS]) > 0:
            item[handlers.TASK_TR_PARAMETERS] = task[handlers.TASK_PARAMETERS]

        parameters = item.get(handlers.TASK_TR_PARAMETERS, None)
        if parameters is not None:
            item[handlers.TASK_TR_PARAMETERS] = parameters

        # check if the class has a field or static method that returns true if the action class needs completion
        # this way we can make completion dependent of parameter values
        has_completion = getattr(
            actions.get_action_class(task[handlers.TASK_ACTION]),
            actions.ACTION_PARAM_HAS_COMPLETION, None)
        if has_completion is not None:
            # if it is static method call it passing the task parameters
            if isinstance(has_completion, types.FunctionType):
                has_completion = has_completion(parameters)
        else:
            # if it does not have this method test if the class has an us_complete method
            has_completion = getattr(
                actions.get_action_class(task[handlers.TASK_ACTION]),
                handlers.COMPLETION_METHOD, None) is not None

        item[handlers.TASK_TR_HAS_COMPLETION] = has_completion

        item[handlers.TASK_TR_RESOURCES] = action_resources

        self._task_items.append(self.dynamo_safe_attribute_types(item))

        return item
Ejemplo n.º 11
0
    def __init__(self, event, context, logger=None, tracking_store=None):
        def log_stream_name():

            classname = self.__class__.__name__
            dt = datetime.utcnow()

            account = self._event.get(handlers.HANDLER_SELECT_ARGUMENTS,
                                      {}).get(handlers.HANDLER_EVENT_ACCOUNT,
                                              "")
            regions = self._event.get(handlers.HANDLER_SELECT_ARGUMENTS,
                                      {}).get(handlers.HANDLER_EVENT_REGIONS,
                                              [])

            if account is not None and len(regions) > 0:
                account_and_region = "-".join([account, regions[0]]) + "-"

            else:
                region = ""

                if self.sub_task is not None:
                    account = ""
                    if self._this_account:
                        if len(self._accounts) == 0:
                            account = os.getenv(
                                handlers.ENV_OPS_AUTOMATOR_ACCOUNT)
                    elif len(self._accounts) == 1:
                        account = self._accounts[0]

                    region = self._regions[0] if len(
                        self._regions) == 1 else ""

                if account != "":
                    if region not in ["", None]:
                        account_and_region = "-".join([account, region]) + "-"
                    else:
                        account_and_region = account
                else:
                    account_and_region = ""

            return LOG_STREAM.format(classname, self.task[handlers.TASK_NAME],
                                     account_and_region, dt.year, dt.month,
                                     dt.day)

        self._context = context
        self._event = event
        self.task = event[handlers.HANDLER_EVENT_TASK]
        self.sub_task = event.get(handlers.HANDLER_EVENT_SUB_TASK, None)
        self.use_custom_select = event.get(
            handlers.HANDLER_EVENT_CUSTOM_SELECT, True)

        # the job id is used to correlate all generated tasks for the selected resources
        self.task_group = self._event.get(handlers.HANDLER_EVENT_TASK_GROUP,
                                          None)
        if self.task_group is None:
            self.task_group = str(uuid.uuid4())

        debug = event[handlers.HANDLER_EVENT_TASK].get(handlers.TASK_DEBUG,
                                                       False)
        if logger is None:
            self._logger = QueuedLogger(logstream=log_stream_name(),
                                        context=context,
                                        buffersize=50 if debug else 20,
                                        debug=debug)
        else:
            self._logger = logger

        self._sts = None

        self.select_args = event.get(handlers.HANDLER_SELECT_ARGUMENTS, {})
        self.task_dt = event[handlers.HANDLER_EVENT_TASK_DT]

        self.action_properties = actions.get_action_properties(
            self.task[handlers.TASK_ACTION])
        self.action_class = actions.get_action_class(
            self.task[handlers.TASK_ACTION])
        self.task_parameters = self.task.get(handlers.TASK_PARAMETERS, {})
        self.metrics = self.task.get(handlers.TASK_METRICS, False)

        self.service = self.action_properties[actions.ACTION_SERVICE]
        self.keep_tags = self.action_properties.get(
            actions.ACTION_KEEP_RESOURCE_TAGS, True)

        self.source = self._event.get(handlers.HANDLER_EVENT_SOURCE,
                                      handlers.UNKNOWN_SOURCE)
        self.run_local = handlers.running_local(self._context)
        self._timer = None
        self._timeout_event = self._timeout_event = threading.Event()

        self.aggregation_level = self.action_properties.get(
            actions.ACTION_AGGREGATION, actions.ACTION_AGGREGATION_RESOURCE)
        if self.aggregation_level is not None and isinstance(
                self.aggregation_level, types.FunctionType):
            self.aggregation_level = self.aggregation_level(
                self.task_parameters)

        self.batch_size = self.action_properties.get(actions.ACTION_BATCH_SIZE)
        if self.batch_size is not None and isinstance(self.batch_size,
                                                      types.FunctionType):
            self.batch_size = self.batch_size(self.task_parameters)

        self.actions_tracking = TaskTrackingTable(
            self._context,
            logger=self._logger) if tracking_store is None else tracking_store
    def add_task_action(self, task, assumed_role, action_resources, task_datetime, source, task_group=None):

        item = {
            handlers.TASK_TR_ID: str(uuid.uuid4()),
            handlers.TASK_TR_NAME: task[handlers.TASK_NAME],
            handlers.TASK_TR_ACTION: task[handlers.TASK_ACTION],
            handlers.TASK_TR_CREATED: datetime.now().isoformat(),
            handlers.TASK_TR_CREATED_TS: int(time()),
            handlers.TASK_TR_SOURCE: source,
            handlers.TASK_TR_DT: task_datetime,
            handlers.TASK_TR_STATUS: handlers.STATUS_PENDING,
            handlers.TASK_TR_DEBUG: task[handlers.TASK_DEBUG],
            handlers.TASK_TR_NOTIFICATIONS: task[handlers.TASK_NOTIFICATIONS],
            handlers.TASK_TR_METRICS: task[handlers.TASK_METRICS],
            handlers.TASK_TR_DRYRUN: task[handlers.TASK_DRYRUN],
            handlers.TASK_TR_INTERNAL: task[handlers.TASK_INTERNAL],
            handlers.TASK_TR_INTERVAL: task[handlers.TASK_INTERVAL],
            handlers.TASK_TR_TIMEZONE: task[handlers.TASK_TIMEZONE],
            handlers.TASK_TR_TIMEOUT: task[handlers.TASK_TIMEOUT],
            handlers.TASK_TR_STARTED_TS: int(time()),
            handlers.TASK_TR_EXECUTE_SIZE: task[handlers.TASK_EXECUTE_SIZE],
            handlers.TASK_TR_SELECT_SIZE: task[handlers.TASK_SELECT_SIZE],
            handlers.TASK_TR_EVENTS: task.get(handlers.TASK_EVENTS, {}),
            handlers.TASK_TR_COMPLETION_SIZE: task[handlers.TASK_COMPLETION_SIZE],
            handlers.TASK_TR_TAGFILTER: task[handlers.TASK_TAG_FILTER],
            handlers.TASK_TR_GROUP: task_group,
            handlers.TASK_TR_SERVICE: task[handlers.TASK_SERVICE],
            handlers.TASK_TR_RESOURCE_TYPE: task[handlers.TASK_RESOURCE_TYPE]
        }

        item[handlers.TASK_TR_RUN_LOCAL] = self._run_local

        if assumed_role is not None:
            item[handlers.TASK_TR_ASSUMED_ROLE] = assumed_role
            item[handlers.TASK_TR_ACCOUNT] = services.account_from_role_arn(assumed_role)
        else:
            item[handlers.TASK_TR_ACCOUNT] = self.account

        if len(task[handlers.TASK_PARAMETERS]) > 0:
            item[handlers.TASK_TR_PARAMETERS] = task[handlers.TASK_PARAMETERS]

        parameters = item.get(handlers.TASK_TR_PARAMETERS, None)
        if parameters is not None:
            item[handlers.TASK_TR_PARAMETERS] = parameters

        # check if the class has a field or static method that returns true if the action class needs completion
        # this way we can make completion dependent of parameter values
        has_completion = getattr(actions.get_action_class(task[handlers.TASK_ACTION]), actions.ACTION_PARAM_HAS_COMPLETION, None)
        if has_completion is not None:
            # if it is static method call it passing the task parameters
            if isinstance(has_completion, types.FunctionType):
                has_completion = has_completion(parameters)
        else:
            # if it does not have this method test if the class has an us_complete method
            has_completion = getattr(actions.get_action_class(task[handlers.TASK_ACTION]),
                                     handlers.COMPLETION_METHOD, None) is not None

        item[handlers.TASK_TR_HAS_COMPLETION] = has_completion

        resource_data_str = safe_json(action_resources)

        encrypted = self._resource_encryption_key not in [None, ""]
        item[handlers.TASK_TR_ENCRYPTED_RESOURCES] = encrypted
        if encrypted:
            resource_data_str = base64.b64encode(self.kms_client.encrypt_with_retries(
                KeyId=self._resource_encryption_key, Plaintext=resource_data_str)["CiphertextBlob"])

        if len(resource_data_str) < int(os.getenv(handlers.ENV_RESOURCE_TO_S3_SIZE, 16)) * 1024:
            if encrypted:
                item[handlers.TASK_TR_RESOURCES] = action_resources if not encrypted else resource_data_str
            else:
                item[handlers.TASK_TR_RESOURCES] = as_dynamo_safe_types(action_resources)
        else:
            bucket = os.getenv(handlers.ENV_RESOURCE_BUCKET)
            key = "{}.json".format(item[handlers.TASK_TR_ID])

            try:
                self.s3_client.put_object_with_retries(Body=resource_data_str, Bucket=bucket, Key=key)
            except Exception as ex:
                raise_exception(ERR_WRITING_RESOURCES, bucket, key, item[handlers.TASK_TR_ID], ex)
            item[handlers.TASK_TR_S3_RESOURCES] = True

        self._new_action_items.append(item)

        return item
    def __init__(self,
                 action_name,
                 tested_region=None,
                 test_region=None,
                 action_stack_name=None,
                 logger=None,
                 task_list_tag_name=None,
                 debug=False):

        self.debug = debug
        self.logger = logger if logger is not None else ConsoleLogger(
            debug=self.debug)
        if getattr(self.logger, "test") is None:
            setattr(self.logger, "test", getattr(self.logger, "info"))

        self.context = None

        self.action_name = action_name
        self.action_class = actions.get_action_class(self.action_name)
        self.action_properties = actions.get_action_properties(
            self.action_name)

        self.tested_region = tested_region if tested_region is not None else boto3.Session(
        ).region_name
        self.test_region = test_region if test_region is not None else boto3.Session(
        ).region_name

        if task_list_tag_name is not None:
            os.environ[handlers.ENV_AUTOMATOR_TAG_NAME] = task_list_tag_name

        self.interval = None

        os.environ[
            handlers.ENV_OPS_AUTOMATOR_ACCOUNT] = services.get_aws_account()

        if action_stack_name is None:
            self.action_stack_name = testing.action_stack_name(
                self.action_name)
        else:
            self.action_stack_name = action_stack_name

        self._action_stack = None

        os.environ[handlers.ENV_STACK_NAME] = self.action_stack_name

        actions.set_report_output_provider(create_output_writer)

        self._action_stack_resources = None

        self._assumed_role = None
        self._tested_account = None
        self._session = None

        self._cloudformation_client = None
        self._action_stack_template = None
        self._events = None
        self._tag_filter = None

        self.results = None
        self.parameters = None
        self.task_name = None
        self.action_select_parameters = None
        self.run_after_select = None

        self.max_concurrency = None
        self.concurrency_key = None
        self.log_subject = None
        self.run_in_regions = None

        self.run_after_select = None

        self.executed_tasks = []
    def handle_request(self):
        """
        Handles action execute requests, creates an instance of the required action class and executes the action on the
        resources passed in the event.
        :return:
        """

        # get class of the action, this class is needed by the _logger property
        self._action_class = actions.get_action_class(self.action)

        try:
            self._action_arguments = {
                actions.ACTION_PARAM_CONTEXT: self._context,
                actions.ACTION_PARAM_EVENT: self._event,
                actions.ACTION_PARAM_SESSION: self.session,
                actions.ACTION_PARAM_RESOURCES: self.action_resources,
                actions.ACTION_PARAM_INTERVAL: self.interval,
                actions.ACTION_PARAM_DEBUG: self.debug,
                actions.ACTION_PARAM_DRYRUN: self.dryrun,
                actions.ACTION_PARAM_TASK_ID: self.action_id,
                actions.ACTION_PARAM_TASK: self.task,
                actions.ACTION_PARAM_TASK_TIMEZONE: self.task_timezone,
                actions.ACTION_PARAM_STACK: self.stack_name,
                actions.ACTION_PARAM_STACK_ID: self.stack_id,
                actions.ACTION_PARAM_STACK_RESOURCES: self.stack_resources,
                actions.ACTION_PARAM_ASSUMED_ROLE: self.assumed_role,
                actions.ACTION_PARAM_STARTED_AT: self.started_at,
                actions.ACTION_PARAM_TAGFILTER: self.tagfilter,
                actions.ACTION_PARAM_TIMEOUT: self.timeout,
                actions.ACTION_PARAM_TAG_FILTER: self.tagfilter,
                actions.ACTION_PARAM_EVENTS: self.events
            }

            # called after initialization other arguments as it is using these to construct the logger
            self._action_arguments[actions.ACTION_PARAM_LOGGER] = self._logger

            if self._context is not None:
                self._timeout_event = threading.Event()
                self._action_arguments[
                    actions.ACTION_PARAM_TIMEOUT_EVENT] = self._timeout_event

            # create the instance of the action class
            self._action_instance = self._action_class(self._action_arguments,
                                                       self.action_parameters)

            self._logger.info(INF_START_EXEC,
                              self._event[handlers.HANDLER_EVENT_ACTION],
                              self.action_id)

            if self._event[
                    handlers.
                    HANDLER_EVENT_ACTION] == handlers.HANDLER_ACTION_EXECUTE:
                return self._handle_task_execution()

            elif self._event[
                    handlers.
                    HANDLER_EVENT_ACTION] == handlers.HANDLER_ACTION_TEST_COMPLETION:
                return self._handle_test_task_completion()

            raise Exception(
                ERR_INVALID_ACTION.format(
                    self._event[handlers.HANDLER_EVENT_ACTION]))

        except Exception as ex:

            self._logger.error(ERR_EXECUTION_TASK,
                               self._event[handlers.HANDLER_EVENT_ACTION],
                               self.task, str(ex),
                               ("\n" + full_stack()) if self.debug else "")

            self._action_tracking.update_task(
                action_id=self.action_id,
                task=self.task,
                task_metrics=self.metrics,
                status=handlers.STATUS_FAILED,
                status_data={handlers.TASK_TR_ERROR: str(ex)})
        finally:
            self._logger.info(INF_FINISH_EXEC,
                              self._event[handlers.HANDLER_EVENT_ACTION],
                              self.action_id)
            self._logger.flush()