def test_deletes(self): def get_drained_instance_group_managers(*args, **kwargs): return [ ndb.Key(models.InstanceGroupManager, 'fake-key-1'), ndb.Key(models.InstanceGroupManager, 'fake-key-3'), ndb.Key(models.InstanceGroupManager, 'fake-key-4'), ] @ndb.tasklet def delete_instance_group_manager(key): yield key.delete_async() self.mock( cleanup.instance_group_managers, 'get_drained_instance_group_managers', get_drained_instance_group_managers, ) self.mock(cleanup, 'delete_instance_group_manager', delete_instance_group_manager) models.InstanceGroupManager(key=ndb.Key(models.InstanceGroupManager, 'fake-key-1'), ).put() models.InstanceGroupManager(key=ndb.Key(models.InstanceGroupManager, 'fake-key-2'), ).put() models.InstanceGroupManager(key=ndb.Key(models.InstanceGroupManager, 'fake-key-3'), ).put() models.InstanceGroupManager(key=ndb.Key(models.InstanceGroupManager, 'fake-key-4'), ).put() cleanup.cleanup_instance_group_managers(max_concurrent=2) self.failIf(ndb.Key(models.InstanceGroupManager, 'fake-key-1').get()) self.failUnless( ndb.Key(models.InstanceGroupManager, 'fake-key-2').get()) self.failIf(ndb.Key(models.InstanceGroupManager, 'fake-key-3').get()) self.failIf(ndb.Key(models.InstanceGroupManager, 'fake-key-4').get())
def test_resize_other_revisions_created(self): def get_instance_group_manager(*args, **kwargs): return { 'currentActions': { 'none': 0, }, 'name': 'name', 'targetSize': 0, } def resize_managed_instance_group(_, name, zone, size): self.assertEqual(name, 'name') self.assertEqual(zone, 'zone') self.assertEqual(size, 4) self.mock( instance_group_managers.gce.Project, 'get_instance_group_manager', get_instance_group_manager, ) self.mock( instance_group_managers.gce.Project, 'resize_managed_instance_group', resize_managed_instance_group, ) key = models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision-1', 'zone', ), minimum_size=7, maximum_size=7, url='https://example.com', ).put() models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision-2', 'zone', ), instances=[ ndb.Key(models.Instance, 'instance-name-1'), ndb.Key(models.Instance, 'instance-name-2'), ndb.Key(models.Instance, 'instance-name-3'), ], ).put() models.InstanceTemplateRevision( key=key.parent(), project='fake-project', ).put() models.InstanceTemplate(key=key.parent().parent()).put() instance_group_managers.resize(key)
def test_deletion_ts(self): """Ensures deletion_ts is not overwritten, but deletion call is repeated.""" def json_request(*args, **kwargs): return {'status': 'DONE'} def send_machine_event(*args, **kwargs): pass self.mock(instances.net, 'json_request', json_request) self.mock(instances.metrics, 'send_machine_event', send_machine_event) now = utils.utcnow() key = instances.get_instance_key( 'base-name', 'revision', 'zone', 'instance-name', ) key = models.Instance( key=key, deletion_ts=now, instance_group_manager=instances.get_instance_group_manager_key( key), pending_deletion=True, url='url', ).put() models.InstanceGroupManager( key=instances.get_instance_group_manager_key(key), ).put() models.InstanceTemplateRevision( key=instances.get_instance_group_manager_key(key).parent(), project='project', ).put() instances.delete_pending(key) self.assertEqual(key.get().deletion_ts, now)
def test_not_drained(self): """Ensures nothing happens when the parent is not drained.""" def json_request(*args, **kwargs): self.fail('json_request called') self.mock(cleanup.net, 'json_request', json_request) key = instances.get_instance_key( 'base-name', 'revision', 'zone', 'instance-name', ) key = models.Instance( key=key, instance_group_manager=instances.get_instance_group_manager_key( key), url='url', ).put() models.InstanceGroupManager( key=instances.get_instance_group_manager_key(key), ).put() models.InstanceTemplateRevision( key=instances.get_instance_group_manager_key( key).parent(), ).put() models.InstanceTemplate(key=instances.get_instance_group_manager_key( key).parent().parent(), ).put() cleanup.cleanup_drained_instance(key) self.failIf(key.get().deleted)
def test_matches_existing_entity(self): """Ensures that an entity matches when it already exists.""" template_cfg = config_pb2.InstanceTemplateConfig.InstanceTemplate( base_name='base-name', ) manager_cfg = config_pb2.InstanceGroupManagerConfig.InstanceGroupManager( maximum_size=3, minimum_size=2, template_base_name='base-name', zone='zone', ) expected_key = parse.get_instance_group_manager_key( template_cfg, manager_cfg) models.InstanceGroupManager( key=expected_key, maximum_size=2, minimum_size=1, ).put() future = parse.ensure_instance_group_manager_exists( template_cfg, manager_cfg) future.wait() key = future.get_result() entity = key.get() self.assertEqual(key, expected_key) self.assertEqual(entity.maximum_size, manager_cfg.maximum_size) self.assertEqual(entity.minimum_size, manager_cfg.minimum_size)
def test_already_exists(self): """Ensures nothing happens when the entity already exists.""" def fetch(*args, **kwargs): return ['url/name'] self.mock(instances, 'fetch', fetch) key = models.Instance( key=instances.get_instance_key( 'base-name', 'revision', 'zone', 'name', ), ).put() models.InstanceGroupManager( key=key.parent(), url='url', ).put() expected_instances = [ key, ] instances.ensure_entities_exist(key.parent()) self.failIf(key.get().url) self.assertItemsEqual(key.parent().get().instances, expected_instances)
def test_doesnt_update_when_creation_fails(self): """Ensures an instance group manager is not updated when creation fails.""" def create_instance_group_manager(*args, **kwargs): raise net.Error('', 400, '') self.mock( instance_group_managers.gce.Project, 'create_instance_group_manager', create_instance_group_manager, ) key = models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision', 'zone', ), minimum_size=2, maximum_size=2, ).put() models.InstanceTemplateRevision( key=key.parent(), project='project', url='instance-template-url', ).put() self.assertRaises(net.Error, instance_group_managers.create, key) self.failIf(key.get().url)
def test_creates(self): """Ensures entity gets created.""" def fetch(*args, **kwargs): return ['url/name'] self.mock(instances, 'fetch', fetch) key = instances.get_instance_key( 'base-name', 'revision', 'zone', 'name', ) models.InstanceGroupManager( key=key.parent(), url='url', ).put() expected_instances = [ key, ] expected_url = 'url/name' instances.ensure_entities_exist(key.parent()) self.assertItemsEqual(key.parent().get().instances, expected_instances) self.assertEqual(key.get().url, expected_url)
def test_updates_when_already_created(self): """Ensures an instance group manager is updated when already created.""" def create_instance_group_manager(*args, **kwargs): raise net.Error('', 409, '') def get_instance_group_manager(*args, **kwargs): return {'selfLink': 'url'} self.mock( instance_group_managers.gce.Project, 'create_instance_group_manager', create_instance_group_manager, ) self.mock( instance_group_managers.gce.Project, 'get_instance_group_manager', get_instance_group_manager, ) key = models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision', 'zone', ), minimum_size=2, ).put() models.InstanceTemplateRevision( key=key.parent(), project='project', url='instance-template-url', ).put() expected_url = 'url' instance_group_managers.create(key) self.assertEqual(key.get().url, expected_url)
def test_not_pending_deletion(self): """Ensures nothing happens when the instance isn't pending deletion.""" def json_request(*args, **kwargs): self.fail('json_request called') def send_machine_event(*args, **kwargs): self.fail('send_machine_event called') self.mock(instances.net, 'json_request', json_request) self.mock(instances.metrics, 'send_machine_event', send_machine_event) key = instances.get_instance_key( 'base-name', 'revision', 'zone', 'instance-name', ) key = models.Instance( key=key, instance_group_manager=instances.get_instance_group_manager_key( key), pending_deletion=False, url='url', ).put() models.InstanceGroupManager( key=instances.get_instance_group_manager_key(key), ).put() models.InstanceTemplateRevision( key=instances.get_instance_group_manager_key(key).parent(), project='project', ).put() instances.delete_pending(key)
def ensure_instance_group_manager_exists(template_cfg, manager_cfg): """Ensures an InstanceGroupManager exists for the given config. Args: template_cfg: proto.config_pb2.InstanceTemplateConfig.InstanceTemplate. manager_cfg: proto.config_pb2.InstanceGroupManagerConfig.InstanceGroupManager which has template_base_name equal to template_cfg.base_name. Returns: ndb.Key for a models.InstanceGroupManager entity. """ instance_group_manager_key = get_instance_group_manager_key( template_cfg, manager_cfg) instance_group_manager = yield instance_group_manager_key.get_async() if not instance_group_manager: logging.info('Creating InstanceGroupManager: %s', instance_group_manager_key) instance_group_manager = models.InstanceGroupManager( key=instance_group_manager_key) if ensure_instance_group_manager_matches(manager_cfg, instance_group_manager): instance_group_manager_key = yield instance_group_manager.put_async() raise ndb.Return(instance_group_manager_key)
def test_creates(self): """Ensures entity gets created.""" def fetch(*args, **kwargs): return ['url/name'] def send_machine_event(*args, **kwargs): pass self.mock(instances, 'fetch', fetch) self.mock(instances.metrics, 'send_machine_event', send_machine_event) key = instances.get_instance_key( 'base-name', 'revision', 'zone', 'name', ) models.InstanceGroupManager( key=instances.get_instance_group_manager_key(key), url='url', ).put() expected_instances = [ key, ] expected_url = 'url/name' instances.ensure_entities_exist( instances.get_instance_group_manager_key(key)) self.assertItemsEqual( instances.get_instance_group_manager_key(key).get().instances, expected_instances, ) self.assertEqual(key.get().url, expected_url)
def test_error_surfaced(self): """Ensures errors are surfaced.""" def json_request(*args, **kwargs): raise net.Error('403', 403, '403') def send_machine_event(*args, **kwargs): pass self.mock(instances.net, 'json_request', json_request) self.mock(instances.metrics, 'send_machine_event', send_machine_event) key = instances.get_instance_key( 'base-name', 'revision', 'zone', 'instance-name', ) key = models.Instance( key=key, instance_group_manager=instances.get_instance_group_manager_key( key), pending_deletion=True, url='url', ).put() models.InstanceGroupManager( key=instances.get_instance_group_manager_key(key), ).put() models.InstanceTemplateRevision( key=instances.get_instance_group_manager_key(key).parent(), project='project', ).put() self.assertRaises(net.Error, instances.delete_pending, key)
def test_deleted_not_done(self): """Ensures nothing happens when instance deletion status is not DONE.""" def json_request(*args, **kwargs): return {'status': 'error'} def send_machine_event(*args, **kwargs): pass self.mock(instances.net, 'json_request', json_request) self.mock(instances.metrics, 'send_machine_event', send_machine_event) key = instances.get_instance_key( 'base-name', 'revision', 'zone', 'instance-name', ) key = models.Instance( key=key, instance_group_manager=instances.get_instance_group_manager_key( key), pending_deletion=True, url='url', ).put() models.InstanceGroupManager( key=instances.get_instance_group_manager_key(key), ).put() models.InstanceTemplateRevision( key=instances.get_instance_group_manager_key(key).parent(), project='project', ).put() instances.delete_pending(key)
def test_active_instances(self): """Ensures nothing happens when there are active Instances.""" key = models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision', 'zone', ), instances=[ ndb.Key(models.Instance, 'fake-key'), ], ).put() models.InstanceTemplateRevision( key=key.parent(), drained=[ key, ], ).put() models.InstanceTemplate( key=key.parent().parent(), active=key.parent(), ).put() future = cleanup.delete_instance_group_manager(key) future.wait() self.failUnless(key.get())
def test_metadata_updated(self): """Ensures metadata is updated.""" def json_request(*args, **kwargs): return {'metadata': {'fingerprint': 'fingerprint', 'items': []}} def set_metadata(*args, **kwargs): return collections.namedtuple('operation', ['url'])(url='url') self.mock(metadata.net, 'json_request', json_request) self.mock(metadata.gce.Project, 'set_metadata', set_metadata) key = models.Instance( key=instances.get_instance_key( 'base-name', 'revision', 'zone', 'instance-name', ), active_metadata_update=models.MetadataUpdate(metadata={ 'key': 'value', }, ), ).put() models.InstanceGroupManager(key=key.parent(), ).put() models.InstanceTemplateRevision( key=key.parent().parent(), project='project', ).put() expected_url = 'url' metadata.update(key) self.assertEqual(key.get().active_metadata_update.url, expected_url)
def test_deletes_implicitly_drained(self): """Ensures an implicitly drained entity is deleted.""" key = models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision', 'zone', ), ).put() models.InstanceTemplateRevision( key=key.parent(), active=[ ndb.Key(models.InstanceGroupManager, 'fake-key-1'), key, ndb.Key(models.InstanceGroupManager, 'fake-key-2'), ], ).put() models.InstanceTemplate( key=key.parent().parent(), drained=[ key.parent(), ], ).put() expected_active = [ ndb.Key(models.InstanceGroupManager, 'fake-key-1'), ndb.Key(models.InstanceGroupManager, 'fake-key-2'), ] future = cleanup.delete_instance_group_manager(key) future.wait() self.failIf(key.get()) self.assertItemsEqual(key.parent().get().active, expected_active)
def test_instances_with_page_token(self): """Ensures all instances are returned.""" def get_instances_in_instance_group(*args, **kwargs): if kwargs.get('page_token'): return { 'items': [ {'instance': 'url/instance-2'}, ], } return { 'items': [ {'instance': 'url/instance-1'}, ], 'nextPageToken': 'page-token', } self.mock( instances.gce.Project, 'get_instances_in_instance_group', get_instances_in_instance_group, ) key = models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision', 'zone', ), url='url', ).put() models.InstanceTemplateRevision(key=key.parent(), project='project').put() expected_urls = ['url/instance-1', 'url/instance-2'] urls = instances.fetch(key) self.assertItemsEqual(urls, expected_urls)
def test_creates(self): """Ensures an instance group manager is created.""" def create_instance_group_manager(*args, **kwargs): return {'targetLink': 'url'} self.mock( instance_group_managers.gce.Project, 'create_instance_group_manager', create_instance_group_manager, ) key = models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision', 'zone', ), minimum_size=2, maximum_size=2, ).put() models.InstanceTemplateRevision( key=key.parent(), project='project', url='instance-template-url', ).put() expected_url = 'url' instance_group_managers.create(key) self.assertEqual(key.get().url, expected_url)
def test_url_specified(self): """Ensures nothing happens when the entity still has a URL.""" key = models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision', 'zone', ), url='url', ).put() models.InstanceTemplateRevision( key=key.parent(), drained=[ key, ], ).put() models.InstanceTemplate( key=key.parent().parent(), active=key.parent(), ).put() future = cleanup.delete_instance_group_manager(key) future.wait() self.failUnless(key.get())
def test_cataloging_error_hostname_reuse(self): """Ensures an instance is marked cataloged on HOSTNAME_REUSE.""" def add_machine(*args, **kwargs): return {'error': 'HOSTNAME_REUSE'} def send_machine_event(*args, **kwargs): pass self.mock(catalog.machine_provider, 'add_machine', add_machine) self.mock(catalog.metrics, 'send_machine_event', send_machine_event) key = instances.get_instance_key( 'base-name', 'revision', 'zone', 'instance-name', ) key = models.Instance( key=key, instance_group_manager=instances.get_instance_group_manager_key( key), ).put() models.InstanceGroupManager( key=instances.get_instance_group_manager_key(key), ).put() models.InstanceTemplateRevision( key=instances.get_instance_group_manager_key(key).parent(), service_accounts=[ models.ServiceAccount(name='service-account'), ], ).put() catalog.catalog(key) self.failUnless(key.get().cataloged)
def test_service_account_not_found(self): """Ensures nothing happens when a service account doesn't exist.""" def add_machine(*args, **kwargs): self.fail('add_machine called') self.mock(catalog.machine_provider, 'add_machine', add_machine) key = instances.get_instance_key( 'base-name', 'revision', 'zone', 'instance-name', ) key = models.Instance( key=key, instance_group_manager=instances.get_instance_group_manager_key( key), ).put() models.InstanceGroupManager( key=instances.get_instance_group_manager_key(key), ).put() models.InstanceTemplateRevision( key=instances.get_instance_group_manager_key( key).parent(), ).put() catalog.catalog(key) self.failIf(key.get().cataloged)
def test_grandparent_unspecified(self): """Ensures nothing happens when the grandparent doesn't exist.""" def json_request(*args, **kwargs): self.fail('json_request called') def send_machine_event(*args, **kwargs): self.fail('send_machine_event called') self.mock(instances.net, 'json_request', json_request) self.mock(instances.metrics, 'send_machine_event', send_machine_event) key = instances.get_instance_key( 'base-name', 'revision', 'zone', 'instance-name', ) key = models.Instance( key=key, instance_group_manager=instances.get_instance_group_manager_key( key), pending_deletion=True, url='url', ).put() models.InstanceGroupManager( key=instances.get_instance_group_manager_key(key), ).put() instances.delete_pending(key)
def test_no_project(self): def get_instance_group_manager(*args, **kwargs): self.fail('get_instance_group_manager called') def resize_managed_instance_group(*args, **kwargs): self.fail('resize_managed_instance_group called') self.mock( instance_group_managers.gce.Project, 'get_instance_group_manager', get_instance_group_manager, ) self.mock( instance_group_managers.gce.Project, 'resize_managed_instance_group', resize_managed_instance_group, ) key = models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision', 'zone', ), minimum_size=10, maximum_size=10, url='https://example.com', ).put() models.InstanceTemplateRevision(key=key.parent(), ).put() models.InstanceTemplate(key=key.parent().parent()).put() instance_group_managers.resize(key)
def test_implicitly_drained(self): """Ensures implicitly drained entities are returned.""" key = models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision', 'zone', ), ).put() models.InstanceTemplateRevision( key=key.parent(), active=[ key, ], ).put() models.InstanceTemplate( key=key.parent().parent(), drained=[ key.parent(), ], ).put() expected_keys = [ key, ] self.assertItemsEqual( instance_group_managers.get_drained_instance_group_managers(), expected_keys, )
def test_several_instance_group_manager(self): models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name-1', 'revision', 'zone-a', ), instances=[ ndb.Key(models.Instance, 'fake-key-1'), ndb.Key(models.Instance, 'fake-key-2'), ], ).put() models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name-1', 'revision', 'zone-b', ), instances=[ ndb.Key(models.Instance, 'fake-key-3'), ], ).put() models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name-2', 'revision', 'zone', ), instances=[ ndb.Key(models.Instance, 'fake-key-4'), ], ).put() models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name-3', 'revision', 'zone', ), ).put() expected = {'base-name-1': 3, 'base-name-2': 1, 'base-name-3': 0} actual = instance_group_managers.count_instances() self.assertEqual(actual, expected)
def test_parent_doesnt_exist(self): """Ensures nothing happens when the parent doesn't exist.""" key = models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision', 'zone', ), ).put() instance_group_managers.create(key) self.failIf(key.get().url)
def test_url_unspecified(self): """Ensures no URL when URL is unspecified.""" key = models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision', 'zone', ), ).put() self.failIf( instance_group_managers.get_instance_group_manager_to_delete(key))
def test_nothing_active_or_drained(self): """Ensures nothing is returned when there are no active/drained entities.""" models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision', 'zone', ), ).put() self.failIf( instance_group_managers.get_drained_instance_group_managers())
def test_one_instance_group_manager_no_instances(self): models.InstanceGroupManager( key=instance_group_managers.get_instance_group_manager_key( 'base-name', 'revision', 'zone', ), ).put() expected = {'base-name': 0} actual = instance_group_managers.count_instances() self.assertEqual(actual, expected)