def _evaluate_interval(self): """Get the interval at which this function is executing. This translates an AWS Rate Schedule Expression ('rate(2 hours)') into a second interval """ rate_match = AWS_RATE_RE.match(self._schedule) if not rate_match: raise AppConfigError('Invalid \'rate\' interval value: ' '{}'.format(self._schedule)) value = rate_match.group(2) or rate_match.group(4) unit = rate_match.group(3) or rate_match.group(5).replace('s', '') translate_to_seconds = { 'minute': 60, 'hour': 60 * 60, 'day': 60 * 60 * 24 } interval = int(value) * translate_to_seconds[unit] LOGGER.debug('Evaluated rate interval: %d seconds', interval) # Get the total seconds that this rate evaluates to return interval
def _get_parameters(cls, *names): """Simple helper function to house the boto3 ssm client get_parameters operations Args: names (list): A list of parameter names to retrieve from the aws ssm parameter store Returns: tuple (dict, list): Dictionary with the load parameter names as keys and the actual parameter (as a dictionary) as the value. The seconary list that is returned contains any invalid parameters that were not loaded """ # Create the ssm boto3 client that will be cached and used throughout this execution # if one does not exist already if AppConfig.SSM_CLIENT is None: boto_config = client.Config(connect_timeout=cls.BOTO_TIMEOUT, read_timeout=cls.BOTO_TIMEOUT) AppConfig.SSM_CLIENT = boto3.client('ssm', config=boto_config) LOGGER.debug('Retrieving values from parameter store with names: %s', ', '.join('\'{}\''.format(name) for name in names)) try: parameters = AppConfig.SSM_CLIENT.get_parameters( Names=list(names), WithDecryption=True) except ClientError as err: joined_names = ', '.join('\'{}\''.format(name) for name in names) raise AppConfigError( 'Could not get parameter with names {}. Error: ' '{}'.format(joined_names, err.response['Error']['Message'])) decoded_params = {} for param in parameters['Parameters']: try: decoded_params[param['Name']] = json.loads(param['Value']) except ValueError: raise AppConfigError( 'Could not load value for parameter with ' 'name \'{}\'. The value is not valid json: ' '\'{}\''.format(param['Name'], param['Value'])) return decoded_params, parameters['InvalidParameters']
def _validate_event(cls, event): """Validate the top level of the config to make sure it has all the right keys Raises: AppConfigError: If the config is invalid, this exception is raised """ event_key_diff = cls.required_event_keys().difference(set(event)) if not event_key_diff: return missing_event_keys = ', '.join('\'{}\''.format(key) for key in event_key_diff) raise AppConfigError('App event is missing the following required ' 'keys: {}'.format(missing_event_keys))
def load_config(cls, event, context): """Load the configuration for this app invocation Args: event (dict): The AWS Lambda input event, which is JSON serialized to a dictionary context (LambdaContext): The AWS LambdaContext object, passed in via the handler. Returns: AppConfig: Configuration for the running application """ # Patch out the protected _remaining_ms method to the AWS timing function AppConfig.remaining_ms = context.get_remaining_time_in_millis func_name = context.function_name func_version = context.function_version # Get full parameter names for authentication and state parameters auth_param_name = '_'.join([func_name, cls.AUTH_CONFIG_SUFFIX]) state_param_name = '_'.join([func_name, cls.STATE_CONFIG_SUFFIX]) # Get the loaded parameters and a list of any invalid ones from parameter store params, invalid_params = cls._get_parameters(auth_param_name, state_param_name) # Check to see if the authentication param is in the invalid params list if auth_param_name in invalid_params: raise AppConfigError( 'Could not load authentication parameter required for this ' 'app: {}'.format(auth_param_name)) LOGGER.debug('Retrieved parameters from parameter store: %s', cls._scrub_auth_info(params, auth_param_name)) LOGGER.debug( 'Invalid parameters could not be retrieved from parameter store: %s', invalid_params) # Load the authentication info. This data can vary from service to service auth_config = { key: value.encode('utf-8') if isinstance(value, unicode) else value for key, value in params[auth_param_name].iteritems() } state_config = params.get(state_param_name, {}) return AppConfig(auth_config, state_config, event, func_name, func_version)