def output(layer, config, layer_name=None, output_name=None, environment_name=None, stage=None): """Gets the value of an output produced by an already deployed layer. :param layer: The Layer object for the layer declaring the reference. :param config: An object holding humilis configuration options. :param layer_name: The logical name of the layer that produced the output. :param output_name: The logical name of the output parameter. """ if not environment_name or not stage: environment_name = layer.env_name stage = layer.env_stage stack_name = utils.get_cf_name(environment_name, layer_name, stage=stage) cf = Cloudformation(config) try: output = cf.get_stack_output(stack_name, output_name) except AttributeError: msg = "No output '{}' in CF stack '{}'".format(output_name, stack_name) ref = "output/{}/{}/{}/{}".format(environment_name, layer_name, stage, output_name) raise ReferenceError(ref, msg) if len(output) < 1: all_stack_outputs = [x['OutputKey'] for x in cf.stack_outputs[stack_name]] msg = ("{} output does not exist for stack {} " "(with outputs {}).").format(output_name, stack_name, all_stack_outputs) ref = "output ({}/{})".format(layer_name, output_name) raise ReferenceError(ref, msg, logger=layer.logger) return output[0]
def boto3(layer, config, service=None, call=None, output_attribute=None, output_key=None): """Calls a boto3facade method. :param layer: The Layer object for the layer declaring the reference. :param config: An object holding humilis configuration options. :param service: The name of the AWS service. :param call: A dict with two keys: method, and parameters. :param output_attribute: Object attribute to return. :param output_key: Dictionary key to return. :returns: The call response, or its corresp. attribute or key. """ facade_name = service.title() if not hasattr(boto3facade, service): ref = "boto3facade.{}.{}.{}: {}".format(service, facade_name, call['method'], call['parameters']) msg = "Service {} not supported".format(service) raise ReferenceError(ref, msg, logger=layer.logger) module = importlib.import_module("boto3facade.{}".format(service)) facade_cls = getattr(module, facade_name) facade = facade_cls(config) method = getattr(facade, call['method']) args = call.get('args', []) kwargs = call.get('kwargs', {}) result = method(*args, **kwargs) if not isinstance(result, str) and not isinstance(result, dict) and \ hasattr(result, '__iter__'): # Convert iterables to lists result = list(result) if isinstance(result, list): if len(result) == 1: result = result[0] else: raise ReferenceError( "boto3/{}/{}".format(service, call), "Must produce exactly one result but {} were produced".format( len(result))) if output_attribute is not None: result = getattr(result, output_attribute) if output_key is not None: return result.get(output_key) else: return result
def _resolve_ref(self, parsername, parameters): """Resolves references.""" parser = config.reference_parsers.get(parsername) if not parser: msg = "Invalid reference parser '{}' in layer '{}'".format( parsername, self.cf_name) raise ReferenceError(ref, msg, logger=self.logger) result = parser(self, config.boto_config, **parameters) return result
def j2_template(layer, config, path=None, s3_upload=False, params=None): """Render a j2 template and return the local or s3 path of the result. :param layer: The layer object for the layer declaring the reference. :param config: An object holding humilis configuration options. :param path: The path of the j2 template to render. Relative to meta.yml. :param s3_upload: Upload the rendered template to s3 or not. :param params: A dict containing the values to render the template. :returns: The local or s3 path of the rendered template. """ if params is None: msg = ("Missing params for j2 rendering in layer '{}' " "and env '{}'").format(layer.name, layer.env_name) ref = "j2_template '{}')".format(path) raise ReferenceError(ref, msg, logger=layer.logger) basefile, j2_ext = os.path.splitext(path) if j2_ext not in {".j2"}: msg = ("The file is not a Jinja2 template. layer : '{}', " "env : '{}'".format(layer.name, layer.env_name)) ref = "j2_template '{}')".format(path) raise ReferenceError(ref, msg, logger=layer.logger) _, ext = os.path.splitext(basefile) _, filename = os.path.split(path) env = jinja2.Environment( extensions=["jinja2.ext.with_"], loader=jinja2.FileSystemLoader(layer.basedir)) result = env.get_template(filename).render(params) output_path = os.path.join(layer.env_basedir, "ref-j2_template_" + str(uuid.uuid4()) + ext) with open(output_path, "w") as f: f.write(result) if s3_upload: s3bucket, s3key = _get_s3path(layer, config, output_path) s3 = S3(config) s3.cp(output_path, s3bucket, s3key) layer.logger.info("{} -> {}/{}".format(output_path, s3bucket, s3key)) return os.path.join("s3://", s3bucket, s3key) return output_path
def _get_stack_resource(layer, config, stack_name, resource_name): """Gets the physical ID of a resource in a CF Stack. :param stack_name: The name of the CF stack. :param resource_name: The logical name of the CF resource. :returns: The physical ID of the resource. """ cf = Cloudformation(config) resource = cf.get_stack_resource(stack_name, resource_name) if len(resource) < 1: all_stack_resources = [x.logical_resource_id for x in cf.get_stack_resources(stack_name)] msg = "{} does not exist in stack {} (with resources {}).".format( resource_name, stack_name, all_stack_resources) raise ReferenceError(resource_name, msg, logger=layer.logger) return resource[0].physical_resource_id