qontract-reconcile 0.10.2.dev44__py3-none-any.whl → 0.10.2.dev45__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qontract-reconcile
3
- Version: 0.10.2.dev44
3
+ Version: 0.10.2.dev45
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Project-URL: homepage, https://github.com/app-sre/qontract-reconcile
6
6
  Project-URL: repository, https://github.com/app-sre/qontract-reconcile
@@ -195,11 +195,11 @@ reconcile/endpoints_discovery/integration.py,sha256=fzy5tv4c_6WoAZGGBNJ276NVNB1O
195
195
  reconcile/endpoints_discovery/merge_request.py,sha256=_yLb4tnvoZMCko8rta2C_CvOInJa9pa3HzSmHNtjgGU,2978
196
196
  reconcile/endpoints_discovery/merge_request_manager.py,sha256=wUMsumxv8RnWaRattax4HfoRlhtVzmgro3GiJJ1C4Vc,6392
197
197
  reconcile/external_resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
198
- reconcile/external_resources/aws.py,sha256=2vimGEtcwcIyVaFvt5Ab2PAAWu2hawJyCu4fuZWSobI,7783
199
- reconcile/external_resources/factories.py,sha256=ldvm3n13IdE899Ei850NSf0RQ35rzsKJBCUkROrbE5Y,5579
198
+ reconcile/external_resources/aws.py,sha256=KAs656zj4oZYpVbgWsDLXnJWJlLOZdR1JnRhikbF4x0,10712
199
+ reconcile/external_resources/factories.py,sha256=C0QHT0soEv6z99-ELAAE19S5MaMHhV0t1fSiQn0Coc4,5970
200
200
  reconcile/external_resources/integration.py,sha256=JF38M7R0Z4ADUTx57TZqSZH9k_xpPlbAxQAcGyIISuM,6925
201
201
  reconcile/external_resources/integration_secrets_sync.py,sha256=dX09O3r6KURziUYYfiki10orNjOGVma-XojhVqd0ww4,1667
202
- reconcile/external_resources/manager.py,sha256=DtxjWx34WdPjPR5TzqV4mZpN_Gn20LcNTZHBbPxqzuQ,16953
202
+ reconcile/external_resources/manager.py,sha256=ZagwLn6YQ1XmgmMN3qpuDzQsQxa4VOYl-IQPZBwCDqM,17103
203
203
  reconcile/external_resources/meta.py,sha256=noaytFzmShpzLA_ebGh7wuP45mOfHIOnnoUxivjDa1I,672
204
204
  reconcile/external_resources/metrics.py,sha256=KiBjMUaN_z0cSkF_7Ar_a8RiuiwVqjyMcVdISlxhzXE,3898
205
205
  reconcile/external_resources/model.py,sha256=EpgIgVRPUsyfHhgjHv_TLUKjzFiIQt0wUd30K0NJJpI,11826
@@ -751,7 +751,7 @@ tools/sd_app_sre_alert_report.py,sha256=jQpJdXVID68bSNtJNOGDh0-ei1CfEUS4Itr4MAaB
751
751
  tools/template_validation.py,sha256=qpKYaTgk0GOPGa2Ct5_5sKdwIHtCAKIBGzsMPuJU5fw,3371
752
752
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
753
753
  tools/cli_commands/container_images_report.py,sha256=8fG9XU-eEhJ7hKCdQzBcdPpvIJR-8WGkHOgFEulpfYQ,5213
754
- tools/cli_commands/erv2.py,sha256=iqihq908iJYIQoaNq3Iofp5SQP8S-DhTurzGdnEragU,23218
754
+ tools/cli_commands/erv2.py,sha256=VxUlNXllo947UwmtvS-42IeI9x_t_X3MHrrSI3K_GRo,23274
755
755
  tools/cli_commands/gpg_encrypt.py,sha256=NhzwN49UN7P5_FJgTUN5A4BIwNbFokIE4lwDax2iP5k,4891
