예제 #1
0
파일: config.py 프로젝트: makkus/freckles
    def get_config_type(self, config_obj):

        if isinstance(config_obj, string_types):
            config_obj = config_obj.strip()
            if config_obj.startswith("{") and config_obj.endswith("}"):
                return "json"
            elif "=" in config_obj:
                return "key_value"
            elif config_obj in self.default_config_dicts.keys():
                return "default_config_dict"
            elif config_obj in self.keys():
                return "user_config_dict"
            else:
                all_context_names = list(
                    self.default_config_dicts.keys()) + list(self.keys())
                raise FrklException(
                    msg="Can't determine config type for string '{}'".format(
                        config_obj),
                    solution=
                    "Value needs to be either json string, key/value pair (separated with '='), or the name of a default or user context config: {}"
                    .format(", ".join(all_context_names)),
                )
        elif isinstance(config_obj, Mapping):
            return "dict"
        else:
            raise FrklException(
                msg="Invalid config type '{}' for object: {}".format(
                    type(config_obj), config_obj),
                solution="Needs to be either string, or dict.",
            )
예제 #2
0
def get_value_from_var_adapter(var_adapter_name,
                               key,
                               arg,
                               root_arg,
                               frecklet=None,
                               is_secret=None,
                               inventory=None):

    if not isinstance(var_adapter_name, six.string_types):
        raise FrklException(
            msg="Internal error",
            reason="Not a var adapter: {}".format(var_adapter_name),
        )

    if var_adapter_name.startswith("value_from_"):

        if inventory is None:
            raise FrklException(
                "Can't retrieve value for var_adapter '{}'.".format(
                    var_adapter_name),
                reason="No inventory provided.",
                solution=
                "Don't use the '{}' var adapter in a non parent frecklet.",
            )

        inventory_secrets = inventory.secret_keys()

        copy_var_name = var_adapter_name[11:]
        value = inventory.retrieve_value(copy_var_name)

        secret = is_secret or copy_var_name in inventory_secrets
        return value, secret
    else:
        if var_adapter_name not in VAR_ADAPTERS.keys():
            raise FrecklesVarException(
                frecklet=frecklet,
                var_name=key,
                errors={key: "No var adapter '{}'.".format(var_adapter_name)},
                solution=
                "Double-check the var adapter name '{}', maybe there's a typo?\n\nIf the name is correct, make sure the python library that contains the var-adapter is installed in the same environment as freckles."
                .format(var_adapter_name),
            )

        var_adapter_obj = VAR_ADAPTERS[var_adapter_name]
        if is_secret is None:
            is_secret = arg.secret
        value = var_adapter_obj.retrieve_value(
            key_name=key,
            arg=arg,
            root_arg=root_arg,
            frecklet=frecklet,
            is_secret=is_secret,
        )
        return value, is_secret
예제 #3
0
def parse_target_string(target_string):

    if target_string == "vagrant" or target_string.startswith("vagrant:"):
        details = get_vagrant_details(target_string)

    elif "find-pi" in target_string:
        details = get_pi_details(target_string)

    elif "::" in target_string:
        c_type, target_string = target_string.split("::", 1)

        if c_type == "lxd":
            details = get_lxd_details(target_string=target_string)
        elif c_type == "docker":
            details = get_docker_details(target_string=target_string)
        elif c_type == "buildah":
            details = get_buildah_details(target_string=target_string)
        else:
            raise FrklException(
                msg="Can't parse target string.",
                reason="Unknown connection type: {}".format(c_type),
            )

        return details
    else:
        details = get_host_details(target_string)

    return details
