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)
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
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)
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
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)