Beispiel #1
0
 def get(self, namespace_name, repository_name, uuid):
     """ Get information for the specified notification. """
     found = model.get_repo_notification(uuid)
     if not found:
         raise NotFound()
     return found.to_dict()
Beispiel #2
0
  def post(self, namespace_name, repo_name, trigger_uuid):
    """ Activate the specified build trigger. """
    trigger = get_trigger(trigger_uuid)
    handler = BuildTriggerHandler.get_handler(trigger)
    if handler.is_active():
      raise InvalidRequest('Trigger config is not sufficient for activation.')

    user_permission = UserAdminPermission(trigger.connected_user.username)
    if user_permission.can():
      # Update the pull robot (if any).
      pull_robot_name = request.get_json().get('pull_robot', None)
      if pull_robot_name:
        try:
          pull_robot = 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, _) = parse_robot_username(pull_robot_name)
        if not AdministerOrganizationPermission(robot_namespace).can():
          raise Unauthorized()

        # Make sure the namespace matches that of the trigger.
        if robot_namespace != namespace_name:
          raise Unauthorized()

        # Set the pull robot.
        trigger.pull_robot = pull_robot

      # Update the config.
      new_config_dict = request.get_json()['config']

      write_token_name = 'Build Trigger: %s' % trigger.service.name
      write_token = model.token.create_delegate_token(namespace_name, repo_name, write_token_name,
                                                      'write')

      try:
        path = url_for('webhooks.build_trigger_webhook', trigger_uuid=trigger.uuid)
        authed_url = _prepare_webhook_url(app.config['PREFERRED_URL_SCHEME'],
                                          '$token', write_token.get_code(),
                                          app.config['SERVER_HOSTNAME'], path)

        handler = BuildTriggerHandler.get_handler(trigger, new_config_dict)
        final_config, private_config = handler.activate(authed_url)

        if 'private_key' in private_config:
          trigger.secure_private_key = DecryptedValue(private_config['private_key'])

          # TODO(remove-unenc): Remove legacy field.
          if ActiveDataMigration.has_flag(ERTMigrationFlags.WRITE_OLD_FIELDS):
            trigger.private_key = private_config['private_key']

      except TriggerException as exc:
        write_token.delete_instance()
        raise request_error(message=exc.message)

      # Save the updated config.
      update_build_trigger(trigger, final_config, write_token=write_token)

      # Log the trigger setup.
      repo = model.repository.get_repository(namespace_name, repo_name)
      log_action('setup_repo_trigger', namespace_name,
                 {'repo': repo_name, 'namespace': namespace_name,
                  'trigger_id': trigger.uuid, 'service': trigger.service.name,
                  'pull_robot': trigger.pull_robot.username if trigger.pull_robot else None,
                  'config': final_config},
                 repo=repo)

      return trigger_view(trigger, can_admin=True)
    else:
      raise Unauthorized()
Beispiel #3
0
    def post(self, namespace_name, repository_name, manifestref):
        """
        Adds a new label into the tag manifest.
        """
        label_data = request.get_json()

        # Check for any reserved prefixes.
        if label_validator.has_reserved_prefix(label_data["key"]):
            abort(400, message="Label has a reserved prefix")

        repo_ref = registry_model.lookup_repository(namespace_name, repository_name)
        if repo_ref is None:
            raise NotFound()

        manifest = registry_model.lookup_manifest_by_digest(repo_ref, manifestref)
        if manifest is None:
            raise NotFound()

        label = None
        try:
            label = registry_model.create_manifest_label(
                manifest, label_data["key"], label_data["value"], "api", label_data["media_type"]
            )
        except InvalidLabelKeyException:
            message = (
                "Label is of an invalid format or missing please "
                + "use %s format for labels" % VALID_LABEL_KEY_REGEX
            )
            abort(400, message=message)
        except InvalidMediaTypeException:
            message = (
                "Media type is invalid please use a valid media type: text/plain, application/json"
            )
            abort(400, message=message)

        if label is None:
            raise NotFound()

        metadata = {
            "id": label.uuid,
            "key": label.key,
            "value": label.value,
            "manifest_digest": manifestref,
            "media_type": label.media_type_name,
            "namespace": namespace_name,
            "repo": repository_name,
        }

        log_action("manifest_label_add", namespace_name, metadata, repo_name=repository_name)

        resp = {"label": _label_dict(label)}
        repo_string = "%s/%s" % (namespace_name, repository_name)
        headers = {
            "Location": api.url_for(
                ManageRepositoryManifestLabel,
                repository=repo_string,
                manifestref=manifestref,
                labelid=label.uuid,
            ),
        }
        return resp, 201, headers
