###################################################################################################################### # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # # # # Licensed under the Apache License Version 2.0 (the "License"). You may not use this file except in compliance # # with the License. A copy of the License is located at # # # # http://www.apache.org/licenses/ # # # # or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES # # OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions # # and limitations under the License. # ###################################################################################################################### from copy import copy from datetime import datetime from configuration import CONFIG_TASK_NAME from configuration.task_admin_api import create_task, delete_task, update_task from configuration.task_configuration import TaskConfiguration from handlers.custom_resource import CustomResource from helpers import safe_dict, safe_json from outputs import raise_exception from outputs.queued_logger import QueuedLogger ERR_DELETING_TASK = "Error deleting task {}, {}" ERR_UPDATING_TASK = "Error updating task {}, {}" ERR_CREATING_TASK_ = "Error creating task {}, {}" ERR_NO_TASK_NAME_RESOURCE_PROPERTY = "Name of Task must be specified in Name property" LOG_STREAM = "{}-{:0>4d}{:0>2d}{:0>2d}" class ConfigurationResourceHandler(CustomResource): def __init__(self, event, context): CustomResource.__init__(self, event, context) self.arguments = copy(self.resource_properties) self.arguments = {a: self.resource_properties[a] for a in self.resource_properties if a not in ["ServiceToken", "Timeout"]} # setup logging dt = datetime.utcnow() classname = self.__class__.__name__ logstream = LOG_STREAM.format(classname, dt.year, dt.month, dt.day) self._logger = QueuedLogger(logstream=logstream, context=context, buffersize=20) @classmethod def is_handling_request(cls, event, _): return event.get("StackId") is not None and event.get("ResourceType") == "Custom::TaskConfig" def handle_request(self): start = datetime.now() self._logger.info("Cloudformation request is {}", safe_json(self._event, indent=2)) try: result = CustomResource.handle_request(self) return safe_dict({ "datetime": datetime.now().isoformat(), "running-time": (datetime.now() - start).total_seconds(), "result": result }) finally: self._logger.flush() def _create_request(self): name = self.resource_properties[CONFIG_TASK_NAME] try: self._logger.info("Creating new Task resource with name {}", name) self.physical_resource_id = name self.task = create_task(**self.arguments) self._logger.info("Created new resource with physical resource id {}", self.physical_resource_id) return True except Exception as ex: self.response["Reason"] = str(ex) self._logger.error(ERR_CREATING_TASK_, name, ex) return False def _update_request(self): self._logger.info("Updating Task resource") name = self.resource_properties.get(CONFIG_TASK_NAME) try: if name is None: raise_exception(ERR_NO_TASK_NAME_RESOURCE_PROPERTY) if name != self.physical_resource_id: self._logger.info("Name change for resource with physical resource id {}, new value is {}", name, self.physical_resource_id) self.arguments[CONFIG_TASK_NAME] = name create_task(**self.arguments) self.physical_resource_id = name self._logger.info("Created new resource with physical resource id {}", self.physical_resource_id) else: update_task(name, **self.arguments) self._logger.info("Updated resource with physical resource id {}", self.physical_resource_id) return True except Exception as ex: self.response["Reason"] = str(ex) self._logger.error(ERR_UPDATING_TASK, name, ex) return False def _delete_request(self): self._logger.info("Deleting Task resource") name = self.resource_properties.get(CONFIG_TASK_NAME) try: self._logger.info("Task name is {}, physical resource id is {}", name, self.physical_resource_id) # as the task can be part of a different stack than the scheduler that owns the configuration table the table could # be deleted by that stack, so first check if the table still exists if TaskConfiguration.config_table_exists(): delete_task(self.physical_resource_id) self._logger.info("Deleted resource {} with physical resource id {}", name, self.physical_resource_id) else: self._logger.info("Configuration table does not longer exist so deletion of item skipped") return True except Exception as ex: self.response["Reason"] = str(ex) self._logger.error(ERR_DELETING_TASK, name, ex) return False