756
756
  tools/cli_commands/systems_and_tools.py,sha256=EMHOF1AtUDaoSk0bbjl6oUKYAz4rTZjIBaF-6E6GspM,16816
757
757
  tools/cli_commands/cost_report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -773,7 +773,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
773
773
  tools/saas_promotion_state/saas_promotion_state.py,sha256=UfwwRLS5Ya4_Nh1w5n1dvoYtchQvYE9yj1VANt2IKqI,3925
774
774
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
775
775
  tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
776
- qontract_reconcile-0.10.2.dev44.dist-info/METADATA,sha256=VYLbNmLeQFocQBk-JLSHKadTfEMFX8Oq3W0GgOBUpkk,24665
777
- qontract_reconcile-0.10.2.dev44.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
778
- qontract_reconcile-0.10.2.dev44.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
779
- qontract_reconcile-0.10.2.dev44.dist-info/RECORD,,
776
+ qontract_reconcile-0.10.2.dev45.dist-info/METADATA,sha256=weP8FCL2ss8xcapzIrtxBfdAV3gAjUFV5fhHpfGe3nY,24665
777
+ qontract_reconcile-0.10.2.dev45.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
778
+ qontract_reconcile-0.10.2.dev45.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
779
+ qontract_reconcile-0.10.2.dev45.dist-info/RECORD,,
@@ -1,9 +1,11 @@
1
+ import re
1
2
  from abc import ABC, abstractmethod
2
3
  from typing import Any
3
4
 
4
5
  from reconcile.external_resources.model import (
5
6
  ExternalResource,
6
7
  ExternalResourceKey,
8
+ ExternalResourceModuleConfiguration,
7
9
  ExternalResourcesInventory,
8
10
  )
9
11
  from reconcile.utils.external_resource_spec import (
@@ -21,10 +23,18 @@ class AWSResourceFactory(ABC):
21
23
  self.secret_reader = secret_reader
22
24
 
23
25
  @abstractmethod
24
- def resolve(self, spec: ExternalResourceSpec) -> dict[str, Any]: ...
26
+ def resolve(
27
+ self,
28
+ spec: ExternalResourceSpec,
29
+ module_conf: ExternalResourceModuleConfiguration,
30
+ ) -> dict[str, Any]: ...
25
31
 
26
32
  @abstractmethod
27
- def validate(self, resource: ExternalResource) -> None: ...
33
+ def validate(
34
+ self,
35
+ resource: ExternalResource,
36
+ module_conf: ExternalResourceModuleConfiguration,
37
+ ) -> None: ...
28
38
 
29
39
  def find_linked_resources(
30
40
  self, spec: ExternalResourceSpec
@@ -35,10 +45,18 @@ class AWSResourceFactory(ABC):
35
45
 
36
46
 
37
47
  class AWSDefaultResourceFactory(AWSResourceFactory):
38
- def resolve(self, spec: ExternalResourceSpec) -> dict[str, Any]:
48
+ def resolve(
49
+ self,
50
+ spec: ExternalResourceSpec,
51
+ module_conf: ExternalResourceModuleConfiguration,
52
+ ) -> dict[str, Any]:
39
53
  return ResourceValueResolver(spec=spec, identifier_as_value=True).resolve()
40
54
 
41
- def validate(self, resource: ExternalResource) -> None: ...
55
+ def validate(
56
+ self,
57
+ resource: ExternalResource,
58
+ module_conf: ExternalResourceModuleConfiguration,
59
+ ) -> None: ...
42
60
 
43
61
 
44
62
  class AWSElasticacheFactory(AWSDefaultResourceFactory):
@@ -49,7 +67,11 @@ class AWSElasticacheFactory(AWSDefaultResourceFactory):
49
67
  "aws", provisioner, "elasticache", identifier
50
68
  )
51
69
 
52
- def resolve(self, spec: ExternalResourceSpec) -> dict[str, Any]:
70
+ def resolve(
71
+ self,
72
+ spec: ExternalResourceSpec,
73
+ module_conf: ExternalResourceModuleConfiguration,
74
+ ) -> dict[str, Any]:
53
75
  """Resolve the elasticache resource specification and translate some attributes to AWS >= 5.60.0 provider format."""
54
76
  rvr = ResourceValueResolver(spec=spec, identifier_as_value=True)
55
77
  data = rvr.resolve()
@@ -70,7 +92,11 @@ class AWSElasticacheFactory(AWSDefaultResourceFactory):
70
92
 
71
93
  return data
72
94
 
73
- def validate(self, resource: ExternalResource) -> None:
95
+ def validate(
96
+ self,
97
+ resource: ExternalResource,
98
+ module_conf: ExternalResourceModuleConfiguration,
99
+ ) -> None:
74
100
  """Validate the elasticache resource specification."""
75
101
  data = resource.data
76
102
  if data.get("parameter_group"):
@@ -96,6 +122,8 @@ class AWSElasticacheFactory(AWSDefaultResourceFactory):
96
122
 
97
123
 
98
124
  class AWSRdsFactory(AWSDefaultResourceFactory):
125
+ TIMEOUT_RE = re.compile(r"^\d+m$")
126
+
99
127
  def _get_source_db_spec(
100
128
  self, provisioner: str, identifier: str
101
129
  ) -> ExternalResourceSpec:
@@ -110,7 +138,11 @@ class AWSRdsFactory(AWSDefaultResourceFactory):
110
138
  "aws", provisioner, "kms", identifier
111
139
  )
