Beispiel #1
0
def test_start_build_disabled_trigger(app):
    trigger = model.build.list_build_triggers("devtable", "building")[0]
    trigger.enabled = False
    trigger.save()

    build = PreparedBuild(trigger=trigger)

    with pytest.raises(BuildTriggerDisabledException):
        start_build(trigger.repository, build)
Beispiel #2
0
    def post(self, namespace_name, repo_name, trigger_uuid):
        """
        Manually start a build from the specified trigger.
        """
        trigger = get_trigger(trigger_uuid)
        if not trigger.enabled:
            raise InvalidRequest("Trigger is not enabled.")

        handler = BuildTriggerHandler.get_handler(trigger)
        if not handler.is_active():
            raise InvalidRequest("Trigger is not active.")

        try:
            repo = model.repository.get_repository(namespace_name, repo_name)
            pull_robot_name = model.build.get_pull_robot_name(trigger)

            run_parameters = request.get_json()
            prepared = handler.manual_start(run_parameters=run_parameters)
            build_request = start_build(repo, prepared, pull_robot_name=pull_robot_name)
        except TriggerException as tse:
            raise InvalidRequest(tse.message)
        except MaximumBuildsQueuedException:
            abort(429, message="Maximum queued build rate exceeded.")
        except BuildTriggerDisabledException:
            abort(400, message="Build trigger is disabled")

        resp = build_status_view(build_request)
        repo_string = "%s/%s" % (namespace_name, repo_name)
        headers = {
            "Location": api.url_for(
                RepositoryBuildStatus, repository=repo_string, build_uuid=build_request.uuid
            ),
        }
        return resp, 201, headers
Beispiel #3
0
def test_maximum_builds(app):
    # Change the maximum number of builds to 1.
    user = model.user.create_user("foobar", "password", "*****@*****.**")
    user.maximum_queued_builds_count = 1
    user.save()

    repo = model.repository.create_repository("foobar", "somerepo", user)

    # Try to queue a build; should succeed.
    prepared_build = PreparedBuild()
    prepared_build.build_name = "foo"
    prepared_build.is_manual = True
    prepared_build.dockerfile_id = "foobar"
    prepared_build.archive_url = "someurl"
    prepared_build.tags = ["latest"]
    prepared_build.subdirectory = "/"
    prepared_build.context = "/"
    prepared_build.metadata = {}

    start_build(repo, prepared_build)

    # Try to queue a second build; should fail.
    with pytest.raises(MaximumBuildsQueuedException):
        start_build(repo, prepared_build)
