def test_new_build_id_generates_monotonicaly_decreasing_ids(self): now = datetime.datetime(2015, 2, 24) self.mock(utils, 'utcnow', lambda: now) last_id = None for i in xrange(1000): now += datetime.timedelta(seconds=i) new_id = model.new_build_id() if last_id is not None: self.assertLess(new_id, last_id) last_id = new_id
def test_peek_multi(self): self.test_build.key = ndb.Key(model.Build, model.new_build_id()) self.test_build.put() # We test that peek returns builds in decreasing order of the build key. The # build key is derived from the inverted current time, so later builds get # smaller ids. Only exception: if the time is the same, randomness decides # the order. So artificially create an id here to avoid flakiness. build2 = model.Build(id=self.test_build.key.id() - 1, bucket='bucket2') build2.put() builds, _ = service.peek(buckets=[self.test_build.bucket, 'bucket2']) self.assertEqual(builds, [self.test_build, build2])
def add_async( bucket, tags=None, parameters=None, lease_expiration_date=None, client_operation_id=None, pubsub_callback=None): """Adds the build entity to the build bucket. Requires the current user to have permissions to add builds to the |bucket|. Args: bucket (str): destination bucket. Required. tags (model.Tags): build tags. parameters (dict): arbitrary build parameters. Cannot be changed after build creation. lease_expiration_date (datetime.datetime): if not None, the build is created as leased and its lease_key is not None. pubsub_callback (model.PubsubCallback): callback parameters. client_operation_id (str): client-supplied operation id. If an a build with the same client operation id was added during last minute, it will be returned instead. Returns: A new Build. """ if client_operation_id is not None: if not isinstance(client_operation_id, basestring): # pragma: no cover raise errors.InvalidInputError('client_operation_id must be string') if '/' in client_operation_id: # pragma: no cover raise errors.InvalidInputError('client_operation_id must not contain /') validate_bucket_name(bucket) if parameters is not None and not isinstance(parameters, dict): raise errors.InvalidInputError('parameters must be a dict or None') validate_lease_expiration_date(lease_expiration_date) validate_tags(tags) tags = tags or [] ctx = ndb.get_context() identity = auth.get_current_identity() if not (yield acl.can_add_build_async(bucket)): # pragma: no branch raise current_identity_cannot('add builds to bucket %s', bucket) if client_operation_id is not None: client_operation_cache_key = ( 'client_op/%s/%s/add_build' % ( identity.to_bytes(), client_operation_id)) build_id = yield ctx.memcache_get(client_operation_cache_key) if build_id: build = yield model.Build.get_by_id_async(build_id) if build: # pragma: no branch raise ndb.Return(build) build = model.Build( id=model.new_build_id(), bucket=bucket, tags=tags, parameters=parameters, status=model.BuildStatus.SCHEDULED, created_by=identity, never_leased=lease_expiration_date is None, pubsub_callback=pubsub_callback, ) if lease_expiration_date is not None: build.lease_expiration_date = lease_expiration_date build.leasee = auth.get_current_identity() build.regenerate_lease_key() for_swarming = yield swarming.is_for_swarming_async(build) if for_swarming: # pragma: no cover yield swarming.create_task_async(build) try: yield build.put_async() except: # pragma: no cover # Best effort. if for_swarming: yield swarming.cancel_task_async(build) raise logging.info( 'Build %s was created by %s', build.key.id(), identity.to_bytes()) metrics.increment(metrics.CREATE_COUNT, build) if client_operation_id is not None: yield ctx.memcache_set(client_operation_cache_key, build.key.id(), 60) raise ndb.Return(build)
def add_async(self, bucket, tags=None, parameters=None, lease_expiration_date=None, client_operation_id=None): """Adds the build entity to the build bucket. Requires the current user to have permissions to add builds to the |bucket|. Args: bucket (str): destination bucket. Required. tags (model.Tags): build tags. parameters (dict): arbitrary build parameters. Cannot be changed after build creation. lease_expiration_date (datetime.datetime): if not None, the build is created as leased and its lease_key is not None. client_operation_id (str): client-supplied operation id. If an a build with the same client operation id was added during last minute, it will be returned instead. Returns: A new Build. """ if client_operation_id is not None: if not isinstance(client_operation_id, basestring): # pragma: no cover raise errors.InvalidInputError( 'client_operation_id must be string') if '/' in client_operation_id: # pragma: no cover raise errors.InvalidInputError( 'client_operation_id must not contain /') validate_bucket_name(bucket) if parameters is not None and not isinstance(parameters, dict): raise errors.InvalidInputError('parameters must be a dict or None') validate_lease_expiration_date(lease_expiration_date) validate_tags(tags) tags = tags or [] ctx = ndb.get_context() identity = auth.get_current_identity() if not (yield acl.can_add_build_async(bucket)): # pragma: no branch raise current_identity_cannot('add builds to bucket %s', bucket) if client_operation_id is not None: client_operation_cache_key = ( 'client_op/%s/%s/add_build' % (identity.to_bytes(), client_operation_id)) build_id = yield ctx.memcache_get(client_operation_cache_key) if build_id: build = yield model.Build.get_by_id_async(build_id) if build: # pragma: no branch raise ndb.Return(build) build = model.Build( id=model.new_build_id(), bucket=bucket, tags=tags, parameters=parameters, status=model.BuildStatus.SCHEDULED, created_by=identity, ) if lease_expiration_date is not None: build.lease_expiration_date = lease_expiration_date build.leasee = auth.get_current_identity() build.regenerate_lease_key() yield build.put_async() logging.info('Build %s was created by %s', build.key.id(), identity.to_bytes()) if client_operation_id is not None: yield ctx.memcache_set(client_operation_cache_key, build.key.id(), 60) raise ndb.Return(build)