Beispiel #4
0
    def put(self, namespace_name, repository_name):
        """ Allow users to modifying the repository's mirroring configuration. """
        values = request.get_json()

        repo = model.repository.get_repository(namespace_name, repository_name)
        if not repo:
            raise NotFound()

        mirror = model.repo_mirror.get_mirror(repo)
        if not mirror:
            raise NotFound()

        if 'is_enabled' in values:
            if values['is_enabled'] == True:
                if model.repo_mirror.enable_mirror(repo):
                    track_and_log('repo_mirror_config_changed',
                                  wrap_repository(repo),
                                  changed='is_enabled',
                                  to=True)
            if values['is_enabled'] == False:
                if model.repo_mirror.disable_mirror(repo):
                    track_and_log('repo_mirror_config_changed',
                                  wrap_repository(repo),
                                  changed='is_enabled',
                                  to=False)

        if 'external_reference' in values:
            if values['external_reference'] == '':
                return {
                    'detail': 'Empty string is an invalid repository location.'
                }, 400
            if model.repo_mirror.change_remote(repo,
                                               values['external_reference']):
                track_and_log('repo_mirror_config_changed',
                              wrap_repository(repo),
                              changed='external_reference',
                              to=values['external_reference'])

        if 'robot_username' in values:
            robot_username = values['robot_username']
            robot = self._setup_robot_for_mirroring(namespace_name,
                                                    repository_name,
                                                    robot_username)
            if model.repo_mirror.set_mirroring_robot(repo, robot):
                track_and_log('repo_mirror_config_changed',
                              wrap_repository(repo),
                              changed='robot_username',
                              to=robot_username)

        if 'sync_start_date' in values:
            try:
                sync_start_date = self._string_to_dt(values['sync_start_date'])
            except ValueError as e:
                return {
                    'detail': 'Incorrect DateTime format for sync_start_date.'
                }, 400
            if model.repo_mirror.change_sync_start_date(repo, sync_start_date):
                track_and_log('repo_mirror_config_changed',
                              wrap_repository(repo),
                              changed='sync_start_date',
                              to=sync_start_date)

        if 'sync_interval' in values:
            if model.repo_mirror.change_sync_interval(repo,
                                                      values['sync_interval']):
                track_and_log('repo_mirror_config_changed',
                              wrap_repository(repo),
                              changed='sync_interval',
                              to=values['sync_interval'])

        if 'external_registry_username' in values and 'external_registry_password' in values:
            username = values['external_registry_username']
            password = values['external_registry_password']
            if username is None and password is not None:
                return {
                    'detail':
                    'Unable to delete username while setting a password.'
                }, 400
            if model.repo_mirror.change_credentials(repo, username, password):
                track_and_log('repo_mirror_config_changed',
                              wrap_repository(repo),
                              changed='external_registry_username',
                              to=username)
                if password is None:
                    track_and_log('repo_mirror_config_changed',
                                  wrap_repository(repo),
                                  changed='external_registry_password',
                                  to=None)
                else:
                    track_and_log('repo_mirror_config_changed',
                                  wrap_repository(repo),
                                  changed='external_registry_password',
                                  to="********")

        elif 'external_registry_username' in values:
            username = values['external_registry_username']
            if model.repo_mirror.change_username(repo, username):
                track_and_log('repo_mirror_config_changed',
                              wrap_repository(repo),
                              changed='external_registry_username',
                              to=username)

        # Do not allow specifying a password without setting a username
        if 'external_registry_password' in values and 'external_registry_username' not in values:
            return {
                'detail':
                'Unable to set a new password without also specifying a username.'
            }, 400

        if 'external_registry_config' in values:
            external_registry_config = values.get('external_registry_config',
                                                  {})

            if 'verify_tls' in external_registry_config:
                updates = {
                    'verify_tls': external_registry_config['verify_tls']
                }
                if model.repo_mirror.change_external_registry_config(
                        repo, updates):
                    track_and_log('repo_mirror_config_changed',
                                  wrap_repository(repo),
                                  changed='verify_tls',
                                  to=external_registry_config['verify_tls'])

            if 'proxy' in external_registry_config:
                proxy_values = external_registry_config.get('proxy', {})

                if 'http_proxy' in proxy_values:
                    updates = {
                        'proxy': {
                            'http_proxy': proxy_values['http_proxy']
                        }
                    }
                    if model.repo_mirror.change_external_registry_config(
                            repo, updates):
                        track_and_log('repo_mirror_config_changed',
                                      wrap_repository(repo),
                                      changed='http_proxy',
                                      to=proxy_values['http_proxy'])

                if 'https_proxy' in proxy_values:
                    updates = {
                        'proxy': {
                            'https_proxy': proxy_values['https_proxy']
                        }
                    }
                    if model.repo_mirror.change_external_registry_config(
                            repo, updates):
                        track_and_log('repo_mirror_config_changed',
                                      wrap_repository(repo),
                                      changed='https_proxy',
                                      to=proxy_values['https_proxy'])

                if 'no_proxy' in proxy_values:
                    updates = {'proxy': {'no_proxy': proxy_values['no_proxy']}}
                    if model.repo_mirror.change_external_registry_config(
                            repo, updates):
                        track_and_log('repo_mirror_config_changed',
                                      wrap_repository(repo),
                                      changed='no_proxy',
                                      to=proxy_values['no_proxy'])

        return '', 201
