def setUp(self): super(ApiTest, self).setUp() self.mock(acl, 'has_projects_access', mock.Mock()) acl.has_projects_access.side_effect = ( lambda pids: {pid: pid != 'secret' for pid in pids}) self.mock(acl, 'has_services_access', lambda sids: {sid: True for sid in sids}) self.mock(projects, 'get_projects', mock.Mock()) projects.get_projects.return_value = [ service_config_pb2.Project(id='chromium'), service_config_pb2.Project(id='v8'), ] self.mock( projects, 'get_metadata_async', mock.Mock( return_value=future({ 'chromium': project_config_pb2.ProjectCfg(), 'v8': project_config_pb2.ProjectCfg(), }))) self.mock( projects, 'get_repos_async', mock.Mock(return_value=future({ 'chromium': (projects.RepositoryType.GITILES, 'https://chromium.example.com'), 'v8': (projects.RepositoryType.GITILES, 'https://v8.example.com'), })))
def test_get_config_multi(self): projects.get_projects.return_value.extend([ service_config_pb2.Project(id='inconsistent'), service_config_pb2.Project(id='secret'), ]) projects.get_metadata_async.return_value.get_result().update({ 'inconsistent': project_config_pb2.ProjectCfg(access='all'), 'secret': project_config_pb2.ProjectCfg(access='administrators'), }) projects.get_repos_async.return_value.get_result().update({ 'inconsistent': (None, None), 'secret': (projects.RepositoryType.GITILES, 'https://localhost/secret'), }) self.mock(storage, 'get_latest_configs_async', mock.Mock()) storage.get_latest_configs_async.return_value = future({ 'projects/chromium': ( 'deadbeef', 'https://x.com/+/deadbeef', 'abc0123', 'config text' ), 'projects/v8': ( 'beefdead', 'https://x.com/+/beefdead', 'ccc123', None # no content ), 'projects/secret': ( 'badcoffee', 'https://x.com/+/badcoffee', 'abcabc', 'abcsdjksl' ), }) req = {'path': 'cq.cfg'} resp = self.call_api('get_project_configs', req).json_body self.assertEqual(resp, { 'configs': [{ 'config_set': 'projects/chromium', 'revision': 'deadbeef', 'content_hash': 'abc0123', 'content': base64.b64encode('config text'), 'url': 'https://x.com/+/deadbeef', }], })
def test_get_projects(self): projects.get_projects.return_value = [ service_config_pb2.Project(id='chromium'), service_config_pb2.Project(id='v8'), service_config_pb2.Project(id='inconsistent'), service_config_pb2.Project(id='secret'), ] projects.get_metadata_async.return_value = future({ 'chromium': project_config_pb2.ProjectCfg(name='Chromium, the best browser', access='all'), 'v8': project_config_pb2.ProjectCfg(access='all'), 'inconsistent': project_config_pb2.ProjectCfg(access='all'), 'secret': project_config_pb2.ProjectCfg(access='administrators'), }) projects.get_repos_async.return_value = future({ 'chromium': (projects.RepositoryType.GITILES, 'https://chromium.example.com'), 'v8': (projects.RepositoryType.GITILES, 'https://v8.example.com'), 'inconsistent': (None, None), 'secret': (projects.RepositoryType.GITILES, 'https://localhost/secret'), }) resp = self.call_api('get_projects', {}).json_body self.assertEqual( resp, { 'projects': [ { 'id': 'chromium', 'name': 'Chromium, the best browser', 'repo_type': 'GITILES', 'repo_url': 'https://chromium.example.com', }, { 'id': 'v8', 'repo_type': 'GITILES', 'repo_url': 'https://v8.example.com', }, ], })
def test_has_project_access_identity(self): self.mock(projects, 'get_metadata', mock.Mock()) projects.get_metadata.return_value = project_config_pb2.ProjectCfg( access=['group:googlers', '*****@*****.**']) self.assertFalse(acl.can_read_config_set('projects/secret')) auth.get_current_identity.return_value = auth.Identity( 'user', '*****@*****.**') self.assertTrue(acl.can_read_config_set('projects/secret'))
def test_has_project_access_no_access(self): self.set_up_identity() self.mock(api, 'get_project_config_async', mock.Mock()) api.get_project_config_async.return_value = ndb.Future() api.get_project_config_async.return_value.set_result( project_config_pb2.ProjectCfg(), # access not configured => internal ) self.assertFalse(config.has_project_access('chromium'))
def test_has_project_access_anon_identity(self): self.set_up_identity() self.mock(api, 'get_project_config_async', mock.Mock()) api.get_project_config_async.return_value = ndb.Future() api.get_project_config_async.return_value.set_result( project_config_pb2.ProjectCfg(access=['anonymous:anonymous'], )) self.assertTrue(config.has_project_access('chromium')) api.get_project_config_async.assert_called_once('chromium')
def test_has_project_access_group(self): self.mock(projects, 'get_metadata', mock.Mock()) projects.get_metadata.return_value = project_config_pb2.ProjectCfg( access=['group:googlers', '*****@*****.**']) self.assertFalse(acl.can_read_config_set('projects/secret')) auth.is_group_member.side_effect = lambda name: name == 'googlers' self.assertTrue(acl.can_read_config_set('projects/secret')) auth.is_group_member.side_effect = lambda name: name == 'project-admins' self.assertTrue(acl.can_read_config_set('projects/secret'))
def test_get_projects(self): projects.get_projects.return_value = [ service_config_pb2.Project(id='chromium'), service_config_pb2.Project(id='v8'), service_config_pb2.Project(id='inconsistent'), service_config_pb2.Project(id='secret'), ] projects.get_metadata.side_effect = [ project_config_pb2.ProjectCfg(name='Chromium, the best browser', access='all'), project_config_pb2.ProjectCfg(access='all'), project_config_pb2.ProjectCfg(access='all'), project_config_pb2.ProjectCfg(access='administrators'), ] projects.get_repo.side_effect = [ (projects.RepositoryType.GITILES, 'http://localhost/chromium'), (projects.RepositoryType.GITILES, 'http://localhost/v8'), (None, None), (projects.RepositoryType.GITILES, 'http://localhost/secret'), ] resp = self.call_api('get_projects', {}).json_body self.assertEqual( resp, { 'projects': [ { 'id': 'chromium', 'name': 'Chromium, the best browser', 'repo_type': 'GITILES', 'repo_url': 'http://localhost/chromium', }, { 'id': 'v8', 'repo_type': 'GITILES', 'repo_url': 'http://localhost/v8', }, ], })
def test_has_project_access_group(self): self.mock(projects, 'get_metadata_async', mock.Mock(return_value=future({ 'secret': project_config_pb2.ProjectCfg( access=['group:googlers', '*****@*****.**']), }))) self.assertFalse(can_read_config_set('projects/secret')) auth.is_group_member.side_effect = lambda name, *_: name == 'googlers' self.assertTrue(can_read_config_set('projects/secret')) auth.is_group_member.side_effect = lambda name, *_: name == 'project-admins' self.assertTrue(can_read_config_set('projects/secret'))
def get_metadata_async(project_ids): """Returns a mapping {project_id: metadata}. If a project does not exist, the metadata is None. The project metadata stored in project.cfg files in each project. """ PROJECT_DOES_NOT_EXIST_SENTINEL = (0, ) cache_ns = 'projects.get_metadata' ctx = ndb.get_context() # ctx.memcache_get is auto-batching. Internally it makes get_multi RPC. cache_futs = { pid: ctx.memcache_get(pid, namespace=cache_ns) for pid in project_ids } yield cache_futs.values() result = {} missing = [] for pid in project_ids: binary = cache_futs[pid].get_result() if binary is not None: # cache hit if binary == PROJECT_DOES_NOT_EXIST_SENTINEL: result[pid] = None else: cfg = project_config_pb2.ProjectCfg() cfg.ParseFromString(binary) result[pid] = cfg else: # cache miss missing.append(pid) if missing: fetched = yield _get_project_configs_async( missing, common.PROJECT_METADATA_FILENAME, project_config_pb2.ProjectCfg) result.update( fetched) # at this point result must have all project ids # Cache metadata for 10 min. In practice, it never changes. # ctx.memcache_set is auto-batching. Internally it makes set_multi RPC. yield [ ctx.memcache_set(pid, cfg.SerializeToString() if cfg else PROJECT_DOES_NOT_EXIST_SENTINEL, namespace=cache_ns, time=60 * 10) for pid, cfg in fetched.items() ] raise ndb.Return(result)
def setUp(self): super(ApiTest, self).setUp() self.mock(acl, 'has_project_access', mock.Mock()) acl.has_project_access.side_effect = (lambda pid: pid != 'secret') self.mock(acl, 'has_service_access', mock.Mock(return_value=True)) self.mock(projects, 'get_projects', mock.Mock()) projects.get_projects.return_value = [ service_config_pb2.Project(id='chromium'), service_config_pb2.Project(id='v8'), ] self.mock(projects, 'get_metadata', mock.Mock()) projects.get_metadata.return_value = project_config_pb2.ProjectCfg() self.mock(projects, 'get_repo', mock.Mock()) projects.get_repo.return_value = (projects.RepositoryType.GITILES, 'https://localhost/project')