Beispiel #1
0
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]
Beispiel #2
0
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
Beispiel #3
0
    def __init__(self,
                 __env,
                 __name,
                 layer_type=None,
                 logger=None,
                 loader=None,
                 humilis_profile=None,
                 **user_params):
        self.__environment_repr = repr(__env)
        self.environment = __env
        if not humilis_profile:
            self.cf = self.environment.cf
        else:
            config.boto_config.activate_profile(humilis_profile)
            self.cf = Cloudformation(config.boto_config)
        if logger is None:
            self.logger = logging.getLogger(__name__)
            # To prevent warnings
            self.logger.addHandler(logging.NullHandler())
        else:
            self.logger = logger
        self.name = __name
        self.env_name = self.environment.name
        self.env_stage = self.environment.stage
        self.env_basedir = self.environment.basedir
        self.depends_on = []
        self.section = {}
        self.type = layer_type
        self.s3_prefix = "{base}{env}/{stage}/{layer}/".format(
            base=config.boto_config.profile.get("s3prefix"),
            env=self.environment.name,
            stage=self.environment.stage,
            layer=__name)

        if layer_type is not None:
            basedir = config.layers.get(layer_type)
            if not basedir:
                msg = ("The plugin providing the layer type '{}' is not "
                       "installed in this system. Please install it and "
                       "try again.").format(layer_type)
                raise MissingPluginError(msg)
        else:
            basedir = None

        if basedir is None:
            basedir = os.path.join(self.env_basedir, 'layers', self.name)

        self.basedir = basedir

        if loader is None:
            loader = DirTreeBackedObject(basedir, self.logger)

        self.loader = loader

        # These param set will be sent to the template compiler and will be
        # populated once the layers this layer depend on have been created.
        self.params = {}

        # the parameters that will be used to compile meta.yaml
        self.meta = {}
        meta_params = {
            p[0]: p[1]
            for p in itertools.chain(self.loader_params.items(),
                                     user_params.items())
        }
        self.meta = self.loader.load_section('meta', params=meta_params)
        self.sns_topic_arn = self.environment.sns_topic_arn
        self.tags = {
            'humilis:environment': self.env_name,
            'humilis:layer': self.name,
            'humilis:stage': self.env_stage,
            'humilis:created': str(datetime.datetime.now())
        }
        for tagname, tagvalue in self.environment.tags.items():
            self.tags[tagname] = tagvalue

        for tagname, tagvalue in self.meta.get('tags', {}).items():
            self.tags[tagname] = tagvalue

        self.yaml_params = self.meta.get('parameters', {})
        for k, v in self.yaml_params.items():
            # Set 1 as default priority for all parameters
            v['priority'] = v.get('priority', 1)

        # User params override what is in the layer definition file
        self.user_params = user_params
        for pname, pvalue in user_params.items():
            if pname in self.yaml_params:
                self.yaml_params[pname]['value'] = pvalue
        self.__ec2 = None
        self.__s3 = None
