gcpdiag.models

Data structures representing generic objects in GCP.
class Messages(builtins.dict):
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)
def get_msg(self, template: str, **kwargs):
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
class Parameter(dict[~T, ~V], typing.Generic[~T, ~V]):
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
class Operation(builtins.dict):
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

messages: Messages
parameters: Parameter
def get_msg(self, key, **kwargs):
85  def get_msg(self, key, **kwargs):
86    return self.messages.get_msg(key, **kwargs)
def get(self, key):
88  def get(self, key):
89    return self.parameters.get(key)

Return the value for key if key is in the dictionary, else default.

Inherited Members
builtins.dict
setdefault
pop
popitem
keys
items
values
update
fromkeys
clear
copy
@dataclasses.dataclass
class Context:
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.

Context( project_id: str, locations: Optional[Iterable[str]] = None, labels: Optional[Mapping[str, str]] = None, parameters: Optional[Parameter[str, str]] = None, resources: Optional[Iterable[str]] = None)
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.

project_id: str
resources_pattern: Optional[re.Pattern]
locations_pattern: Optional[re.Pattern]
labels: Optional[Mapping[str, str]]
parameters: Parameter[str, str]
IGNORELOCATION = 'IGNORELOCATION'
IGNORELABEL = mappingproxy({'IGNORELABEL': 'IGNORELABEL'})
def match_project_resource( self, resource: Optional[str], location: Optional[str] = 'IGNORELOCATION', labels: Optional[Mapping[str, str]] = mappingproxy({'IGNORELABEL': 'IGNORELABEL'})) -> bool:
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

class Resource(abc.ABC):
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.

project_id: str
270  @property
271  def project_id(self) -> str:
272    """Project id (not project number)."""
273    return self._project_id

Project id (not project number).

full_path: str
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'

short_path: str
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'