gcpdiag.models
32class Messages(dict): 33 34 def get_msg(self, template: str, **kwargs): 35 return self.get( 36 template, 37 'NOTICE: No message available to parse for this step').format(**kwargs)
Inherited Members
- builtins.dict
- get
- setdefault
- pop
- popitem
- keys
- items
- values
- update
- fromkeys
- clear
- copy
44class Parameter(dict[T, V], Generic[T, V]): 45 """Class to store parameters""" 46 47 def __init__(self, *args, **kwargs): 48 super().__init__() 49 for dict_arg in args: 50 for key, value in dict_arg.items(): 51 self[key] = value 52 for key, value in kwargs.items(): 53 self[key] = value 54 55 def _parse_value(self, value: str) -> Any: 56 """Make all values lower string and strip whitespaces.""" 57 if isinstance(value, str): 58 return value.strip().lower() 59 return value 60 61 def __setitem__(self, key: T, value: V) -> None: 62 super().__setitem__(key, self._parse_value(value)) 63 64 def update(self, *args, **kwargs) -> None: 65 for k, v in dict(*args, **kwargs).items(): 66 self[k] = v 67 68 def setdefault(self, key: T, default: V = None) -> V: 69 if key not in self: 70 converted_default = self._parse_value(default) if isinstance( 71 default, str) else default 72 self[key] = converted_default 73 return super().setdefault(key, self[key])
Class to store parameters
Inherited Members
- builtins.dict
- update
- setdefault
- get
- pop
- popitem
- keys
- items
- values
- fromkeys
- clear
- copy
76class Operation(dict): 77 """ Operation Object to help with getting parameters and templates""" 78 messages: Messages 79 parameters: Parameter 80 81 def __init__(self, message: Messages, parameters: Parameter): 82 self.messages = message 83 self.parameters = parameters 84 85 def get_msg(self, key, **kwargs): 86 return self.messages.get_msg(key, **kwargs) 87 88 def get(self, key): 89 return self.parameters.get(key) 90 91 def __getitem__(self, key): 92 # Redirect item access to parameters 93 return self.parameters[key] 94 95 def __setitem__(self, key, value): 96 # Redirect item setting to parameters 97 self.parameters[key] = value
Operation Object to help with getting parameters and templates
Inherited Members
- builtins.dict
- setdefault
- pop
- popitem
- keys
- items
- values
- update
- fromkeys
- clear
- copy
100@dataclasses.dataclass 101class Context: 102 """List of resource groups / scopes that should be analyzed.""" 103 # project_id of project that is being analyzed, mandatory 104 project_id: str 105 # a pattern of sub project resources that match 106 resources_pattern: Optional[re.Pattern] 107 # list of GCP all locations to use as linting scope 108 # i.e. regions (e.g.: 'us-central1') or zone (e.g.: 'us-central1-a'). 109 # a compiled project resources provided by user 110 locations_pattern: Optional[re.Pattern] 111 112 # list of "label sets" that must match. 113 labels: Optional[Mapping[str, str]] 114 # list of "runbook parameters sets" that must match. 115 parameters: Parameter[str, str] 116 117 # the selected resources are the intersection of project_id, locations, 118 # and labels(i.e. all must match), but each value in locations, and 119 # labels is a OR, so it means: 120 # project_id AND 121 # (region1 OR region2) AND 122 # ({label1=value1,label2=value2} OR {label3=value3}) 123 124 def __init__(self, 125 project_id: str, 126 locations: Optional[Iterable[str]] = None, 127 labels: Optional[Mapping[str, str]] = None, 128 parameters: Optional[Parameter[str, str]] = None, 129 resources: Optional[Iterable[str]] = None): 130 """Args: 131 132 project: project_id of project that should be inspected. 133 locations: only include resources in these GCP locations. 134 labels: only include resources with these labels. Expected 135 is a dict, is a set of key=value pairs that must match. 136 137 Example: `{'key1'='bla', 'key2'='baz'}`. This 138 will match resources that either have key1=bla or key2=baz. 139 resources: only include sub project resources with this name attribute. 140 """ 141 142 self.project_id = project_id 143 144 if locations: 145 if not isinstance(locations, List): 146 raise ValueError( 147 str(locations) + ' did not supply full list of locations') 148 for location in locations: 149 if not (utils.is_region(location) or utils.is_zone(location)): 150 raise ValueError(location + ' does not look like a valid region/zone') 151 152 self.locations_pattern = re.compile('|'.join(locations), re.IGNORECASE) 153 else: 154 self.locations_pattern = None 155 156 if labels: 157 if not isinstance(labels, Mapping): 158 raise ValueError('labels must be Mapping[str,str]]') 159 160 self.labels = labels 161 else: 162 self.labels = None 163 164 if resources: 165 if not isinstance(resources, List): 166 raise ValueError( 167 str(resources) + ' did not supply full list of resources') 168 169 self.resources_pattern = re.compile('|'.join(resources), re.IGNORECASE) 170 171 else: 172 self.resources_pattern = None 173 174 if parameters: 175 if not isinstance(parameters, Mapping): 176 raise ValueError('parameters must be Mapping[str,str]]') 177 178 self.parameters = Parameter(parameters) 179 self.parameters['project_id'] = self.project_id 180 else: 181 self.parameters = Parameter() 182 183 def __str__(self): 184 string = 'project: ' + self.project_id 185 if self.resources_pattern: 186 string += ', resources: ' + self.resources_pattern.pattern 187 if self.locations_pattern: 188 string += ', locations (regions/zones): ' + self.locations_pattern.pattern 189 if self.labels: 190 string += ', labels: {' + _mapping_str(self.labels) + '}' 191 if self.parameters: 192 string += ', parameters: {' + _mapping_str(self.parameters) + '}' 193 return string 194 195 def __hash__(self): 196 return self.__str__().__hash__() 197 198 IGNORELOCATION = 'IGNORELOCATION' 199 IGNORELABEL = MappingProxyType({'IGNORELABEL': 'IGNORELABEL'}) 200 201 def match_project_resource( 202 self, 203 resource: Optional[str], 204 location: Optional[str] = IGNORELOCATION, 205 labels: Optional[Mapping[str, str]] = IGNORELABEL, 206 ) -> bool: 207 """Compare resource fields to the name and/or location and/or labels supplied 208 by the user and return a boolean outcome depending on the context. 209 210 Args: 211 rosource: name of the resource under analysis. Always inspected if user 212 supplied a name criteria 213 214 location: region or zone of the resource. IGNORELOCATION completely skips analysis 215 of the location even if user has supplied location criteria 216 217 labels: labels in the resource under inspection. Functions which do not 218 support labels can completely skip checks by providing the IGNORELABEL constant 219 220 Returns: 221 A boolean which indicates the outcome of the analysis 222 """ 223 224 # Match resources. 225 if self.resources_pattern: 226 if not resource or not self.resources_pattern.match(resource): 227 return False 228 229 # Match location. 230 if self.locations_pattern and location is not self.IGNORELOCATION: 231 if not location or not self.locations_pattern.match(location): 232 return False 233 234 # Match labels. 235 if self.labels and labels is not self.IGNORELABEL: 236 if not labels: 237 return False 238 239 if any(labels.get(k) == v for k, v in self.labels.items()): 240 pass 241 else: 242 return False 243 244 # Everything matched. 245 return True
List of resource groups / scopes that should be analyzed.
124 def __init__(self, 125 project_id: str, 126 locations: Optional[Iterable[str]] = None, 127 labels: Optional[Mapping[str, str]] = None, 128 parameters: Optional[Parameter[str, str]] = None, 129 resources: Optional[Iterable[str]] = None): 130 """Args: 131 132 project: project_id of project that should be inspected. 133 locations: only include resources in these GCP locations. 134 labels: only include resources with these labels. Expected 135 is a dict, is a set of key=value pairs that must match. 136 137 Example: `{'key1'='bla', 'key2'='baz'}`. This 138 will match resources that either have key1=bla or key2=baz. 139 resources: only include sub project resources with this name attribute. 140 """ 141 142 self.project_id = project_id 143 144 if locations: 145 if not isinstance(locations, List): 146 raise ValueError( 147 str(locations) + ' did not supply full list of locations') 148 for location in locations: 149 if not (utils.is_region(location) or utils.is_zone(location)): 150 raise ValueError(location + ' does not look like a valid region/zone') 151 152 self.locations_pattern = re.compile('|'.join(locations), re.IGNORECASE) 153 else: 154 self.locations_pattern = None 155 156 if labels: 157 if not isinstance(labels, Mapping): 158 raise ValueError('labels must be Mapping[str,str]]') 159 160 self.labels = labels 161 else: 162 self.labels = None 163 164 if resources: 165 if not isinstance(resources, List): 166 raise ValueError( 167 str(resources) + ' did not supply full list of resources') 168 169 self.resources_pattern = re.compile('|'.join(resources), re.IGNORECASE) 170 171 else: 172 self.resources_pattern = None 173 174 if parameters: 175 if not isinstance(parameters, Mapping): 176 raise ValueError('parameters must be Mapping[str,str]]') 177 178 self.parameters = Parameter(parameters) 179 self.parameters['project_id'] = self.project_id 180 else: 181 self.parameters = Parameter()
Args:
project: project_id of project that should be inspected. locations: only include resources in these GCP locations. labels: only include resources with these labels. Expected is a dict, is a set of key=value pairs that must match.
Example: {'key1'='bla', 'key2'='baz'}
. This
will match resources that either have key1=bla or key2=baz.
resources: only include sub project resources with this name attribute.
201 def match_project_resource( 202 self, 203 resource: Optional[str], 204 location: Optional[str] = IGNORELOCATION, 205 labels: Optional[Mapping[str, str]] = IGNORELABEL, 206 ) -> bool: 207 """Compare resource fields to the name and/or location and/or labels supplied 208 by the user and return a boolean outcome depending on the context. 209 210 Args: 211 rosource: name of the resource under analysis. Always inspected if user 212 supplied a name criteria 213 214 location: region or zone of the resource. IGNORELOCATION completely skips analysis 215 of the location even if user has supplied location criteria 216 217 labels: labels in the resource under inspection. Functions which do not 218 support labels can completely skip checks by providing the IGNORELABEL constant 219 220 Returns: 221 A boolean which indicates the outcome of the analysis 222 """ 223 224 # Match resources. 225 if self.resources_pattern: 226 if not resource or not self.resources_pattern.match(resource): 227 return False 228 229 # Match location. 230 if self.locations_pattern and location is not self.IGNORELOCATION: 231 if not location or not self.locations_pattern.match(location): 232 return False 233 234 # Match labels. 235 if self.labels and labels is not self.IGNORELABEL: 236 if not labels: 237 return False 238 239 if any(labels.get(k) == v for k, v in self.labels.items()): 240 pass 241 else: 242 return False 243 244 # Everything matched. 245 return True
Compare resource fields to the name and/or location and/or labels supplied by the user and return a boolean outcome depending on the context.
Arguments:
- rosource: name of the resource under analysis. Always inspected if user
- supplied a name criteria
- location: region or zone of the resource. IGNORELOCATION completely skips analysis
- of the location even if user has supplied location criteria
- labels: labels in the resource under inspection. Functions which do not
- support labels can completely skip checks by providing the IGNORELABEL constant
Returns:
A boolean which indicates the outcome of the analysis
248class Resource(abc.ABC): 249 """Represents a single resource in GCP.""" 250 _project_id: str 251 252 def __init__(self, project_id): 253 self._project_id = project_id 254 255 def __str__(self): 256 return self.full_path 257 258 def __hash__(self): 259 return self.full_path.__hash__() 260 261 def __lt__(self, other): 262 return self.full_path < other.full_path 263 264 def __eq__(self, other): 265 if self.__class__ == other.__class__: 266 return self.full_path == other.full_path 267 else: 268 return False 269 270 @property 271 def project_id(self) -> str: 272 """Project id (not project number).""" 273 return self._project_id 274 275 @property 276 @abc.abstractmethod 277 def full_path(self) -> str: 278 """Returns the full path of this resource. 279 280 Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1' 281 """ 282 pass 283 284 @property 285 def short_path(self) -> str: 286 """Returns the short name for this resource. 287 288 Note that it isn't clear from this name what kind of resource it is. 289 290 Example: 'gke1' 291 """ 292 return self.full_path
Represents a single resource in GCP.
270 @property 271 def project_id(self) -> str: 272 """Project id (not project number).""" 273 return self._project_id
Project id (not project number).
275 @property 276 @abc.abstractmethod 277 def full_path(self) -> str: 278 """Returns the full path of this resource. 279 280 Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1' 281 """ 282 pass
Returns the full path of this resource.
Example: 'projects/gcpdiag-gke-1-9b90/zones/europe-west4-a/clusters/gke1'
284 @property 285 def short_path(self) -> str: 286 """Returns the short name for this resource. 287 288 Note that it isn't clear from this name what kind of resource it is. 289 290 Example: 'gke1' 291 """ 292 return self.full_path
Returns the short name for this resource.
Note that it isn't clear from this name what kind of resource it is.
Example: 'gke1'