112
140
 
113
- def resolve(self, spec: ExternalResourceSpec) -> dict[str, Any]:
141
+ def resolve(
142
+ self,
143
+ spec: ExternalResourceSpec,
144
+ module_conf: ExternalResourceModuleConfiguration,
145
+ ) -> dict[str, Any]:
114
146
  rvr = ResourceValueResolver(spec=spec, identifier_as_value=True)
115
147
  data = rvr.resolve()
116
148
 
@@ -126,7 +158,7 @@ class AWSRdsFactory(AWSDefaultResourceFactory):
126
158
  sourcedb_spec = self._get_source_db_spec(
127
159
  spec.provisioner_name, data["replica_source"]
128
160
  )
129
- sourcedb = self.resolve(sourcedb_spec)
161
+ sourcedb = self.resolve(sourcedb_spec, module_conf)
130
162
  sourcedb_region = (
131
163
  sourcedb.get("region", None)
132
164
  or sourcedb_spec.provisioner["resources_default_region"]
@@ -142,9 +174,59 @@ class AWSRdsFactory(AWSDefaultResourceFactory):
142
174
  spec.provisioner_name, kms_key_id
143
175
  ).identifier
144
176
 
177
+ # If not timeouts are set, set default timeouts according to the module reconcile timeout configuration
178
+ # 5 minutes are substracted to let terraform finish gracefully before the Job is killed.
179
+ if "timeouts" not in data:
180
+ data["timeouts"] = {
181
+ "create": f"{module_conf.reconcile_timeout_minutes - 5}m",
182
+ "update": f"{module_conf.reconcile_timeout_minutes - 5}m",
183
+ "delete": f"{module_conf.reconcile_timeout_minutes - 5}m",
184
+ }
145
185
  return data
146
186
 
