gcpdiag/queries/gcf.py (69 lines of code) (raw):

# Copyright 2021 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Lint as: python3 """Queries related to GCP CloudFunctions instances.""" import logging import re from typing import Dict, Mapping import googleapiclient.errors from gcpdiag import caching, config, models, utils from gcpdiag.queries import apis class CloudFunction(models.Resource): """Represents a GCF instance.""" _resource_data: dict def __init__(self, project_id, resource_data): super().__init__(project_id=project_id) self._resource_data = resource_data self._metadata_dict = None @property def name(self) -> str: m = re.search(r'/functions/([^/]+)$', self._resource_data['name']) if not m: raise RuntimeError('can\'t determine name of cloudfunction %s' % (self._resource_data['name'])) return m.group(1) @property def description(self) -> str: return self._resource_data['description'] @property def full_path(self) -> str: return self._resource_data['name'] @property def short_path(self) -> str: path = self.project_id + '/' + self.name return path @property def runtime(self) -> str: return self._resource_data['runtime'] @property def memory(self) -> str: return self._resource_data['availableMemoryMb'] @caching.cached_api_call def get_cloudfunctions(context: models.Context) -> Mapping[str, CloudFunction]: """Get a list of CloudFunctions matching the given context, indexed by CloudFunction name.""" cloudfunctions: Dict[str, CloudFunction] = {} if not apis.is_enabled(context.project_id, 'cloudfunctions'): return cloudfunctions gcf_api = apis.get_api('cloudfunctions', 'v1', context.project_id) logging.debug('fetching list of GCF functions in project %s', context.project_id) query = gcf_api.projects().locations().functions().list( parent=f'projects/{context.project_id}/locations/-') try: resp = query.execute(num_retries=config.API_RETRIES) if 'functions' not in resp: return cloudfunctions for f in resp['functions']: # verify that we have some minimal data that we expect if 'name' not in f or 'runtime' not in f: raise RuntimeError( 'missing data in projects.locations.functions.list response') # projects/*/locations/*/functions/* result = re.match( r'projects/[^/]+/(?:locations)/([^/]+)/functions/([^/]+)', f['name']) if not result: logging.error('invalid cloud functions data: %s', f['name']) continue location = result.group(1) labels = f.get('labels', {}) name = f.get('name', '') if not context.match_project_resource( location=location, labels=labels, resource=name): continue cloudfunctions[f['name']] = CloudFunction(project_id=context.project_id, resource_data=f) except googleapiclient.errors.HttpError as err: raise utils.GcpApiError(err) from err return cloudfunctions