gcpdiag.queries.crm

Queries related to Resource Manager (projects, resources).
class Project(gcpdiag.models.Resource):
29class Project(models.Resource):
30  """Represents a Project resource.
31
32  See also the API documentation:
33  https://cloud.google.com/resource-manager/reference/rest/v3/projects/get
34  """
35  _id: str
36  _resource_data: dict
37  _number: int
38
39  def __init__(self, resource_data):
40    super().__init__(project_id=resource_data['projectId'])
41    self._id = resource_data['projectId']
42    self._resource_data = resource_data
43    match = re.match(r'projects/(\d+)$', resource_data['name'])
44    if not match:
45      raise ValueError(f'can\'t determine project id ({resource_data})')
46    self._number = int(match.group(1))
47
48  @property
49  def number(self) -> int:
50    return self._number
51
52  @property
53  def id(self) -> str:
54    """Project id (not project number)."""
55    return self._id
56
57  @property
58  def name(self) -> str:
59    return self._resource_data['displayName']
60
61  @property
62  def full_path(self) -> str:
63    return f'projects/{self._id}'
64
65  @property
66  def short_path(self) -> str:
67    return self._id
68
69  @property
70  def default_compute_service_account(self) -> str:
71    return f'{self.number}-compute@developer.gserviceaccount.com'
72
73  @property
74  def parent(self) -> str:
75    return self._resource_data['parent']

Represents a Project resource.

See also the API documentation: https://cloud.google.com/resource-manager/reference/rest/v3/projects/get

Project(resource_data)
39  def __init__(self, resource_data):
40    super().__init__(project_id=resource_data['projectId'])
41    self._id = resource_data['projectId']
42    self._resource_data = resource_data
43    match = re.match(r'projects/(\d+)$', resource_data['name'])
44    if not match:
45      raise ValueError(f'can\'t determine project id ({resource_data})')
46    self._number = int(match.group(1))
number: int
48  @property
49  def number(self) -> int:
50    return self._number
id: str
52  @property
53  def id(self) -> str:
54    """Project id (not project number)."""
55    return self._id

Project id (not project number).

name: str
57  @property
58  def name(self) -> str:
59    return self._resource_data['displayName']
full_path: str
61  @property
62  def full_path(self) -> str:
63    return f'projects/{self._id}'

Returns the full path of this resource.

Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'

short_path: str
65  @property
66  def short_path(self) -> str:
67    return self._id

Returns the short name for this resource.

Note that it isn't clear from this name what kind of resource it is.

Example: 'gke1'

default_compute_service_account: str
69  @property
70  def default_compute_service_account(self) -> str:
71    return f'{self.number}-compute@developer.gserviceaccount.com'
parent: str
73  @property
74  def parent(self) -> str:
75    return self._resource_data['parent']
@caching.cached_api_call
def get_project(project_id: str) -> Project:
 78@caching.cached_api_call
 79def get_project(project_id: str) -> Project:
 80  '''Attempts to retrieve project details for the supplied project id or number.
 81    If the project is found/accessible, it returns a Project object with the resource data.
 82    If the project cannot be retrieved, the application raises one of the exceptions below.
 83
 84    Args:
 85        project_id (str): The project id or number of
 86        the project (e.g., "123456789", "example-project").
 87
 88    Returns:
 89        Project: An object representing the project's full details.
 90
 91    Raises:
 92        utils.GcpApiError: If there is an issue calling the GCP/HTTP Error API.
 93
 94    Usage:
 95        When using project identifier from gcpdiag.models.Context
 96
 97        project = crm.get_project(context.project_id)
 98
 99        An unknown project identifier
100        try:
101          project = crm.get_project("123456789")
102        except:
103          # Handle exception
104        else:
105          # use project data
106  '''
107  try:
108    logging.info('retrieving project %s ', project_id)
109    crm_api = apis.get_api('cloudresourcemanager', 'v3', project_id)
110    request = crm_api.projects().get(name=f'projects/{project_id}')
111    response = request.execute(num_retries=config.API_RETRIES)
112  except googleapiclient.errors.HttpError as e:
113    error = utils.GcpApiError(response=e)
114    if 'IAM_PERMISSION_DENIED' == error.reason:
115      print(
116          f'[ERROR]:Authenticated account doesn\'t have access to project details of {project_id}.'
117          f'\nExecute:\ngcloud projects add-iam-policy-binding {project_id} --role=roles/viewer'
118          '--member="user|group|serviceAccount:EMAIL_ACCOUNT" ',
119          file=sys.stderr)
120    else:
121      print(f'[ERROR]:can\'t access project {project_id}: {error.message}.',
122            file=sys.stderr)
123    print(
124        f'[DEBUG]: An Http Error occured whiles accessing projects.get \n\n{e}',
125        file=sys.stderr)
126    raise error from e
127  else:
128    return Project(resource_data=response)

Attempts to retrieve project details for the supplied project id or number. If the project is found/accessible, it returns a Project object with the resource data. If the project cannot be retrieved, the application raises one of the exceptions below.

Arguments:
  • project_id (str): The project id or number of
  • the project (e.g., "123456789", "example-project").
Returns:

Project: An object representing the project's full details.

Raises:
  • utils.GcpApiError: If there is an issue calling the GCP/HTTP Error API.
Usage:

When using project identifier from gcpdiag.models.Context

project = crm.get_project(context.project_id)

An unknown project identifier try: project = crm.get_project("123456789") except: # Handle exception else: # use project data

@caching.cached_api_call
def get_all_projects_in_parent(project_id: str) -> List[gcpdiag.queries.billing.ProjectBillingInfo]:
131@caching.cached_api_call
132def get_all_projects_in_parent(project_id: str) -> List[ProjectBillingInfo]:
133  """Get all projects in the Parent Folder that current user has
134  permission to view"""
135  projects: List[ProjectBillingInfo] = []
136  if (not project_id) or (not apis.is_enabled(project_id, 'cloudbilling')):
137    return projects
138  project = get_project(project_id)
139  p_filter = 'parent.type:'+project.parent.split('/')[0][:-1]\
140    +' parent.id:'+project.parent.split('/')[1] if project.parent else ''
141
142  api = apis.get_api('cloudresourcemanager', 'v3')
143  for p in apis_utils.list_all(request=api.projects().list(filter=p_filter),
144                               next_function=api.projects().list_next,
145                               response_keyword='projects'):
146    try:
147      crm_api = apis.get_api('cloudresourcemanager', 'v3', p['projectId'])
148      p_name = 'projects/' + p['projectId'] if 'projects/' not in p[
149          'projectId'] else p['projectId']
150      request = crm_api.projects().get(name=p_name)
151      response = request.execute(num_retries=config.API_RETRIES)
152      projects.append(
153          ProjectBillingInfo(response['projectId'],
154                             get_billing_info(p['projectId'])))
155    except (utils.GcpApiError, googleapiclient.errors.HttpError) as error:
156      if isinstance(error, googleapiclient.errors.HttpError):
157        error = utils.GcpApiError(error)
158      if error.reason in [
159          'IAM_PERMISSION_DENIED', 'USER_PROJECT_DENIED', 'SERVICE_DISABLED'
160      ]:
161        # skip projects that user does not have permissions on
162        continue
163      else:
164        print(
165            f'[ERROR]: An Http Error occured whiles accessing projects.get \n\n{error}',
166            file=sys.stderr)
167      raise error from error
168  return projects

Get all projects in the Parent Folder that current user has permission to view