예제 #4
0
    def load_frecklet(self,
                      frecklet_full_path_or_name_or_content,
                      validate=False):
        """Loads a frecklet.

        First, checksi if argument is a path and exists. If that is the case, uses that to create the frecklet. If not, tries
        to find a frecklet with the provided name. If that doesn't exists either, it tries to interprete the string as frecklet content.
        """

        if isinstance(frecklet_full_path_or_name_or_content, string_types):

            full_path = os.path.realpath(
                os.path.expanduser(frecklet_full_path_or_name_or_content))

            if os.path.isfile(frecklet_full_path_or_name_or_content):
                frecklet_name = self.add_dynamic_frecklet(
                    path_or_frecklet_content=full_path, validate=False)
                frecklet = self.get_frecklet(frecklet_name, validate=validate)
                return (frecklet, frecklet_name)

            if is_url_or_abbrev(frecklet_full_path_or_name_or_content):

                if not self.config_value("allow_remote"):
                    raise FrklException(
                        msg="Loading remote frecklet from not allowed: {}".
                        format(frecklet_full_path_or_name_or_content),
                        reason=
                        "Context config value 'allow_remote' set to 'false'.",
                        solution=
                        "Set config value 'allow_remote' to 'true', e.g. via the '--context allow_remote=true' cli argument.",
                        references={
                            "freckles remote configuration":
                            "https://freckles.io/doc/security#remote-repo-permission-config"
                        },
                    )

                content = content_from_url(
                    frecklet_full_path_or_name_or_content,
                    update=True,
                    cache_base=os.path.join(FRECKLES_CACHE_BASE,
                                            "remote_frecklets"),
                )
                frecklet_name = self.add_dynamic_frecklet(
                    path_or_frecklet_content=content, validate=validate)
                frecklet = self.get_frecklet(frecklet_name, validate=validate)
                return (frecklet, frecklet_name)

            if frecklet_full_path_or_name_or_content in self.get_frecklet_names(
            ):
                frecklet = self.get_frecklet(
                    frecklet_full_path_or_name_or_content, validate=validate)
                return (frecklet, frecklet_full_path_or_name_or_content)

        frecklet_name = self.add_dynamic_frecklet(
            frecklet_full_path_or_name_or_content, validate=validate)
        if frecklet_name:
            frecklet = self.get_frecklet(frecklet_name, validate=validate)
            return (frecklet, frecklet_name)

        return None, None
예제 #5
0
    def __init__(self, watch_path=None, adapter_log=False):

        if adapter_log and watch_path:
            raise FrklException(
                msg="Can only watch either the adapter log, or a specific path."
            )

        self._watch_path = watch_path
        self._adapter_log = adapter_log
        self._log_file_printers = {}
예제 #6
0
    def __init__(
        self,
        run_alias,
        watch_path=None,
        created_callback=None,
        callback=None,
        finished_callback=None,
        adapter_log=None,
        index=0,
    ):

        if adapter_log and watch_path:
            raise FrklException(
                msg="Can only watch either the adapter log, or a specific path."
            )

        if watch_path is None:
            watch_path = "run_log.json"

        if run_alias.startswith("__dyn_"):
            run_alias = "_no_name_"
        self._alias = run_alias
        self._index = index
        self._watch_path = watch_path
        self._created_callback = created_callback
        self._callback = callback
        self._finished_callback = finished_callback
        self._last_file_pos = 0
        self._adapter_log = adapter_log
        if not self._adapter_log:
            self._log_file = os.path.join(self._env_dir, self._watch_path)
        else:
            if self._adapter == "nsbl":
                self._log_file = os.path.join(self._env_dir,
                                              "nsbl/logs/ansible_run_log")
            else:
                raise FrklException(
                    msg="Watching logs for adapter '{}' not supported.")
        self._watch_dir = os.path.dirname(self._log_file)
예제 #7
0
    def add_result(self, result_id, result):

        try:
            registered = self._registers.get(result_id, None)
            if registered is None:
                raise FrklException(
                    msg=
                    "Result for id '{}' not registered, this is most likely a bug."
                    .format(result_id))

            value_template = registered.get("value", None)
            if value_template is not None:
                new_value = replace_strings_in_obj(
                    value_template,
                    replacement_dict={"__result__": result},
                    jinja_env=DEFAULT_RUN_CONFIG_JINJA_ENV,
                )
            else:
                new_value = result

            target = registered.get("target", None)
            if target is None:
                if not isinstance(result, Mapping):
                    raise FrklException(
                        msg="Can't merge result id '{}'".format(result_id),
                        reason=
                        "Value for result-id '{}' not a mapping, and no 'target' provided.",
                        solution=
                        "Either provide a 'target' value, or use the 'value' template to convert the result.",
                    )
                dict_merge(self._result, new_value, copy_dct=False)
            else:
                temp = {}
                dpath.new(temp, target, new_value, separator=".")
                dict_merge(self._result, temp, copy_dct=False)
        except (Exception) as e:
            log.error("Could not register result '{}': {}".format(
                result_id, e))