Beispiel #5
0
    def put(self, namespace_name, repository_name):
        """
        Allow users to modifying the repository's mirroring configuration.
        """
        values = request.get_json()

        repo = model.repository.get_repository(namespace_name, repository_name)
        if not repo:
            raise NotFound()

        mirror = model.repo_mirror.get_mirror(repo)
        if not mirror:
            raise NotFound()

        if "is_enabled" in values:
            if values["is_enabled"] == True:
                if model.repo_mirror.enable_mirror(repo):
                    track_and_log(
                        "repo_mirror_config_changed",
                        wrap_repository(repo),
                        changed="is_enabled",
                        to=True,
                    )
            if values["is_enabled"] == False:
                if model.repo_mirror.disable_mirror(repo):
                    track_and_log(
                        "repo_mirror_config_changed",
                        wrap_repository(repo),
                        changed="is_enabled",
                        to=False,
                    )

        if "external_reference" in values:
            if values["external_reference"] == "":
                return {
                    "detail": "Empty string is an invalid repository location."
                }, 400
            if model.repo_mirror.change_remote(repo,
                                               values["external_reference"]):
                track_and_log(
                    "repo_mirror_config_changed",
                    wrap_repository(repo),
                    changed="external_reference",
                    to=values["external_reference"],
                )

        if "robot_username" in values:
            robot_username = values["robot_username"]
            robot = self._setup_robot_for_mirroring(namespace_name,
                                                    repository_name,
                                                    robot_username)
            if model.repo_mirror.set_mirroring_robot(repo, robot):
                track_and_log(
                    "repo_mirror_config_changed",
                    wrap_repository(repo),
                    changed="robot_username",
                    to=robot_username,
                )

        if "sync_start_date" in values:
            try:
                sync_start_date = self._string_to_dt(values["sync_start_date"])
            except ValueError as e:
                return {
                    "detail": "Incorrect DateTime format for sync_start_date."
                }, 400
            if model.repo_mirror.change_sync_start_date(repo, sync_start_date):
                track_and_log(
                    "repo_mirror_config_changed",
                    wrap_repository(repo),
                    changed="sync_start_date",
                    to=sync_start_date,
                )

        if "sync_interval" in values:
            if model.repo_mirror.change_sync_interval(repo,
                                                      values["sync_interval"]):
                track_and_log(
                    "repo_mirror_config_changed",
                    wrap_repository(repo),
                    changed="sync_interval",
                    to=values["sync_interval"],
                )

        if "external_registry_username" in values and "external_registry_password" in values:
            username = values["external_registry_username"]
            password = values["external_registry_password"]
            if username is None and password is not None:
                return {
                    "detail":
                    "Unable to delete username while setting a password."
                }, 400
            if model.repo_mirror.change_credentials(repo, username, password):
                track_and_log(
                    "repo_mirror_config_changed",
                    wrap_repository(repo),
                    changed="external_registry_username",
                    to=username,
                )
                if password is None:
                    track_and_log(
                        "repo_mirror_config_changed",
                        wrap_repository(repo),
                        changed="external_registry_password",
                        to=None,
                    )
                else:
                    track_and_log(
                        "repo_mirror_config_changed",
                        wrap_repository(repo),
                        changed="external_registry_password",
                        to="********",
                    )

        elif "external_registry_username" in values:
            username = values["external_registry_username"]
            if model.repo_mirror.change_username(repo, username):
                track_and_log(
                    "repo_mirror_config_changed",
                    wrap_repository(repo),
                    changed="external_registry_username",
                    to=username,
                )

        # Do not allow specifying a password without setting a username
        if "external_registry_password" in values and "external_registry_username" not in values:
            return (
                {
                    "detail":
                    "Unable to set a new password without also specifying a username."
                },
                400,
            )

        if "external_registry_config" in values:
            external_registry_config = values.get("external_registry_config",
                                                  {})

            if "verify_tls" in external_registry_config:
                updates = {
                    "verify_tls": external_registry_config["verify_tls"]
                }
                if model.repo_mirror.change_external_registry_config(
                        repo, updates):
                    track_and_log(
                        "repo_mirror_config_changed",
                        wrap_repository(repo),
                        changed="verify_tls",
                        to=external_registry_config["verify_tls"],
                    )

            if "proxy" in external_registry_config:
                proxy_values = external_registry_config.get("proxy", {})

                if "http_proxy" in proxy_values:
                    updates = {
                        "proxy": {
                            "http_proxy": proxy_values["http_proxy"]
                        }
                    }
                    if model.repo_mirror.change_external_registry_config(
                            repo, updates):
                        track_and_log(
                            "repo_mirror_config_changed",
                            wrap_repository(repo),
                            changed="http_proxy",
                            to=proxy_values["http_proxy"],
                        )

                if "https_proxy" in proxy_values:
                    updates = {
                        "proxy": {
                            "https_proxy": proxy_values["https_proxy"]
                        }
                    }
                    if model.repo_mirror.change_external_registry_config(
                            repo, updates):
                        track_and_log(
                            "repo_mirror_config_changed",
                            wrap_repository(repo),
                            changed="https_proxy",
                            to=proxy_values["https_proxy"],
                        )

                if "no_proxy" in proxy_values:
                    updates = {"proxy": {"no_proxy": proxy_values["no_proxy"]}}
                    if model.repo_mirror.change_external_registry_config(
                            repo, updates):
                        track_and_log(
                            "repo_mirror_config_changed",
                            wrap_repository(repo),
                            changed="no_proxy",
                            to=proxy_values["no_proxy"],
                        )

        if "root_rule" in values:

            if values["root_rule"]["rule_kind"] != "tag_glob_csv":
                raise ValidationError(
                    'validation failed: rule_kind must be "tag_glob_csv"')

            if model.repo_mirror.change_rule(
                    repo, RepoMirrorRuleType.TAG_GLOB_CSV,
                    values["root_rule"]["rule_value"]):
                track_and_log(
                    "repo_mirror_config_changed",
                    wrap_repository(repo),
                    changed="mirror_rule",
                    to=values["root_rule"]["rule_value"],
                )

        return "", 201