Beispiel #4
0
    def post(self, namespace, repository):
        """
        Request that a repository be built and pushed from the specified input.
        """
        logger.debug("User requested repository initialization.")
        request_json = request.get_json()

        dockerfile_id = request_json.get("file_id", None)
        archive_url = request_json.get("archive_url", None)

        if not dockerfile_id and not archive_url:
            raise InvalidRequest("file_id or archive_url required")

        if archive_url:
            archive_match = None
            try:
                archive_match = urlparse(archive_url)
            except ValueError:
                pass

            if not archive_match:
                raise InvalidRequest(
                    "Invalid Archive URL: Must be a valid URI")

            scheme = archive_match.scheme
            if scheme != "http" and scheme != "https":
                raise InvalidRequest(
                    "Invalid Archive URL: Must be http or https")

        context, subdir = self.get_dockerfile_context(request_json)
        tags = request_json.get("docker_tags", ["latest"])
        pull_robot_name = request_json.get("pull_robot", None)

        # Verify the security behind the pull robot.
        if pull_robot_name:
            result = parse_robot_username(pull_robot_name)
            if result:
                try:
                    model.user.lookup_robot(pull_robot_name)
                except model.InvalidRobotException:
                    raise NotFound()

                # Make sure the user has administer permissions for the robot's namespace.
                (robot_namespace, _) = result
                if not AdministerOrganizationPermission(robot_namespace).can():
                    raise Unauthorized()
            else:
                raise Unauthorized()

        # Check if the dockerfile resource has already been used. If so, then it
        # can only be reused if the user has access to the repository in which the
        # dockerfile was previously built.
        if dockerfile_id:
            associated_repository = model.build.get_repository_for_resource(
                dockerfile_id)
            if associated_repository:
                if not ModifyRepositoryPermission(
                        associated_repository.namespace_user.username,
                        associated_repository.name):
                    raise Unauthorized()

        # Start the build.
        repo = model.repository.get_repository(namespace, repository)
        if repo is None:
            raise NotFound()

        try:
            build_name = (user_files.get_file_checksum(dockerfile_id)
                          if dockerfile_id else hashlib.sha224(
                              archive_url.encode("ascii")).hexdigest()[0:7])
        except IOError:
            raise InvalidRequest("File %s could not be found or is invalid" %
                                 dockerfile_id)

        prepared = PreparedBuild()
        prepared.build_name = build_name
        prepared.dockerfile_id = dockerfile_id
        prepared.archive_url = archive_url
        prepared.tags = tags
        prepared.subdirectory = subdir
        prepared.context = context
        prepared.is_manual = True
        prepared.metadata = {}
        try:
            build_request = start_build(repo,
                                        prepared,
                                        pull_robot_name=pull_robot_name)
        except MaximumBuildsQueuedException:
            abort(429, message="Maximum queued build rate exceeded.")
        except BuildTriggerDisabledException:
            abort(400, message="Build trigger is disabled")

        resp = build_status_view(build_request)
        repo_string = "%s/%s" % (namespace, repository)
        headers = {
            "Location":
            api.url_for(RepositoryBuildStatus,
                        repository=repo_string,
                        build_uuid=build_request.uuid),
        }
        return resp, 201, headers
Beispiel #5
0
def build_trigger_webhook(trigger_uuid, **kwargs):
    logger.debug("Webhook received with uuid %s", trigger_uuid)

    try:
        trigger = model.build.get_build_trigger(trigger_uuid)
    except model.InvalidBuildTriggerException:
        # It is ok to return 404 here, since letting an attacker know that a trigger UUID is valid
        # doesn't leak anything
        abort(404)

    # Ensure we are not currently in read-only mode.
    if app.config.get("REGISTRY_STATE", "normal") == "readonly":
        abort(503, "System is currently in read-only mode")

    # Ensure the trigger has permission.
    namespace = trigger.repository.namespace_user.username
    repository = trigger.repository.name
    if ModifyRepositoryPermission(namespace, repository).can():
        handler = BuildTriggerHandler.get_handler(trigger)

        if trigger.repository.kind.name != "image":
            abort(
                501,
                "Build triggers cannot be invoked on application repositories")

        if trigger.repository.state != RepositoryState.NORMAL:
            abort(503, "Repository is currently in read only or mirror mode")

        logger.debug("Passing webhook request to handler %s", handler)
        try:
            prepared = handler.handle_trigger_request(request)
        except ValidationRequestException:
            logger.debug("Handler reported a validation exception: %s",
                         handler)
            # This was just a validation request, we don't need to build anything
            return make_response("Okay")
        except SkipRequestException:
            logger.debug("Handler reported to skip the build: %s", handler)
            # The build was requested to be skipped
            return make_response("Okay")
        except InvalidPayloadException as ipe:
            logger.exception("Invalid payload")
            # The payload was malformed
            abort(400, message=str(ipe))

        pull_robot_name = model.build.get_pull_robot_name(trigger)
        repo = model.repository.get_repository(namespace, repository)
        try:
            start_build(repo, prepared, pull_robot_name=pull_robot_name)
        except MaximumBuildsQueuedException:
            abort(429, message="Maximum queued build rate exceeded.")
        except BuildTriggerDisabledException:
            logger.debug("Build trigger %s is disabled", trigger_uuid)
            abort(
                400,
                message=
                "This build trigger is currently disabled. Please re-enable to continue.",
            )

        return make_response("Okay")

    abort(403)