def get_serving_versions(self) -> FrozenSet[common.VersionKey]: """Returns the serving versions of every Nomulus service. For each service in appengine.SERVICES, gets the version(s) actually serving traffic. Services with the 'SERVING' status but no allocated traffic are not included. Services not included in appengine.SERVICES are also ignored. Returns: An immutable collection of the serving versions grouped by service. """ services = common.list_all_pages(self._services.list, 'services', appsId=self._project) # Response format is specified at # http://googleapis.github.io/google-api-python-client/docs/dyn/appengine_v1beta.apps.services.html#list. versions = [] for service in services: if service['id'] in SERVICES: # yapf: disable versions_with_traffic = ( service.get('split', {}).get('allocations', {}).keys()) # yapf: enable for version in versions_with_traffic: versions.append(common.VersionKey(service['id'], version)) return frozenset(versions)
def test_list_all_pages_single_page(self): self._mock_request.execute.return_value = {'data': [1]} response = common.list_all_pages(self._mock_api.list, 'data', appsId='project') self.assertSequenceEqual(response, [1]) self._mock_api.list.assert_called_once_with(pageToken=None, appsId='project')
def get_version_configs( self, versions: Set[common.VersionKey] ) -> FrozenSet[common.VersionConfig]: # yapf: enable """Returns the configuration of requested versions. For each version in the request, gets the rollback-related data from its static configuration (found in appengine-web.xml). Args: versions: A set of the VersionKey objects, each containing the versions being queried in that service. Returns: The version configurations in an immutable set. """ requested_services = {version.service_id for version in versions} version_configs = [] # Sort the requested services for ease of testing. For now the mocked # AppEngine admin in appengine_test can only respond in a fixed order. for service_id in sorted(requested_services): response = common.list_all_pages(self._versions.list, 'versions', appsId=self._project, servicesId=service_id) # Format of version_list is defined at # https://googleapis.github.io/google-api-python-client/docs/dyn/appengine_v1beta.apps.services.versions.html#list. for version in response: if common.VersionKey(service_id, version['id']) in versions: scalings = [ s for s in list(common.AppEngineScaling) if s.value in version ] if len(scalings) != 1: raise common.CannotRollbackError( f'Expecting exactly one scaling, found {scalings}') scaling = common.AppEngineScaling(list(scalings)[0]) if scaling == common.AppEngineScaling.MANUAL: manual_instances = version.get( scaling.value).get('instances') else: manual_instances = None version_configs.append( common.VersionConfig(service_id, version['id'], scaling, manual_instances)) return frozenset(version_configs)
def test_list_all_pages_multi_page(self): self._mock_request.execute.side_effect = [{ 'data': [1], 'nextPageToken': 'token' }, { 'data': [2] }] response = common.list_all_pages(self._mock_api.list, 'data', appsId='project') self.assertSequenceEqual(response, [1, 2]) self.assertSequenceEqual(self._mock_api.list.call_args_list, [ call(pageToken=None, appsId='project'), call(pageToken='token', appsId='project') ])
def list_instances( self, version: common.VersionKey) -> Tuple[common.VmInstanceInfo, ...]: instances = common.list_all_pages(self._versions.instances().list, 'instances', appsId=self._project, servicesId=version.service_id, versionsId=version.version_id) # Format of version_list is defined at # https://googleapis.github.io/google-api-python-client/docs/dyn/appengine_v1beta.apps.services.versions.instances.html#list return tuple([ common.VmInstanceInfo( inst['id'], common.parse_gcp_timestamp(inst['startTime'])) for inst in instances ])