Beispiel #6
0
    def get(self, uuid):
        note = model.notification.lookup_notification(get_authenticated_user(), uuid)
        if not note:
            raise NotFound()

        return notification_view(note)
Beispiel #7
0
def get_quota(namespace_name, quota_id):
    quota = model.namespacequota.get_namespace_quota(namespace_name, quota_id)
    if quota is None:
        raise NotFound()
    return quota
Beispiel #8
0
def subscribe(user, plan, token, require_business_plan):
    if not features.BILLING:
        return

    plan_found = None
    for plan_obj in PLANS:
        if plan_obj["stripeId"] == plan:
            plan_found = plan_obj

    if not plan_found or plan_found["deprecated"]:
        logger.warning("Plan not found or deprecated: %s", plan)
        raise NotFound()

    if require_business_plan and not plan_found[
            "bus_features"] and not plan_found["price"] == 0:
        logger.warning("Business attempting to subscribe to personal plan: %s",
                       user.username)
        raise request_error(message="No matching plan found")

    private_repos = model.get_private_repo_count(user.username)

    # This is the default response
    response_json = {
        "plan": plan,
        "usedPrivateRepos": private_repos,
    }
    status_code = 200

    if not user.stripe_id:
        # Check if a non-paying user is trying to subscribe to a free plan
        if not plan_found["price"] == 0:
            # They want a real paying plan, create the customer and plan
            # simultaneously
            card = token

            try:
                cus = billing.Customer.create(email=user.email,
                                              plan=plan,
                                              card=card)
                user.stripe_id = cus.id
                user.save()
                check_repository_usage(user, plan_found)
                log_action("account_change_plan", user.username,
                           {"plan": plan})
            except stripe.error.CardError as e:
                return carderror_response(e)
            except stripe.error.APIConnectionError as e:
                return connection_response(e)

            response_json = subscription_view(cus.subscription, private_repos)
            status_code = 201

    else:
        # Change the plan
        try:
            cus = billing.Customer.retrieve(user.stripe_id)
        except stripe.error.APIConnectionError as e:
            return connection_response(e)

        if plan_found["price"] == 0:
            if cus.subscription is not None:
                # We only have to cancel the subscription if they actually have one
                try:
                    cus.subscription.delete()
                except stripe.error.APIConnectionError as e:
                    return connection_response(e)

                check_repository_usage(user, plan_found)
                log_action("account_change_plan", user.username,
                           {"plan": plan})

        else:
            # User may have been a previous customer who is resubscribing
            if token:
                cus.card = token

            cus.plan = plan

            try:
                cus.save()
            except stripe.error.CardError as e:
                return carderror_response(e)
            except stripe.error.APIConnectionError as e:
                return connection_response(e)

            response_json = subscription_view(cus.subscription, private_repos)
            check_repository_usage(user, plan_found)
            log_action("account_change_plan", user.username, {"plan": plan})

    return response_json, status_code
