Exemplo n.º 1
0
def test_feedstock_tokens_roundtrip(gh_mock, git_mock, tmp_mock, tmpdir, repo,
                                    project):
    gh_mock.return_value = "abc123"
    tmp_mock.TemporaryDirectory.return_value.__enter__.return_value = str(
        tmpdir)

    user = "******"
    pth = os.path.expanduser("~/.conda-smithy/foo_%s.token" % project)
    token_json_pth = os.path.join(tmpdir, "tokens", "%s.json" % project)
    os.makedirs(os.path.join(tmpdir, "tokens"), exist_ok=True)

    try:
        generate_and_write_feedstock_token(user, project)
        assert os.path.exists(pth)

        register_feedstock_token(user, project, repo)
        assert os.path.exists(token_json_pth)

        with open(pth, "r") as fp:
            feedstock_token = fp.read().strip()

        retval = is_valid_feedstock_token(user, project, feedstock_token, repo)
    finally:
        if os.path.exists(pth):
            os.remove(pth)

    assert retval
Exemplo n.º 2
0
    async def post(self):
        headers = self.request.headers
        feedstock_token = headers.get('FEEDSTOCK_TOKEN', None)
        data = tornado.escape.json_decode(self.request.body)
        feedstock = data.get("feedstock", None)

        LOGGER.info("")
        LOGGER.info("===================================================")
        LOGGER.info("token registration for feedstock '%s'" % feedstock)
        LOGGER.info("===================================================")

        if (
            feedstock_token is None
            or feedstock is None
            or not is_valid_feedstock_token(
                "conda-forge", "staged-recipes", feedstock_token, TOKENS_REPO)
        ):
            LOGGER.warning('    invalid token registration request for %s!' % feedstock)
            self.set_status(403)
            self.write_error(403)
        else:
            register_error = await tornado.ioloop.IOLoop.current().run_in_executor(
                _thread_pool(),
                register_feedstock_token_handler,
                feedstock,
            )

            if register_error:
                LOGGER.info('    failed token registration request for %s!' % feedstock)
                self.set_status(403)
                self.write_error(403)
            else:
                LOGGER.info('    token registration request for %s worked!' % feedstock)

        return
Exemplo n.º 3
0
def validate_feedstock_outputs(
    project,
    outputs,
    feedstock_token,
    register=True,
):
    """Validate feedstock outputs on the staging channel.

    Parameters
    ----------
    project : str
        The name of the feedstock.
    outputs : dict
        A dictionary mapping each full qualified output in the conda index
        to a hash ("md5"), its name ("name"), and version ("version").
    feedstock_token : str
        The secret token used to validate that this feedstock is who it says
        it is.
    register : bool
        If True, attempt to register any outputs that do not exist by pushing
        the proper json blob to `output_repo`. Default is True.

    Returns
    -------
    valid : dict
        A dict keyed on the keys in `outputs` with values True in the output
        is valid and False otherwise.
    """
    valid = {o: False for o in outputs}

    if not is_valid_feedstock_token("conda-forge", project, feedstock_token,
                                    TOKENS_REPO):
        return valid, ["invalid feedstock token"]

    valid_outputs = _is_valid_feedstock_output(
        project,
        [o["name"] for _, o in outputs.items()],
        register=register,
    )

    valid_hashes = _is_valid_output_hash(outputs)

    errors = []
    for o in outputs:
        _errors = []
        if not valid_outputs[outputs[o]["name"]]:
            _errors.append("output %s not allowed for conda-forge/%s" %
                           (o, project))
        if not valid_hashes[o]:
            _errors.append("output %s does not have a valid md5 checksum" % o)

        if len(_errors) > 0:
            errors.extend(_errors)
        else:
            valid[o] = True

    return valid, errors
Exemplo n.º 4
0
def test_is_valid_feedstock_token_nofile(gh_mock, git_mock, tmp_mock, tmpdir,
                                         repo, project):
    gh_mock.return_value = "abc123"
    tmp_mock.TemporaryDirectory.return_value.__enter__.return_value = str(
        tmpdir)

    user = "******"
    feedstock_token = "akdjhfl"
    retval = is_valid_feedstock_token(user, project, feedstock_token, repo)
    assert not retval