예제 #8
0
파일: config.py 프로젝트: makkus/freckles
    def __init__(self,
                 repo_name,
                 tingsets,
                 cnf,
                 default_config_dicts=None,
                 **kwargs):

        if default_config_dicts is None:
            default_config_dicts = DEFAULT_CONFIG_DICTS

        self.default_config_dicts = copy.deepcopy(default_config_dicts)

        invalid = []
        for tings in tingsets:
            for ting in tings.values():
                if ting.id in self.default_config_dicts.keys():
                    invalid.append(ting.id)

        if invalid:
            raise FrklException(
                msg="Invalid context name(s) for '{}': {}".format(
                    repo_name, ", ".join(invalid)),
                reason=
                "Context config files named after reserved context names in repo: {}"
                .format(", ".join(repo_name)),
                solution="Rename affected context configs.",
            )

        if cnf is None:
            raise Exception("Base configuration object can't be None.")

        if "profile_load" not in cnf.get_interpreter_names():
            raise Exception("No 'profile_load' cnf interpreter available.")
        load_config = cnf.get_interpreter("profile_load")

        if "root_config" not in cnf.get_interpreter_names():
            raise Exception("No root_config profile interpreter in cnf.")

        self._cnf = cnf
        self._root_config = cnf.get_interpreter("root_config").config

        super(ContextConfigs, self).__init__(
            repo_name=repo_name,
            tingsets=tingsets,
            load_config=load_config,
            indexes=["filename_no_ext"],
        )
예제 #9
0
    def retrieve_value(self,
                       key_name,
                       arg,
                       root_arg,
                       frecklet,
                       is_secret=False,
                       profile_names=None):

        if not hasattr(frecklet, "full_path"):
            raise FrklException(
                msg="Can't resolve variable value 'frecklet_dir'.",
                reason="frecklet in question is dynamic.",
                solution=
                "Only use the '::frecklet_dir::' var adapter in combination with local frecklets.",
            )

        return os.path.dirname(frecklet.full_path)
예제 #10
0
    def get_frecklet(self, frecklet_name, validate=False, index_name=None):

        if frecklet_name is None:
            raise FrklException(
                "Can't retrieve frecklet, no 'frecklet_name' provided.")

        result = self.frecklet_index.get_from_index(frecklet_name,
                                                    index_name=index_name)

        if validate:

            valid = result.valid
            if not valid:

                raise FreckletException(
                    frecklet=result,
                    parent_exception=result.invalid_exception,
                    frecklet_name=frecklet_name,
                )

        return result
예제 #11
0
    def register_task(self, frecklet_metadata):

        register = frecklet_metadata.get("register", None)
        if register is None:
            return

        if isinstance(register, string_types):
            register = {"target": register}

        if not isinstance(register, Mapping):
            raise TypeError("Invalid type for 'register' value: {}".format(
                type(register)))

        if not register.get("target", None):
            frecklet_metadata.pop("register", None)
            return

        if "value" not in register.keys():
            register["value"] = None

        if "id" not in register.keys():
            register_id = str(uuid.uuid4())
            register_id = slugify("result_" + register_id, separator="_")
            register_id.replace("-", "_")
            register["id"] = register_id

        if register["id"] in self._registers.keys():
            raise FrklException(
                "Can't register result with 'id': {}".format(register["id"]),
                reason="Id already registered.",
                solution="Specify a different id.",
            )

        self._registers[register["id"]] = register

        frecklet_metadata["register"] = register
예제 #12
0
파일: config.py 프로젝트: makkus/freckles
    def resolve_context_config_dict(self,
                                    config_chain,
                                    current_config_dict=None):

        if current_config_dict is None:
            current_config_dict = {}

        for config in config_chain:

            config_type = self.get_config_type(config)

            if config_type == "dict":
                config_dict = copy.deepcopy(config)

            elif config_type == "json":
                try:
                    config_dict = json.loads(config)
                except (Exception):
                    raise FrklException(
                        msg="Can't parse json config object: {}".format(
                            config),
                        solution="Check json format.",
                    )

            elif config_type == "key_value":
                key, value = config.split("=", 1)
                if value == "":
                    raise FrklException(
                        msg="No value provided for key/value config object: {}"
                        .format(config))
                if value.lower() in ["true", "yes"]:
                    value = True
                elif value.lower() in ["false", "no"]:
                    value = False
                else:
                    try:
                        value = int(value)
                    except (Exception):
                        # fine, we'll just use the string
                        # TODO: support lists
                        pass
                config_dict = {key: value}

            elif config_type == "default_config_dict":
                config_dict = copy.deepcopy(self.default_config_dicts[config])
            elif config_type == "user_config_dict":
                config_dict = copy.deepcopy(self.get(config).config_dict)
            else:
                raise FrklException(
                    msg="Invalid config type: {}".format(config_type))

            config_parents = config_dict.pop("parents", [])
            config_extra_repos = config_dict.pop("extra_repos", [])
            if isinstance(config_extra_repos, string_types) or not isinstance(
                    config_extra_repos, Sequence):
                config_extra_repos = [config_extra_repos]

            self.resolve_context_config_dict(
                config_chain=config_parents,
                current_config_dict=current_config_dict)
            dict_merge(current_config_dict, config_dict, copy_dct=False)

            if config_extra_repos:
                current_config_dict.setdefault("repos",
                                               []).extend(config_extra_repos)

        return current_config_dict