Пример #1
0
def load_experiment(experiment_source: str,
                    settings: Settings = None,
                    verify_tls: bool = True) -> Experiment:
    """
    Load an experiment from the given source.

    The source may be a local file or a HTTP(s) URL. If the endpoint requires
    authentication, please set the appropriate entry in the settings file,
    under the `auths:` section, keyed by domain. For instance:

    ```yaml
    auths:
      mydomain.com:
        type: basic
        value: XYZ
      otherdomain.com:
        type: bearer
        value: UIY
      localhost:8081:
        type: digest
        value: UIY
    ```

    Set `verify_tls` to `False` if the source is a over a self-signed
    certificate HTTP endpoint to instruct the loader to not verify the
    certificates.
    """
    with controls(level="loader", context=experiment_source) as control:
        if os.path.exists(experiment_source):
            parsed = parse_experiment_from_file(experiment_source)
            control.with_state(parsed)
            return parsed

        p = urlparse(experiment_source)
        if not p.scheme and not os.path.exists(p.path):
            raise InvalidSource('Path "{}" does not exist.'.format(p.path))

        if p.scheme not in ("http", "https"):
            raise InvalidSource(
                "'{}' is not a supported source scheme.".format(p.scheme))

        headers = {"Accept": "application/json, application/x-yaml"}
        if settings:
            auths = settings.get("auths", [])
            for domain in auths:
                if domain == p.netloc:
                    auth = auths[domain]
                    headers["Authorization"] = "{} {}".format(
                        auth["type"], auth["value"])
                    break

        r = requests.get(experiment_source, headers=headers, verify=verify_tls)
        if r.status_code != 200:
            raise InvalidSource("Failed to fetch the experiment: {}".format(
                r.text))

        logger.debug("Fetched experiment: \n{}".format(r.text))
        parsed = parse_experiment_from_http(r)
        control.with_state(parsed)
        return parsed
Пример #2
0
def load_experiment(experiment_source: str,
                    settings: Settings = None) -> Experiment:
    """
    Load an experiment from the given source.

    The source may be a local file or a HTTP(s) URL. If the endpoint requires
    authentication, please set the appropriate entry in the settings file,
    under the `auths:` section, keyed by domain. For instance:

    ```yaml
    auths:
      mydomain.com:
        type: basic
        value: XYZ
      otherdomain.com:
        type: bearer
        value: UIY
      localhost:8081:
        type: digest
        value: UIY
    ```
    """
    if os.path.exists(experiment_source):
        return parse_experiment_from_file(experiment_source)

    p = urlparse(experiment_source)
    if not p.scheme and not os.path.exists(p.path):
        raise InvalidSource('Path "{}" does not exist.'.format(p.path))

    if p.scheme not in ("http", "https"):
        raise InvalidSource("'{}' is not a supported source scheme.".format(
            p.scheme))

    headers = {"Accept": "application/json, application/x-yaml"}
    if settings:
        auths = settings.get("auths", [])
        for domain in auths:
            if domain == p.netloc:
                auth = auths[domain]
                headers["Authorization"] = '{} {}'.format(
                    auth["type"], auth["value"])
                break

    r = requests.get(experiment_source, headers=headers)
    if r.status_code != 200:
        raise InvalidSource("Failed to fetch the experiment: {}".format(
            r.text))

    logger.debug("Fetched experiment: \n{}".format(r.text))
    return parse_experiment_from_http(r)
Пример #3
0
def parse_experiment_from_http(response: requests.Response) -> Experiment:
    """
    Parse the given experiment from the request's `response`.
    """
    content_type = response.headers.get("Content-Type")

    if 'application/json' in content_type:
        return response.json()
    elif 'application/x-yaml' in content_type or 'text/yaml' in content_type:
        try:
            return yaml.safe_load(response.text)
        except yaml.YAMLError as ye:
            raise InvalidSource("Failed parsing YAML experiment: {}".format(
                str(ye)))
    elif 'text/plain' in content_type:
        content = response.text
        try:
            return json.loads(content)
        except JSONDecodeError:
            try:
                return yaml.safe_load(content)
            except yaml.YAMLError:
                pass

    raise InvalidExperiment(
        "only files with json, yaml or yml extensions are supported")