147
- def validate(self, resource: ExternalResource) -> None: ...
187
+ def _get_timeout_minutes(
188
+ self,
189
+ timeout: str,
190
+ ) -> int:
191
+ if not re.match(AWSRdsFactory.TIMEOUT_RE, timeout):
192
+ raise ValueError(
193
+ f"Invalid RDS instance timeout format: {timeout}. Specify timeout in minutes(m)."
194
+ )
195
+ return int(timeout[:-1])
196
+
197
+ def _validate_timeouts(
198
+ self,
199
+ resource: ExternalResource,
200
+ module_conf: ExternalResourceModuleConfiguration,
201
+ ) -> None:
202
+ timeouts = resource.data.get("timeouts")
203
+ if not timeouts:
204
+ return
205
+
206
+ if not isinstance(timeouts, dict):
207
+ raise ValueError(
208
+ "Timeouts must be a dictionary with 'create', 'update' and/or 'delete' keys."
209
+ )
210
+
211
+ allowed_keys = {"create", "update", "delete"}
212
+ if unknown_keys := timeouts.keys() - allowed_keys:
213
+ raise ValueError(
214
+ f"Timeouts must be a dictionary with 'create', 'update' and/or 'delete' keys. Offending keys: {unknown_keys}."
215
+ )
216
+
217
+ for option, timeout in timeouts.items():
218
+ timeout_minutes = self._get_timeout_minutes(timeout)
219
+ if timeout_minutes >= module_conf.reconcile_timeout_minutes:
220
+ raise ValueError(
221
+ f"RDS instance {option} timeout value {timeout_minutes} must be lower than the module reconcile_timeout_minutes value {module_conf.reconcile_timeout_minutes}."
222
+ )
223
+
224
+ def validate(
225
+ self,
226
+ resource: ExternalResource,
227
+ module_conf: ExternalResourceModuleConfiguration,
228
+ ) -> None:
229
+ self._validate_timeouts(resource, module_conf)
148
230
 
149
231
  def find_linked_resources(
150
232
  self, spec: ExternalResourceSpec
@@ -166,7 +248,11 @@ class AWSMskFactory(AWSDefaultResourceFactory):
166
248
  "aws", provisioner, "msk", identifier
167
249
  )
168
250
 
169
- def resolve(self, spec: ExternalResourceSpec) -> dict[str, Any]:
251
+ def resolve(
252
+ self,
253
+ spec: ExternalResourceSpec,
254
+ module_conf: ExternalResourceModuleConfiguration,
255
+ ) -> dict[str, Any]:
170
256
  rvr = ResourceValueResolver(spec=spec, identifier_as_value=True)
171
257
  data = rvr.resolve()
172
258
  data["output_prefix"] = spec.output_prefix
@@ -188,7 +274,11 @@ class AWSMskFactory(AWSDefaultResourceFactory):
188
274
  del data["users"]
189
275
  return data
190
276
 
191
- def validate(self, resource: ExternalResource) -> None:
277
+ def validate(
278
+ self,
279
+ resource: ExternalResource,
280
+ module_conf: ExternalResourceModuleConfiguration,
281
+ ) -> None:
192
282
  data = resource.data
