예제 #1
0
def validate_tags(string_pairs, mode):
    """Validates a list of common.StringPair tags.

  For mode, see buildtags.validate_tags docstring.
  """
    for p in string_pairs:
        if ':' in p.key:
            _err('tag key "%s" cannot have a colon', p.key)

    with _handle_invalid_input_error():
        tags = ['%s:%s' % (p.key, p.value) for p in string_pairs]
        buildtags.validate_tags(tags, mode)
예제 #2
0
 def test_two_gitiles_commits(self):
   err_pattern = r'More than one commits/gitiles buildset'
   with self.assertRaisesRegexp(errors.InvalidInputError, err_pattern):
     tags = [
         (
             'buildset:commit/gitiles/chromium.googlesource.com/chromium/src'
             '/+/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
         ),
         (
             'buildset:commit/gitiles/chromium.googlesource.com/chromium/src'
             '/+/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb'
         ),
     ]
     buildtags.validate_tags(tags, 'new')
예제 #3
0
def _complete(build_id,
              lease_key,
              status,
              result_details,
              url=None,
              new_tags=None):
    """Marks a build as completed. Used by succeed and fail methods."""
    validate_lease_key(lease_key)
    validate_url(url)
    buildtags.validate_tags(new_tags, 'append')
    assert model.is_terminal_status(status), status

    @ndb.transactional
    def txn():
        build = _get_leasable_build(build_id)

        if build.is_ended:
            if (build.proto.status == status
                    and build.result_details == result_details
                    and build.url == url):
                return False, build
            raise errors.BuildIsCompletedError(
                'Build %s has already completed' % build_id)
        _check_lease(build, lease_key)

        now = utils.utcnow()
        build.proto.status = status
        build.status_changed_time = now
        build.proto.end_time.FromDatetime(now)
        if url is not None:  # pragma: no branch
            build.url = url
        build.result_details = result_details
        if new_tags:
            build.tags.extend(new_tags)
            build.tags = sorted(set(build.tags))
        build.clear_lease()

        _fut_results(
            build.put_async(),
            events.on_build_completing_async(build),
            _put_output_properties_async(build.key, result_details),
        )
        return True, build

    updated, build = txn()
    if updated:
        events.on_build_completed(build)
    return build
예제 #4
0
    def validate(self):
        """Raises errors.InvalidInputError if self is invalid."""
        assert isinstance(self.status,
                          (type(None), StatusFilter, int)), self.status
        assert isinstance(self.bucket_ids, (type(None), list)), self.bucket_ids

        if self.bucket_ids and self.project:
            raise errors.InvalidInputError(
                'project and bucket_ids are mutually exclusive')
        if self.builder and not self.bucket_ids:
            raise errors.InvalidInputError(
                'builder requires non-empty bucket_ids')

        buildtags.validate_tags(self.tags, 'search')

        create_time_range = (self.create_time_low is not None
                             or self.create_time_high is not None)
        build_range = self.build_low is not None or self.build_high is not None
        if create_time_range and build_range:
            raise errors.InvalidInputError(
                'create_time_low and create_time_high are mutually exclusive with '
                'build_low and build_high')
예제 #5
0
def put_request_message_to_build_request(put_request):
    """Converts PutRequest to BuildRequest.

  Raises errors.InvalidInputError if the put_request is invalid.
  """
    lease_expiration_date = parse_datetime(put_request.lease_expiration_ts)
    errors.validate_lease_expiration_date(lease_expiration_date)

    # Read parameters.
    parameters = parse_json_object(put_request.parameters_json,
                                   'parameters_json')
    parameters = parameters or {}
    validate_known_build_parameters(parameters)

    builder = parameters.get(model.BUILDER_PARAMETER) or ''

    # Validate tags.
    buildtags.validate_tags(put_request.tags, 'new', builder=builder)

    # Read properties. Remove them from parameters.
    props = parameters.pop(model.PROPERTIES_PARAMETER, None)
    if props is not None and not isinstance(props, dict):
        raise errors.InvalidInputError(
            '"properties" parameter must be a JSON object or null')
    props = props or {}

    changes = parameters.get(_PARAM_CHANGES)
    if changes:  # pragma: no branch
        # Buildbucket-Buildbot integration passes repo_url of the first change in
        # build parameter "changes" as "repository" attribute of SourceStamp.
        # https://chromium.googlesource.com/chromium/tools/build/+/2c6023d
        # /scripts/master/buildbucket/changestore.py#140
        # Buildbot passes repository of the build source stamp as "repository"
        # build property. Recipes, in partiular bot_update recipe module, rely on
        # "repository" property and it is an almost sane property to support in
        # swarmbucket.
        repo_url = changes[0].get('repo_url')
        if repo_url:  # pragma: no branch
            props['repository'] = repo_url

        # Buildbot-Buildbucket integration converts emails in changes to blamelist
        # property.
        emails = [c.get('author', {}).get('email') for c in changes]
        props['blamelist'] = filter(None, emails)

    # Create a v2 request.
    sbr = rpc_pb2.ScheduleBuildRequest(
        builder=build_pb2.BuilderID(builder=builder),
        properties=bbutil.dict_to_struct(props),
        request_id=put_request.client_operation_id,
        experimental=bbutil.BOOLISH_TO_TRINARY[put_request.experimental],
        canary=api_common.CANARY_PREFERENCE_TO_TRINARY.get(
            put_request.canary_preference, common_pb2.UNSET),
    )
    sbr.builder.project, sbr.builder.bucket = config.parse_bucket_id(
        put_request.bucket)

    # Parse tags. Extract gitiles commit and gerrit changes.
    tags, gitiles_commit, gerrit_changes = parse_v1_tags(put_request.tags)
    sbr.tags.extend(tags)
    if gitiles_commit:
        sbr.gitiles_commit.CopyFrom(gitiles_commit)

    # Gerrit changes explicitly passed via "gerrit_changes" parameter win.
    gerrit_change_list = parameters.pop('gerrit_changes', None)
    if gerrit_change_list is not None:
        if not isinstance(gerrit_change_list, list):  # pragma: no cover
            raise errors.InvalidInputError('gerrit_changes must be a list')
        try:
            gerrit_changes = [
                json_format.ParseDict(c, common_pb2.GerritChange())
                for c in gerrit_change_list
            ]
        except json_format.ParseError as ex:  # pragma: no cover
            raise errors.InvalidInputError('Invalid gerrit_changes: %s' % ex)

    sbr.gerrit_changes.extend(gerrit_changes)

    if (not gerrit_changes and
            not sbr.builder.bucket.startswith('master.')):  # pragma: no cover
        changes = parameters.get('changes')
        if isinstance(changes, list) and changes and not gitiles_commit:
            legacy_revision = changes[0].get('revision')
            if legacy_revision:
                raise errors.InvalidInputError(
                    'legacy revision without gitiles buildset tag')

    # Populate Gerrit project from patch_project property.
    # V2 API users will have to provide this.
    patch_project = props.get('patch_project')
    if len(sbr.gerrit_changes) == 1 and isinstance(patch_project, basestring):
        sbr.gerrit_changes[0].project = patch_project

    # Read PubSub callback.
    pubsub_callback_auth_token = None
    if put_request.pubsub_callback:
        pubsub_callback_auth_token = put_request.pubsub_callback.auth_token
        pubsub_callback_to_notification_config(put_request.pubsub_callback,
                                               sbr.notify)

    # Validate the resulting v2 request before continuing.
    with _wrap_validation_error():
        validation.validate_schedule_build_request(sbr, legacy=True)

    return creation.BuildRequest(
        schedule_build_request=sbr,
        parameters=parameters,
        lease_expiration_date=lease_expiration_date,
        pubsub_callback_auth_token=pubsub_callback_auth_token,
        override_builder_cfg=_override_builder_cfg_func(parameters),
    )
예제 #6
0
 def test_invalid_buildset(self):
   with self.assertRaises(errors.InvalidInputError):
     buildtags.validate_tags(['buildset:patch/gerrit/foo'], 'search')
예제 #7
0
 def test_append_builder(self):
   with self.assertRaises(errors.InvalidInputError):
     buildtags.validate_tags(['builder:1'], 'append')
예제 #8
0
 def test_no_key(self):
   with self.assertRaises(errors.InvalidInputError):
     buildtags.validate_tags([':'], 'search')
예제 #9
0
 def test_builder_inconsistent(self):
   err_pattern = r'conflicts with tag'
   with self.assertRaisesRegexp(errors.InvalidInputError, err_pattern):
     buildtags.validate_tags(['builder:a', 'builder:b'], 'new')
예제 #10
0
 def test_builder_inconsistent_with_param(self):
   with self.assertRaises(errors.InvalidInputError):
     buildtags.validate_tags(['builder:a'], 'new', 'b')
예제 #11
0
 def test_build_address(self):
   with self.assertRaises(errors.InvalidInputError):
     buildtags.validate_tags(['build_address:1'], 'new')
   with self.assertRaises(errors.InvalidInputError):
     buildtags.validate_tags(['build_address:1'], 'append')
예제 #12
0
 def test_no_colon(self):
   with self.assertRaises(errors.InvalidInputError):
     buildtags.validate_tags(['tag,value'], 'search')
예제 #13
0
 def test_nonstring(self):
   with self.assertRaises(errors.InvalidInputError):
     buildtags.validate_tags(['tag:value', 123456], 'search')
예제 #14
0
 def test_nonlist(self):
   with self.assertRaises(errors.InvalidInputError):
     buildtags.validate_tags('tag:value', 'search')
예제 #15
0
 def test_tags_none(self):
   self.assertIsNone(buildtags.validate_tags(None, 'search'))