Пример #4
0
def set_run_context(settings):

    try:
        update_settings_from_env(settings)
        ensure_settings_are_valid(settings)
    except Exception as x:
        logger.debug(x)
        logger.error(
            "Your experiment results will not be uploaded to the cloud. " +
            str(x))
        raise Exception(str(x))

    params = click.get_current_context().params

    # configure upload options
    try:
        vcs_info = vcs_information_factory().as_dict(params.get('source'))
        settings = add_to_run_context(settings, 'vcs', vcs_info)
        add_to_run_context(settings, 'no_upload', False)
    except Exception as ex:
        logger.debug(ex)
        logger.warning(
            "Your experiment results will not be uploaded to the cloud. "
            "Run an experiment within your repository.")
        add_to_run_context(settings, 'no_upload', True)

    add_to_run_context(settings, 'description', params.get('description'))

    # set experiment path and verify if it is on local drive
    source = params.get('source')
    filename = click.format_filename(source)
    if not os.path.exists(filename):
        raise InvalidSource('Path "{}" does not exist.'.format(filename))
    add_to_run_context(settings, 'path', source)

    trigger = 'manual'
    if 'CHAOS_TASK_ID' in os.environ:
        trigger = 'ci'
    task = None
    if trigger == 'ci':
        task = {
            'id': os.environ.get('CHAOS_TASK_ID', None),
            'uri': os.environ.get('CHAOS_TASK_URI', None)
        }

    add_to_run_context(settings, 'trigger', trigger)
    add_to_run_context(settings, 'task', task)
Пример #5
0
def parse_experiment_from_file(path: str) -> Experiment:
    """
    Parse the given experiment from `path` and return it.
    """
    with io.open(path) as f:
        p, ext = os.path.splitext(path)
        if ext in (".yaml", ".yml"):
            try:
                return yaml.safe_load(f)
            except yaml.YAMLError as ye:
                raise InvalidSource(
                    "Failed parsing YAML experiment: {}".format(str(ye)))
        elif ext == ".json":
            return json.load(f)

    raise InvalidExperiment(
        "only files with json, yaml or yml extensions are supported")
Пример #6
0
def switch_team_during_verification_run(
        source: str,  # noqa: C901
        settings: Settings) -> bool:
    """
    Verification may be run in a different team than the active team the user
    selected. Rather than preventing the verification from running, try to
    switch to the appropriate team's context for the duration of this run.

    It's all in memory and not changed on disk.
    """
    if not has_chaosiq_extension_configured(settings):
        logger.fatal(
            "Please signin to ChaosIQ services first with `$ chaos signin`")
        return False

    base_url = get_endpoint_url(settings)
    verify_tls = get_verify_tls(settings)
    default_org = get_default_org(settings)
    team = get_default_team(default_org)
    if not team:
        logger.fatal("Please select a default team with `$chaos team`")
        return False
    team_id = team["id"]

    token = get_auth_token(settings, base_url)
    if not token:
        logger.fatal(
            "Please signin to ChaosIQ services first with `$ chaos signin`")

    p = urlparse(source)
    if p.scheme.lower() in ["http", "https"]:
        r = requests.get(source,
                         headers={"Authorization": "Bearer {}".format(token)},
                         verify=verify_tls)
        if r.status_code != 200:
            logger.fatal("Failed to retrieve verification at '{}': {}".format(
                source, r.text))
            return False

        experiment = r.json()
        experiment_team_id = get_team_id(experiment)
        if experiment_team_id:
            team_id = experiment_team_id
    else:
        if not os.path.exists(p.path):
            raise InvalidSource('Path "{}" does not exist.'.format(source))
        experiment = parse_experiment_from_file(source)
        experiment_team_id = get_team_id(experiment)
        if experiment_team_id:
            team_id = experiment_team_id

    if not team_id:
        logger.fatal(
            "Failed to lookup the team identifier from the verification. "
            "Are you trying to run a verification using an experiment you "
            "created manually? This is not possible right now unfortunately.")
        return False

    if team["id"] != team_id:
        team_url = urls.team(urls.org(urls.base(base_url),
                                      organization_id=default_org["id"]),
                             team_id=team_id)

        r = request_team(team_url, token, verify_tls)
        if r.status_code != 200:
            logger.fatal("You cannot access the team owning this verification."
                         "Please request them to join the team.")
            return False

        team = r.json()
        if default_org["id"] != team["org_id"]:
            logger.fatal(
                "You must be signed in to the appropriate organization to run "
                "this verification. Please run `$ chaos signin`.")
            return False

        logger.debug("Running a verification in a team different from the "
                     "active one. Activating '{}' for this run.".format(
                         team["name"]))

        set_default_team(default_org, {
            "id": team_id,
            "default": True,
            "name": team["name"]
        })

    return True