Beispiel #9
0
    def put(self, namespace, repository, tag):
        """
        Change which image a tag points to or create a new tag.
        """
        if not TAG_REGEX.match(tag):
            abort(400, TAG_ERROR)

        repo_ref = registry_model.lookup_repository(namespace, repository)
        if repo_ref is None:
            raise NotFound()

        if "expiration" in request.get_json():
            tag_ref = registry_model.get_repo_tag(repo_ref, tag)
            if tag_ref is None:
                raise NotFound()

            expiration = request.get_json().get("expiration")
            expiration_date = None
            if expiration is not None:
                try:
                    expiration_date = datetime.utcfromtimestamp(
                        float(expiration))
                except ValueError:
                    abort(400)

                if expiration_date <= datetime.now():
                    abort(400)

            existing_end_ts, ok = registry_model.change_repository_tag_expiration(
                tag_ref, expiration_date)
            if ok:
                if not (existing_end_ts is None and expiration_date is None):
                    log_action(
                        "change_tag_expiration",
                        namespace,
                        {
                            "username": get_authenticated_user().username,
                            "repo": repository,
                            "tag": tag,
                            "namespace": namespace,
                            "expiration_date": expiration_date,
                            "old_expiration_date": existing_end_ts,
                        },
                        repo_name=repository,
                    )
            else:
                raise InvalidRequest(
                    "Could not update tag expiration; Tag has probably changed"
                )

        if "image" in request.get_json(
        ) or "manifest_digest" in request.get_json():
            existing_tag = registry_model.get_repo_tag(
                repo_ref, tag, include_legacy_image=True)

            manifest_or_image = None
            image_id = None
            manifest_digest = None

            if "image" in request.get_json():
                image_id = request.get_json()["image"]
                manifest_or_image = registry_model.get_legacy_image(
                    repo_ref, image_id)
            else:
                manifest_digest = request.get_json()["manifest_digest"]
                manifest_or_image = registry_model.lookup_manifest_by_digest(
                    repo_ref, manifest_digest, require_available=True)

            if manifest_or_image is None:
                raise NotFound()

            # TODO: Remove this check once fully on V22
            existing_manifest_digest = None
            if existing_tag:
                existing_manifest = registry_model.get_manifest_for_tag(
                    existing_tag)
                existing_manifest_digest = existing_manifest.digest if existing_manifest else None

            if not registry_model.retarget_tag(repo_ref, tag,
                                               manifest_or_image, storage,
                                               docker_v2_signing_key):
                raise InvalidRequest("Could not move tag")

            username = get_authenticated_user().username

            log_action(
                "move_tag" if existing_tag else "create_tag",
                namespace,
                {
                    "username":
                    username,
                    "repo":
                    repository,
                    "tag":
                    tag,
                    "namespace":
                    namespace,
                    "image":
                    image_id,
                    "manifest_digest":
                    manifest_digest,
                    "original_image":
                    (existing_tag.legacy_image.docker_image_id if existing_tag
                     and existing_tag.legacy_image_if_present else None),
                    "original_manifest_digest":
                    existing_manifest_digest,
                },
                repo_name=repository,
            )

        return "Updated", 201
