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.
- {qontract_reconcile-0.10.2.dev44.dist-info → qontract_reconcile-0.10.2.dev45.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev44.dist-info → qontract_reconcile-0.10.2.dev45.dist-info}/RECORD +8 -8
- reconcile/external_resources/aws.py +101 -11
- reconcile/external_resources/factories.py +23 -6
- reconcile/external_resources/manager.py +12 -7
- tools/cli_commands/erv2.py +2 -2
- {qontract_reconcile-0.10.2.dev44.dist-info → qontract_reconcile-0.10.2.dev45.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev44.dist-info → qontract_reconcile-0.10.2.dev45.dist-info}/entry_points.txt +0 -0
{qontract_reconcile-0.10.2.dev44.dist-info → qontract_reconcile-0.10.2.dev45.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.2.
|
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
|
{qontract_reconcile-0.10.2.dev44.dist-info → qontract_reconcile-0.10.2.dev45.dist-info}/RECORD
RENAMED
@@ -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=
|
199
|
-
reconcile/external_resources/factories.py,sha256=
|
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=
|
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=
|
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.
|
777
|
-
qontract_reconcile-0.10.2.
|
778
|
-
qontract_reconcile-0.10.2.
|
779
|
-
qontract_reconcile-0.10.2.
|
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(
|
26
|
+
def resolve(
|
27
|
+
self,
|
28
|
+
spec: ExternalResourceSpec,
|
29
|
+
module_conf: ExternalResourceModuleConfiguration,
|
30
|
+
) -> dict[str, Any]: ...
|
25
31
|
|
26
32
|
@abstractmethod
|
27
|
-
def validate(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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
|
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(
|
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(
|
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(
|
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(
|
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(
|
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(
|
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=
|
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(
|
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(
|
tools/cli_commands/erv2.py
CHANGED
@@ -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:
|
{qontract_reconcile-0.10.2.dev44.dist-info → qontract_reconcile-0.10.2.dev45.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|