gcpdiag.queries.crm
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 36 _id: str 37 _resource_data: dict 38 _number: int 39 40 def __init__(self, resource_data): 41 super().__init__(project_id=resource_data['projectId']) 42 self._id = resource_data['projectId'] 43 self._resource_data = resource_data 44 match = re.match(r'projects/(\d+)$', resource_data['name']) 45 if not match: 46 raise ValueError(f"can't determine project id ({resource_data})") 47 self._number = int(match.group(1)) 48 49 @property 50 def number(self) -> int: 51 return self._number 52 53 @property 54 def id(self) -> str: 55 """Project id (not project number).""" 56 return self._id 57 58 @property 59 def name(self) -> str: 60 return self._resource_data['displayName'] 61 62 @property 63 def full_path(self) -> str: 64 return f'projects/{self._id}' 65 66 @property 67 def short_path(self) -> str: 68 return self._id 69 70 @property 71 def default_compute_service_account(self) -> str: 72 return f'{self.number}-compute@developer.gserviceaccount.com' 73 74 @property 75 def parent(self) -> str: 76 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
40 def __init__(self, resource_data): 41 super().__init__(project_id=resource_data['projectId']) 42 self._id = resource_data['projectId'] 43 self._resource_data = resource_data 44 match = re.match(r'projects/(\d+)$', resource_data['name']) 45 if not match: 46 raise ValueError(f"can't determine project id ({resource_data})") 47 self._number = int(match.group(1))
Returns the full path of this resource.
Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'
Returns the short name for this resource.
Note that it isn't clear from this name what kind of resource it is.
Example: 'gke1'
79@caching.cached_api_call 80def get_project(project_id: str) -> Project: 81 """Attempts to retrieve project details for the supplied project id or number. 82 If the project is found/accessible, it returns a Project object with the resource data. 83 If the project cannot be retrieved, the application raises one of the exceptions below. 84 85 Args: 86 project_id (str): The project id or number of 87 the project (e.g., "123456789", "example-project"). 88 89 Returns: 90 Project: An object representing the project's full details. 91 92 Raises: 93 utils.GcpApiError: If there is an issue calling the GCP/HTTP Error API. 94 95 Usage: 96 When using project identifier from gcpdiag.models.Context 97 98 project = crm.get_project(context.project_id) 99 100 An unknown project identifier 101 try: 102 project = crm.get_project("123456789") 103 except: 104 # Handle exception 105 else: 106 # use project data 107 """ 108 try: 109 logging.debug('retrieving project %s ', project_id) 110 crm_api = apis.get_api('cloudresourcemanager', 'v3', project_id) 111 request = crm_api.projects().get(name=f'projects/{project_id}') 112 response = request.execute(num_retries=config.API_RETRIES) 113 except googleapiclient.errors.HttpError as e: 114 error = utils.GcpApiError(response=e) 115 if 'IAM_PERMISSION_DENIED' == error.reason: 116 error.message = ( 117 "Authenticated account doesn't have access to project details of " 118 f'{project_id}.\nExecute:\n' 119 f'gcloud projects add-iam-policy-binding {project_id} ' 120 '--role=roles/viewer ' 121 '--member="user|group|serviceAccount:EMAIL_ACCOUNT"' 122 ) 123 else: 124 error.message = f"can't access project {project_id}: {error.message}." 125 logging.debug('An Http Error occurred whiles accessing projects.get \n\n%s', e) 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
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 = ( 140 'parent.type:' 141 + project.parent.split('/')[0][:-1] 142 + ' parent.id:' 143 + project.parent.split('/')[1] 144 if project.parent 145 else '' 146 ) 147 148 api = apis.get_api('cloudresourcemanager', 'v3') 149 for p in apis_utils.list_all( 150 request=api.projects().search(query=p_filter), 151 next_function=api.projects().search_next, 152 response_keyword='projects', 153 ): 154 try: 155 crm_api = apis.get_api('cloudresourcemanager', 'v3', p['projectId']) 156 p_name = 'projects/' + p['projectId'] if 'projects/' not in p['projectId'] else p['projectId'] 157 request = crm_api.projects().get(name=p_name) 158 response = request.execute(num_retries=config.API_RETRIES) 159 projects.append(get_billing_info(response['projectId'])) 160 except (utils.GcpApiError, googleapiclient.errors.HttpError) as error: 161 if isinstance(error, googleapiclient.errors.HttpError): 162 error = utils.GcpApiError(error) 163 if error.reason in ['IAM_PERMISSION_DENIED', 'USER_PROJECT_DENIED', 'SERVICE_DISABLED']: 164 # skip projects that user does not have permissions on 165 continue 166 else: 167 print( 168 f'[ERROR]: An Http Error occurred whiles accessing projects.get \n\n{error}', 169 file=sys.stderr, 170 ) 171 raise error from error 172 return projects
Get all projects in the Parent Folder that current user has permission to view
175class Organization(models.Resource): 176 """Represents an Organization resource. 177 178 See also the API documentation: 179 https://cloud.google.com/resource-manager/reference/rest/v1/organizations/get 180 """ 181 182 _resource_data: dict 183 184 def __init__(self, project_id, resource_data): 185 super().__init__(project_id=project_id) 186 self._resource_data = resource_data 187 188 @property 189 def id(self) -> str: 190 """The numeric organization ID.""" 191 # Note: organization ID is returned in the format 'organizations/12345' 192 return self._resource_data['name'].split('/')[-1] 193 194 @property 195 def name(self) -> str: 196 """The organization's display name.""" 197 return self._resource_data['displayName'] 198 199 @property 200 def full_path(self) -> str: 201 return self._resource_data['name'] 202 203 @property 204 def short_path(self) -> str: 205 return f'organizations/{self.id}'
Represents an Organization resource.
See also the API documentation: https://cloud.google.com/resource-manager/reference/rest/v1/organizations/get
188 @property 189 def id(self) -> str: 190 """The numeric organization ID.""" 191 # Note: organization ID is returned in the format 'organizations/12345' 192 return self._resource_data['name'].split('/')[-1]
The numeric organization ID.
194 @property 195 def name(self) -> str: 196 """The organization's display name.""" 197 return self._resource_data['displayName']
The organization's display name.
208@caching.cached_api_call 209def get_organization(project_id: str, skip_error_print: bool = True) -> Organization | None: 210 """Retrieves the parent Organization for a given project. 211 212 This function first finds the project's ancestry to identify the 213 organization ID, then fetches the organization's details. 214 215 Args: 216 project_id (str): The ID of the project whose organization is to be fetched. 217 218 Returns: 219 An Organization object if the project belongs to an organization, 220 otherwise None. 221 222 Raises: 223 utils.GcpApiError: If there is an issue calling the GCP/HTTP Error API. 224 """ 225 try: 226 logging.debug('retrieving ancestry for project %s', project_id) 227 crm_v1_api = apis.get_api('cloudresourcemanager', 'v1', project_id) 228 ancestry_request = crm_v1_api.projects().getAncestry(projectId=project_id) 229 ancestry_response = ancestry_request.execute(num_retries=config.API_RETRIES) 230 231 org_id = None 232 for ancestor in ancestry_response.get('ancestor', []): 233 if ancestor.get('resourceId', {}).get('type') == 'organization': 234 org_id = ancestor['resourceId']['id'] 235 break 236 237 if not org_id: 238 logging.debug('project %s is not part of an organization', project_id) 239 return None 240 241 crm_v1_api = apis.get_api('cloudresourcemanager', 'v1', project_id) 242 org_request = crm_v1_api.organizations().get(name=f'organizations/{org_id}') 243 org_response = org_request.execute(num_retries=config.API_RETRIES) 244 245 return Organization(project_id=project_id, resource_data=org_response) 246 247 except googleapiclient.errors.HttpError as e: 248 error = utils.GcpApiError(response=e) 249 if not skip_error_print: 250 print( 251 f"[ERROR]: can't access organization for project {project_id}: {error.message}.", 252 file=sys.stderr, 253 ) 254 print( 255 f'[DEBUG]: An Http Error occurred while accessing organization details \n\n{e}', 256 file=sys.stderr, 257 ) 258 raise error from e
Retrieves the parent Organization for a given project.
This function first finds the project's ancestry to identify the organization ID, then fetches the organization's details.
Arguments:
- project_id (str): The ID of the project whose organization is to be fetched.
Returns:
An Organization object if the project belongs to an organization, otherwise None.
Raises:
- utils.GcpApiError: If there is an issue calling the GCP/HTTP Error API.