Beispiel #10
0
    def get(self, error_type):
        """ Get a detailed description of the error """
        if error_type in ERROR_DESCRIPTION.keys():
            return error_view(error_type)

        raise NotFound()
Beispiel #11
0
def get_trigger(trigger_uuid):
    try:
        trigger = model.build.get_build_trigger(trigger_uuid)
    except model.InvalidBuildTriggerException:
        raise NotFound()
    return trigger
Beispiel #12
0
    def post(self, namespace_name, repo_name, trigger_uuid):
        """
        Activate the specified build trigger.
        """
        trigger = get_trigger(trigger_uuid)
        handler = BuildTriggerHandler.get_handler(trigger)
        if handler.is_active():
            raise InvalidRequest("Trigger config is not sufficient for activation.")

        user_permission = UserAdminPermission(trigger.connected_user.username)
        if user_permission.can():
            # Update the pull robot (if any).
            pull_robot_name = request.get_json().get("pull_robot", None)
            if pull_robot_name:
                try:
                    pull_robot = 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, _) = parse_robot_username(pull_robot_name)
                if not AdministerOrganizationPermission(robot_namespace).can():
                    raise Unauthorized()

                # Make sure the namespace matches that of the trigger.
                if robot_namespace != namespace_name:
                    raise Unauthorized()

                # Set the pull robot.
                trigger.pull_robot = pull_robot

            # Update the config.
            new_config_dict = request.get_json()["config"]

            write_token_name = "Build Trigger: %s" % trigger.service.name
            write_token = model.token.create_delegate_token(
                namespace_name, repo_name, write_token_name, "write"
            )

            try:
                path = url_for("webhooks.build_trigger_webhook", trigger_uuid=trigger.uuid)
                authed_url = _prepare_webhook_url(
                    app.config["PREFERRED_URL_SCHEME"],
                    "$token",
                    write_token.get_code(),
                    app.config["SERVER_HOSTNAME"],
                    path,
                )

                handler = BuildTriggerHandler.get_handler(trigger, new_config_dict)
                final_config, private_config = handler.activate(authed_url)

                if "private_key" in private_config:
                    trigger.secure_private_key = DecryptedValue(private_config["private_key"])

            except TriggerException as exc:
                write_token.delete_instance()
                raise request_error(message=str(exc))

            # Save the updated config.
            update_build_trigger(trigger, final_config, write_token=write_token)

            # Log the trigger setup.
            repo = model.repository.get_repository(namespace_name, repo_name)
            log_action(
                "setup_repo_trigger",
                namespace_name,
                {
                    "repo": repo_name,
                    "namespace": namespace_name,
                    "trigger_id": trigger.uuid,
                    "service": trigger.service.name,
                    "pull_robot": trigger.pull_robot.username if trigger.pull_robot else None,
                    "config": final_config,
                },
                repo=repo,
            )

            return trigger_view(trigger, can_admin=True)
        else:
            raise Unauthorized()
Beispiel #13
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).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