Beispiel #4
0
    def __init__(self,
                 yml_path,
                 logger=None,
                 stage=None,
                 vault_layer=None,
                 parameters=None):
        if logger is None:
            self.logger = logging.getLogger(__name__)
            # To prevent warnings
            self.logger.addHandler(logging.NullHandler())
        else:
            self.logger = logger
        self.__yml_path = yml_path
        self.stage = stage and stage.upper()
        basedir, envfile = os.path.split(yml_path)
        self.basedir = os.path.abspath(basedir)
        self._j2_env = j2.Environment(extensions=["jinja2.ext.with_"],
                                      loader=j2.FileSystemLoader(self.basedir))
        # Add custom functions and filters
        utils.update_jinja2_env(self._j2_env)
        if parameters is None:
            parameters = {}

        if "_default" in parameters:
            def_params = parameters.get("_default", {})
            del parameters["_default"]
            parameters.update(def_params)

        parameters.update(os.environ)
        with open(yml_path, 'r') as f:
            if os.path.splitext(yml_path)[1] == ".j2":
                template = self._j2_env.get_template(envfile)
                meta = yaml.load(template.render(stage=stage, **parameters))
            else:
                meta = yaml.load(f)

        self.name = list(meta.keys())[0]
        self.meta = meta.get(self.name)

        if len(self.meta) == 0:
            raise FileFormatError(yml_path,
                                  "Error getting environment name ",
                                  logger=self.logger)

        self.cf = Cloudformation(config.boto_config)
        self.sns_topic_arn = self.meta.get('sns-topic-arn', [])
        self.tags = self.meta.get('tags', {})
        self.tags['humilis:environment'] = self.name

        self.layers = []
        for layer in self.meta.get('layers', []):
            layer_name = layer.get('layer', None)
            if layer_name is None:
                msg = "Wrongly formatted layer: {}".format(layer)
                raise FileFormatError(yml_path, msg)
            if layer.get('disable', False):
                message = ("Layer {} is disabled by configuration. "
                           "Skipping.".format(layer.get('layer')))
                self.logger.warning(message)
                continue

            # Get the layer params provided in the environment spec
            layer_params = {k: v for k, v in layer.items() if k != 'layer'}
            layer_obj = Layer(self, layer_name, **layer_params)
            self.layers.append(layer_obj)

        self.vault_layer = self.get_layer(vault_layer or 'secrets-vault')
        self.__secrets_table_name = "{}-{}-secrets".format(
            self.name, self.stage)
        if self.stage:
            self.__keychain_namespace = "{}:{}".format(self.name,
                                                       self.stage.lower())
        else:
            self.__keychain_namespace = self.name

        self.__dynamodb = None
Beispiel #5
0
def cf():
    """Create a Cloudformation facade object"""
    yield Cloudformation(config.boto_config)
Beispiel #6
0
    def __init__(self,
                 yml_path,
                 logger=None,
                 stage=None,
                 vault_layer=None,
                 parameters=None):
        if logger is None:
            self.logger = logging.getLogger(__name__)
            # To prevent warnings
            self.logger.addHandler(logging.NullHandler())
        else:
            self.logger = logger

        if stage is None:
            raise ValueError("stage can't be None")

        self.__yml_path = yml_path
        self.stage = stage and stage.upper()
        basedir, envfile = os.path.split(yml_path)
        self.basedir = os.path.abspath(basedir)
        self._j2_env = j2.Environment(loader=j2.FileSystemLoader(self.basedir))
        # Add custom functions and filters
        utils.update_jinja2_env(self._j2_env)

        parameters = self._preprocess_parameters(parameters)
        with open(yml_path, 'r') as f:
            if os.path.splitext(yml_path)[1] == ".j2":
                template = self._j2_env.get_template(envfile)
                meta = yaml.load(
                    template.render(
                        stage=stage,  # Backwards compatibility
                        __context={
                            'stage': stage,
                            'aws': {
                                'account_id':
                                boto3.client('sts').get_caller_identity().get(
                                    'Account')
                            }
                        },
                        __env=os.environ,
                        **parameters),
                    Loader=yaml.FullLoader)
            else:
                meta = yaml.load(f, Loader=yaml.FullLoader)

        self.name = list(meta.keys())[0]
        self.meta = meta.get(self.name)

        if len(self.meta) == 0:
            raise FileFormatError(yml_path,
                                  "Error getting environment name ",
                                  logger=self.logger)

        self.cf = Cloudformation(config.boto_config)
        self.sns_topic_arn = self.meta.get('sns-topic-arn', [])
        self.tags = self.meta.get('tags', {})
        self.tags['humilis:environment'] = self.name

        self.layers = []
        for layer in self.meta.get('layers', []):
            layer_name = layer.get('layer', None)
            if layer_name is None:
                msg = "Wrongly formatted layer: {}".format(layer)
                raise FileFormatError(yml_path, msg)
            if layer.get('disable', False):
                message = ("Layer {} is disabled by configuration. "
                           "Skipping.".format(layer.get('layer')))
                self.logger.warning(message)
                continue

            # Get the layer params provided in the environment spec
            layer_params = {k: v for k, v in layer.items() if k != 'layer'}
            layer_obj = Layer(self, layer_name, **layer_params)
            self.layers.append(layer_obj)

        self.vault_layer = self.get_layer(vault_layer or 'secrets-vault')
        self.__secrets_table_name = "{}-{}-secrets".format(
            self.name, self.stage)
        self.__keychain_namespace = "{}:{}".format(self.name,
                                                   self.stage.lower())
        self.__dynamodb = None