Exemplo n.º 5
0
def test_is_valid_feedstock_token_badtoken(gh_mock, git_mock, tmp_mock, tmpdir,
                                           repo, project):
    gh_mock.return_value = "abc123"
    tmp_mock.TemporaryDirectory.return_value.__enter__.return_value = str(
        tmpdir)

    user = "******"
    feedstock_token = "akdjhfl"

    token_pth = os.path.join(tmpdir, "tokens", "%s.json" % project)
    os.makedirs(os.path.dirname(token_pth), exist_ok=True)
    with open(token_pth, "w") as fp:
        json.dump({"salt": b"adf".hex(), "hashed_token": b"fgh".hex()}, fp)

    retval = is_valid_feedstock_token(user, project, feedstock_token, repo)
    assert not retval
Exemplo n.º 6
0
    async def post(self):
        headers = self.request.headers
        feedstock_token = headers.get('FEEDSTOCK_TOKEN', None)
        data = tornado.escape.json_decode(self.request.body)
        feedstock = data.get("feedstock", None)
        outputs = data.get("outputs", None)
        channel = data.get("channel", None)

        LOGGER.info("")
        LOGGER.info("===================================================")
        LOGGER.info("copy outputs for feedstock '%s'" % feedstock)
        LOGGER.info("===================================================")

        if (
            feedstock_token is None
            or feedstock is None
            or outputs is None
            or channel is None
            or not is_valid_feedstock_token(
                "conda-forge", feedstock, feedstock_token, TOKENS_REPO)
        ):
            LOGGER.warning('    invalid outputs copy request for %s!' % feedstock)
            self.set_status(403)
            self.write_error(403)
        else:
            valid, errors = await tornado.ioloop.IOLoop.current().run_in_executor(
                _thread_pool(),
                validate_feedstock_outputs,
                feedstock,
                outputs,
                feedstock_token,
            )

            outputs_to_copy = {}
            for o in valid:
                if valid[o]:
                    outputs_to_copy[o] = outputs[o]

            if outputs_to_copy:
                copied = await tornado.ioloop.IOLoop.current().run_in_executor(
                    _thread_pool(),
                    copy_feedstock_outputs,
                    outputs_to_copy,
                    channel,
                )
            else:
                copied = {}

            for o in outputs:
                if o not in copied:
                    copied[o] = False

            if not all(v for v in copied.values()):
                self.set_status(403)

            self.write(json.dumps({"errors": errors, "valid": valid, "copied": copied}))

            LOGGER.info("    errors: %s\n    valid: %s\n    copied: %s" % (
                errors, valid, copied))

        return
def validate_feedstock_outputs(
    project,
    outputs,
    feedstock_token,
):
    """Validate feedstock outputs on the staging channel.

    Parameters
    ----------
    project : str
        The name of the feedstock.
    outputs : dict
        A dictionary mapping each output to its md5 hash. The keys should be the
        full names with the platform directory, version/build info, and file extension
        (e.g., `noarch/blah-fa31b0-2020.04.13.15.54.07-py_0.tar.bz2`).
    feedstock_token : str
        The secret token used to validate that this feedstock is who it says
        it is.

    Returns
    -------
    valid : dict
        A dict keyed on the keys in `outputs` with values True in the output
        is valid and False otherwise.
    errors : list of str
        A list of any errors encountered.
    """
    valid = {o: False for o in outputs}

    if not is_valid_feedstock_token(
        "conda-forge", project, feedstock_token, TOKENS_REPO
    ):
        return valid, ["invalid feedstock token"]

    errors = []

    correctly_formatted = {}
    for o in outputs:
        try:
            parse_conda_pkg(o)
            correctly_formatted[o] = True
        except RuntimeError:
            correctly_formatted[o] = False
            errors.append(
                "output '%s' is not correctly formatted (it must be the fully "
                "qualified name w/ extension, `noarch/blah-fa31b0-2020.04.13.15"
                ".54.07-py_0.tar.bz2`)" % o
            )

    outputs_to_test = {o: v for o, v in outputs.items() if correctly_formatted[o]}

    valid_outputs = is_valid_feedstock_output(
        project,
        outputs_to_test,
        register=True,
    )

    valid_hashes = _is_valid_output_hash(outputs_to_test)

    for o in outputs_to_test:
        _errors = []
        if not valid_outputs[o]:
            _errors.append(
                "output %s not allowed for conda-forge/%s" % (o, project)
            )
        if not valid_hashes[o]:
            _errors.append("output %s does not have a valid md5 checksum" % o)

        if len(_errors) > 0:
            errors.extend(_errors)
        else:
            valid[o] = True

    return valid, errors