193
283
  if (
194
284
  data["number_of_broker_nodes"]
@@ -15,6 +15,7 @@ from reconcile.external_resources.meta import QONTRACT_INTEGRATION
15
15
  from reconcile.external_resources.model import (
16
16
  ExternalResource,
17
17
  ExternalResourceKey,
18
+ ExternalResourceModuleConfiguration,
18
19
  ExternalResourceProvision,
19
20
  ExternalResourcesInventory,
20
21
  ModuleInventory,
@@ -55,11 +56,19 @@ class ObjectFactory(Generic[T]):
55
56
 
56
57
  class ExternalResourceFactory(ABC):
57
58
  @abstractmethod
58
- def create_external_resource(self, spec: ExternalResourceSpec) -> ExternalResource:
59
+ def create_external_resource(
60
+ self,
61
+ spec: ExternalResourceSpec,
62
+ module_conf: ExternalResourceModuleConfiguration,
63
+ ) -> ExternalResource:
59
64
  pass
60
65
 
61
66
  @abstractmethod
62
- def validate_external_resource(self, resource: ExternalResource) -> None:
67
+ def validate_external_resource(
68
+ self,
69
+ resource: ExternalResource,
70
+ module_conf: ExternalResourceModuleConfiguration,
71
+ ) -> None:
63
72
  pass
64
73
 
65
74
  def find_linked_resources(
@@ -121,9 +130,13 @@ class AWSExternalResourceFactory(ExternalResourceFactory):
121
130
  self.er_inventory = er_inventory
122
131
  self.secret_reader = secret_reader
123
132
 
124
- def create_external_resource(self, spec: ExternalResourceSpec) -> ExternalResource:
133
+ def create_external_resource(
134
+ self,
135
+ spec: ExternalResourceSpec,
136
+ module_conf: ExternalResourceModuleConfiguration,
137
+ ) -> ExternalResource:
125
138
  f = self.resource_factories.get_factory(spec.provider)
126
- data = f.resolve(spec)
139
+ data = f.resolve(spec, module_conf)
127
140
  data["tags"] = spec.tags(integration=QONTRACT_INTEGRATION)
128
141
  data["default_tags"] = AWS_DEFAULT_TAGS
129
142
 
@@ -152,9 +165,13 @@ class AWSExternalResourceFactory(ExternalResourceFactory):
152
165
 
153
166
  return ExternalResource(data=data, provision=provision)
154
167
 
155
- def validate_external_resource(self, resource: ExternalResource) -> None:
168
+ def validate_external_resource(
169
+ self,
170
+ resource: ExternalResource,
171
+ module_conf: ExternalResourceModuleConfiguration,
172
+ ) -> None:
156
173
  f = self.resource_factories.get_factory(resource.provision.provider)
157
- f.validate(resource)
174
+ f.validate(resource, module_conf)
158
175
 
159
176
  def find_linked_resources(
160
177
  self, spec: ExternalResourceSpec
@@ -197,8 +197,11 @@ class ExternalResourcesManager:
197
197
  if spec.marked_to_delete:
198
198
  continue
199
199
  module = self.module_inventory.get_from_spec(spec)
200
+ module_conf = ExternalResourceModuleConfiguration.resolve_configuration(
201
+ module, spec, self.settings
202
+ )
200
203
  try:
201
- resource = self._build_external_resource(spec)
204
+ resource = self._build_external_resource(spec, module_conf)
202
205
  except ExternalResourceValidationError as e:
203
206
  self.errors[key] = e
204
207
  continue
@@ -208,9 +211,7 @@ class ExternalResourcesManager:
208
211
  resource_hash=resource.hash(),
209
212
  input=resource.json(),
210
213
  action=Action.APPLY,
211
- module_configuration=ExternalResourceModuleConfiguration.resolve_configuration(
212
- module, spec, self.settings
213
- ),
214
+ module_configuration=module_conf,
214
215
  linked_resources=self._find_linked_resources(spec),
215
216
  )
216
217
  r.add(reconciliation)
@@ -362,10 +363,14 @@ class ExternalResourcesManager:
362
363
  )
363
364
  self.state_mgr.update_resource_status(key, ResourceStatus.CREATED)
364
365
 
365
- def _build_external_resource(self, spec: ExternalResourceSpec) -> ExternalResource:
366
+ def _build_external_resource(
367
+ self,
368
+ spec: ExternalResourceSpec,
369
+ module_conf: ExternalResourceModuleConfiguration,
370
+ ) -> ExternalResource:
366
371
  f = self.factories.get_factory(spec.provision_provider)
367
- resource = f.create_external_resource(spec)
368
- f.validate_external_resource(resource)
372
+ resource = f.create_external_resource(spec, module_conf)
373
+ f.validate_external_resource(resource, module_conf)
369
374
  return resource
370
375
 
371
376
  def _find_linked_resources(
@@ -118,13 +118,13 @@ class Erv2Cli:
118
118
  self._er_settings, m_inventory, er_inventory, self._secret_reader
119
119
  )
120
120
  f = factories.get_factory(spec.provision_provider)
121
- self._resource = f.create_external_resource(spec)
122
- f.validate_external_resource(self._resource)
123
121
  self._module_configuration = (
124
122
  ExternalResourceModuleConfiguration.resolve_configuration(
125
123
  m_inventory.get_from_spec(spec), spec, self._er_settings
126
124
  )
127
125
  )
126
+ self._resource = f.create_external_resource(spec, self._module_configuration)
127
+ f.validate_external_resource(self._resource, self._module_configuration)
128
128
 
129
129
  @property
130
130
  def input_data(self) -> str: