qontract-reconcile 0.10.1rc877__py3-none-any.whl → 0.10.1rc879__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.1rc877.dist-info → qontract_reconcile-0.10.1rc879.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc877.dist-info → qontract_reconcile-0.10.1rc879.dist-info}/RECORD +12 -12
- reconcile/gql_definitions/common/saas_files.py +17 -0
- reconcile/gql_definitions/common/saas_target_namespaces.py +5 -0
- reconcile/gql_definitions/fragments/saas_target_namespace.py +7 -0
- reconcile/test/test_saasherder.py +62 -0
- reconcile/utils/saasherder/interfaces.py +9 -0
- reconcile/utils/saasherder/models.py +1 -0
- reconcile/utils/saasherder/saasherder.py +27 -0
- {qontract_reconcile-0.10.1rc877.dist-info → qontract_reconcile-0.10.1rc879.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc877.dist-info → qontract_reconcile-0.10.1rc879.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc877.dist-info → qontract_reconcile-0.10.1rc879.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc877.dist-info → qontract_reconcile-0.10.1rc879.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.1rc879
|
4
4
|
Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
|
5
5
|
Home-page: https://github.com/app-sre/qontract-reconcile
|
6
6
|
Author: Red Hat App-SRE Team
|
{qontract_reconcile-0.10.1rc877.dist-info → qontract_reconcile-0.10.1rc879.dist-info}/RECORD
RENAMED
@@ -258,8 +258,8 @@ reconcile/gql_definitions/common/pgp_reencryption_settings.py,sha256=NPLmO6J-zSu
|
|
258
258
|
reconcile/gql_definitions/common/pipeline_providers.py,sha256=JJgmmghqLIwjKOdcWYHPnf4PDgAq4GF7046i0ozrqgI,9127
|
259
259
|
reconcile/gql_definitions/common/quay_instances.py,sha256=toBkdYYVTmEafezAHZKgaW-mQ29xEW6jeronzsAlNyI,1786
|
260
260
|
reconcile/gql_definitions/common/reserved_networks.py,sha256=yP9qSQCaSQcva-ZgTnZp09qH27ur5_qK080ToIs04MY,2560
|
261
|
-
reconcile/gql_definitions/common/saas_files.py,sha256=
|
262
|
-
reconcile/gql_definitions/common/saas_target_namespaces.py,sha256=
|
261
|
+
reconcile/gql_definitions/common/saas_files.py,sha256=8JoysSi-lAjsdGKTK3YBTBPYy95B47guXzfp71JHh1I,16516
|
262
|
+
reconcile/gql_definitions/common/saas_target_namespaces.py,sha256=4VYP2VbwY8WVwtSFk2-jsUNhSmRD3X4FWKxetOKvmd0,2835
|
263
263
|
reconcile/gql_definitions/common/saasherder_settings.py,sha256=nqQLcMwYxLseqq0BEcVvmrpIj2eQq0h8XDSpLN6GGCw,1793
|
264
264
|
reconcile/gql_definitions/common/slack_workspaces.py,sha256=2o0kgi4QiaRuNmZJnc_By4F6NsKIdRaXkrufRQw7Nok,1753
|
265
265
|
reconcile/gql_definitions/common/smtp_client_settings.py,sha256=JU6t6D-Qj-z1gLlgUiHKe0W7AxWQdty9jlv-ig_43tM,2248
|
@@ -299,7 +299,7 @@ reconcile/gql_definitions/fragments/prometheus_instance.py,sha256=12ltnV9kdEw6Ln
|
|
299
299
|
reconcile/gql_definitions/fragments/resource_limits_requirements.py,sha256=ucskQ_a8RxvFl5-IWxz5kk3g4-5Pvh_W4N3nLmuKxi0,744
|
300
300
|
reconcile/gql_definitions/fragments/resource_requests_requirements.py,sha256=TFKO4YALFPanSvZvIJFz0dCioBU7i73Q6hkDtGMvs9I,736
|
301
301
|
reconcile/gql_definitions/fragments/resource_values.py,sha256=-N2lNRhWp8PgocmIeX3U9f3l90Q97N2lXoq1pXdb_LE,742
|
302
|
-
reconcile/gql_definitions/fragments/saas_target_namespace.py,sha256=
|
302
|
+
reconcile/gql_definitions/fragments/saas_target_namespace.py,sha256=8kMXeD7u2bmdjn10zHmMJ80ScOhUp6KqSfWfjWZW-40,4001
|
303
303
|
reconcile/gql_definitions/fragments/terraform_state.py,sha256=S5QuTR9YlvUObiU7hevS9ybxZEssWoRGqCR9YtGwePs,1024
|
304
304
|
reconcile/gql_definitions/fragments/upgrade_policy.py,sha256=cVza8zfra1E3yBsHiS-hKbys17fvv572GFnKshJjluE,1246
|
305
305
|
reconcile/gql_definitions/fragments/user.py,sha256=84RGYYSYnZmyrwHlCX89-EgAu7UaLFOTMQXobmHCfz8,939
|
@@ -535,7 +535,7 @@ reconcile/test/test_quay_repos.py,sha256=TdkcRF_a8PLp01Kti9eZZN-vGup2yPBT4Iba3k0
|
|
535
535
|
reconcile/test/test_queries.py,sha256=SpH3RmNpBjEr_ne3VjAMCgKK8RE1z1zo7bypkT5uoO4,1946
|
536
536
|
reconcile/test/test_repo_owners.py,sha256=uRYMLbMmh-9usF0TerabZTZV-Z1CS4I6ybT-LQqCLe8,1423
|
537
537
|
reconcile/test/test_requests_sender.py,sha256=7fd9C2kEFS0-CYtlsif66N1kO9c44pzuBPAJKR9igqU,5385
|
538
|
-
reconcile/test/test_saasherder.py,sha256=
|
538
|
+
reconcile/test/test_saasherder.py,sha256=C2vwkdjk2cccS14GML2ByG8WKcRP8wgTJfzwaEUKdtw,54603
|
539
539
|
reconcile/test/test_saasherder_allowed_secret_paths.py,sha256=5NHQwNJO66at6HiyMZ5sVRTQDwxdvlOQo0KmkBWCw5Q,4853
|
540
540
|
reconcile/test/test_secret_reader.py,sha256=kz7nzcPjvA08cytnvcA_PMA98AEyqJWsESkYeRn5xCk,4994
|
541
541
|
reconcile/test/test_slack_base.py,sha256=gpbWOLNxMMX6fyAbs1JakhLTnwfedb3f7WpUae4tQZE,5060
|
@@ -787,9 +787,9 @@ reconcile/utils/runtime/meta.py,sha256=X44HzyXIBprf3zcsGr2XLCgoeFkz6r3U2nlFXM1H7
|
|
787
787
|
reconcile/utils/runtime/runner.py,sha256=72cc-I6yXyPov8UCLHpyERRy1eiMLpGite2roO0yUlo,7979
|
788
788
|
reconcile/utils/runtime/sharding.py,sha256=roCdbnBklhTK_g34zbgQYqzpKPaNQ8J6Xd9XLO9-t6Q,16258
|
789
789
|
reconcile/utils/saasherder/__init__.py,sha256=J3MBZBFa5YmhqYm08QsjBXz8mFcVOCiOCkyIcw41t7E,343
|
790
|
-
reconcile/utils/saasherder/interfaces.py,sha256=
|
791
|
-
reconcile/utils/saasherder/models.py,sha256=
|
792
|
-
reconcile/utils/saasherder/saasherder.py,sha256=
|
790
|
+
reconcile/utils/saasherder/interfaces.py,sha256=UG3O5zuamNrP1bgbfhQAtNfN_8W9_iSv7UBESnR-mXE,9363
|
791
|
+
reconcile/utils/saasherder/models.py,sha256=iLQZGXMx4FYnK62MJKkVp1T32BpsZ88NOcU7RW0WyAk,5579
|
792
|
+
reconcile/utils/saasherder/saasherder.py,sha256=boCEscTR7Vu6Zjjd7pJIQDO5B_gYiOljwgBa_KDUVLM,88141
|
793
793
|
reconcile/utils/terraform/__init__.py,sha256=zNbiyTWo35AT1sFTElL2j_AA0jJ_yWE_bfFn-nD2xik,250
|
794
794
|
reconcile/utils/terraform/config.py,sha256=5UVrd563TMcvi4ooa5JvWVDW1I3bIWg484u79evfV_8,164
|
795
795
|
reconcile/utils/terraform/config_client.py,sha256=py-Ree-QUYD6Hvng6bM40VgSuttteehIKNgwOSoJO1o,4706
|
@@ -837,8 +837,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
|
|
837
837
|
tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jrss,4941
|
838
838
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
839
839
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
840
|
-
qontract_reconcile-0.10.
|
841
|
-
qontract_reconcile-0.10.
|
842
|
-
qontract_reconcile-0.10.
|
843
|
-
qontract_reconcile-0.10.
|
844
|
-
qontract_reconcile-0.10.
|
840
|
+
qontract_reconcile-0.10.1rc879.dist-info/METADATA,sha256=4h4jN56Q5GugfmWXxRkczGqc0xzefD2xu-AnO3BlsBc,2273
|
841
|
+
qontract_reconcile-0.10.1rc879.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
842
|
+
qontract_reconcile-0.10.1rc879.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
|
843
|
+
qontract_reconcile-0.10.1rc879.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
844
|
+
qontract_reconcile-0.10.1rc879.dist-info/RECORD,,
|
@@ -82,6 +82,11 @@ fragment SaasTargetNamespace on Namespace_v1 {
|
|
82
82
|
name
|
83
83
|
email
|
84
84
|
}
|
85
|
+
codeComponents {
|
86
|
+
url
|
87
|
+
blockedVersions
|
88
|
+
hotfixVersions
|
89
|
+
}
|
85
90
|
}
|
86
91
|
cluster {
|
87
92
|
name
|
@@ -137,6 +142,11 @@ query SaasFiles {
|
|
137
142
|
name
|
138
143
|
email
|
139
144
|
}
|
145
|
+
codeComponents {
|
146
|
+
url
|
147
|
+
blockedVersions
|
148
|
+
hotfixVersions
|
149
|
+
}
|
140
150
|
}
|
141
151
|
pipelinesProvider {
|
142
152
|
name
|
@@ -320,11 +330,18 @@ class OwnerV1(ConfiguredBaseModel):
|
|
320
330
|
email: str = Field(..., alias="email")
|
321
331
|
|
322
332
|
|
333
|
+
class AppCodeComponentsV1(ConfiguredBaseModel):
|
334
|
+
url: str = Field(..., alias="url")
|
335
|
+
blocked_versions: Optional[list[str]] = Field(..., alias="blockedVersions")
|
336
|
+
hotfix_versions: Optional[list[str]] = Field(..., alias="hotfixVersions")
|
337
|
+
|
338
|
+
|
323
339
|
class AppV1(ConfiguredBaseModel):
|
324
340
|
name: str = Field(..., alias="name")
|
325
341
|
parent_app: Optional[AppV1_AppV1] = Field(..., alias="parentApp")
|
326
342
|
self_service_roles: Optional[list[RoleV1]] = Field(..., alias="selfServiceRoles")
|
327
343
|
service_owners: Optional[list[OwnerV1]] = Field(..., alias="serviceOwners")
|
344
|
+
code_components: Optional[list[AppCodeComponentsV1]] = Field(..., alias="codeComponents")
|
328
345
|
|
329
346
|
|
330
347
|
class PipelinesProviderV1(ConfiguredBaseModel):
|
@@ -52,12 +52,19 @@ class OwnerV1(ConfiguredBaseModel):
|
|
52
52
|
email: str = Field(..., alias="email")
|
53
53
|
|
54
54
|
|
55
|
+
class AppCodeComponentsV1(ConfiguredBaseModel):
|
56
|
+
url: str = Field(..., alias="url")
|
57
|
+
blocked_versions: Optional[list[str]] = Field(..., alias="blockedVersions")
|
58
|
+
hotfix_versions: Optional[list[str]] = Field(..., alias="hotfixVersions")
|
59
|
+
|
60
|
+
|
55
61
|
class AppV1(ConfiguredBaseModel):
|
56
62
|
name: str = Field(..., alias="name")
|
57
63
|
parent_app: Optional[AppV1_AppV1] = Field(..., alias="parentApp")
|
58
64
|
labels: Optional[Json] = Field(..., alias="labels")
|
59
65
|
self_service_roles: Optional[list[RoleV1]] = Field(..., alias="selfServiceRoles")
|
60
66
|
service_owners: Optional[list[OwnerV1]] = Field(..., alias="serviceOwners")
|
67
|
+
code_components: Optional[list[AppCodeComponentsV1]] = Field(..., alias="codeComponents")
|
61
68
|
|
62
69
|
|
63
70
|
class DisableClusterAutomationsV1(ConfiguredBaseModel):
|
@@ -33,9 +33,12 @@ from reconcile.typed_queries.saas_files import (
|
|
33
33
|
)
|
34
34
|
from reconcile.utils.jjb_client import JJB
|
35
35
|
from reconcile.utils.openshift_resource import ResourceInventory
|
36
|
+
from reconcile.utils.promotion_state import PromotionData
|
36
37
|
from reconcile.utils.saasherder import SaasHerder
|
37
38
|
from reconcile.utils.saasherder.interfaces import SaasFile as SaasFileInterface
|
38
39
|
from reconcile.utils.saasherder.models import (
|
40
|
+
Channel,
|
41
|
+
Promotion,
|
39
42
|
TriggerSpecContainerImage,
|
40
43
|
TriggerSpecMovingCommit,
|
41
44
|
TriggerSpecUpstreamJob,
|
@@ -1350,6 +1353,65 @@ class TestRemoveNoneAttributes(TestCase):
|
|
1350
1353
|
self.assertEqual(res, expected)
|
1351
1354
|
|
1352
1355
|
|
1356
|
+
@pytest.mark.usefixtures("inject_gql_class_factory")
|
1357
|
+
class TestPromotionBlockedHoxfixVersions(TestCase):
|
1358
|
+
def setUp(self) -> None:
|
1359
|
+
self.saas_file = self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
1360
|
+
SaasFile,
|
1361
|
+
Fixtures("saasherder").get_anymarkup("saas.gql.yml"),
|
1362
|
+
)
|
1363
|
+
self.state_patcher = patch("reconcile.utils.state.State", autospec=True)
|
1364
|
+
self.state_mock = self.state_patcher.start().return_value
|
1365
|
+
self.saasherder = SaasHerder(
|
1366
|
+
[self.saas_file],
|
1367
|
+
secret_reader=MockSecretReader(),
|
1368
|
+
thread_pool_size=1,
|
1369
|
+
state=self.state_mock,
|
1370
|
+
integration="",
|
1371
|
+
integration_version="",
|
1372
|
+
hash_length=7,
|
1373
|
+
repo_url="https://repo-url.com",
|
1374
|
+
)
|
1375
|
+
self.promotion_state_patcher = patch(
|
1376
|
+
"reconcile.utils.promotion_state.PromotionState", autospec=True
|
1377
|
+
)
|
1378
|
+
self.promotion_state_mock = self.promotion_state_patcher.start().return_value
|
1379
|
+
self.saasherder._promotion_state = self.promotion_state_mock
|
1380
|
+
|
1381
|
+
def tearDown(self) -> None:
|
1382
|
+
self.state_patcher.stop()
|
1383
|
+
self.promotion_state_patcher.stop()
|
1384
|
+
|
1385
|
+
def test_blocked_hotfix_version_promotion_validity(self) -> None:
|
1386
|
+
code_component_url = "https://github.com/app-sre/test-saas-deployments"
|
1387
|
+
hotfix_version = "1234567890123456789012345678901234567890"
|
1388
|
+
# code_component = self.saas_file.app.code_components[0]
|
1389
|
+
channel = Channel(
|
1390
|
+
name="",
|
1391
|
+
publisher_uids=[""],
|
1392
|
+
)
|
1393
|
+
promotion = Promotion(
|
1394
|
+
url=code_component_url,
|
1395
|
+
commit_sha=hotfix_version,
|
1396
|
+
saas_file=self.saas_file.name,
|
1397
|
+
target_config_hash="",
|
1398
|
+
saas_target_uid="",
|
1399
|
+
soak_days=0,
|
1400
|
+
subscribe=[channel],
|
1401
|
+
)
|
1402
|
+
self.saasherder.promotions = [promotion]
|
1403
|
+
self.promotion_state_mock.get_promotion_data.return_value = PromotionData(
|
1404
|
+
success=False
|
1405
|
+
)
|
1406
|
+
self.assertFalse(self.saasherder.validate_promotions())
|
1407
|
+
|
1408
|
+
self.saasherder.hotfix_versions[code_component_url] = {hotfix_version}
|
1409
|
+
self.assertTrue(self.saasherder.validate_promotions())
|
1410
|
+
|
1411
|
+
self.saasherder.blocked_versions[code_component_url] = {hotfix_version}
|
1412
|
+
self.assertFalse(self.saasherder.validate_promotions())
|
1413
|
+
|
1414
|
+
|
1353
1415
|
def test_render_templated_parameters(
|
1354
1416
|
gql_class_factory: Callable[..., SaasFileInterface],
|
1355
1417
|
) -> None:
|
@@ -63,6 +63,9 @@ class SaasApp(Protocol):
|
|
63
63
|
@property
|
64
64
|
def service_owners(self) -> Optional[Sequence[SaasServiceOwner]]: ...
|
65
65
|
|
66
|
+
@property
|
67
|
+
def code_components(self) -> Optional[Sequence[AppCodeComponent]]: ...
|
68
|
+
|
66
69
|
|
67
70
|
class SaasPipelinesProvider(Protocol):
|
68
71
|
name: str
|
@@ -356,6 +359,12 @@ class SaasServiceOwner(Protocol):
|
|
356
359
|
email: str
|
357
360
|
|
358
361
|
|
362
|
+
class AppCodeComponent(Protocol):
|
363
|
+
url: str
|
364
|
+
blocked_versions: Optional[list[str]]
|
365
|
+
hotfix_versions: Optional[list[str]]
|
366
|
+
|
367
|
+
|
359
368
|
SaasPipelinesProviders = Union[SaasPipelinesProviderTekton, SaasPipelinesProvider]
|
360
369
|
|
361
370
|
|
@@ -150,6 +150,8 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
150
150
|
self._promotion_state = PromotionState(state=state) if state else None
|
151
151
|
self._channel_map = self._assemble_channels(saas_files=all_saas_files)
|
152
152
|
self.images: set[str] = set()
|
153
|
+
self.blocked_versions = self._collect_blocked_versions()
|
154
|
+
self.hotfix_versions = self._collect_hotfix_versions()
|
153
155
|
|
154
156
|
# each namespace is in fact a target,
|
155
157
|
# so we can use it to calculate.
|
@@ -1057,6 +1059,7 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1057
1059
|
self._channel_map[sub] for sub in target.promotion.subscribe or []
|
1058
1060
|
]
|
1059
1061
|
target_promotion = Promotion(
|
1062
|
+
url=url,
|
1060
1063
|
auto=target.promotion.auto,
|
1061
1064
|
publish=target.promotion.publish,
|
1062
1065
|
subscribe=channels,
|
@@ -1098,6 +1101,22 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1098
1101
|
channel_map[publish].publisher_uids.append(publisher_uid)
|
1099
1102
|
return channel_map
|
1100
1103
|
|
1104
|
+
def _collect_blocked_versions(self) -> dict[str, set[str]]:
|
1105
|
+
blocked_versions: dict[str, set[str]] = {}
|
1106
|
+
for saas_file in self.saas_files:
|
1107
|
+
for cc in saas_file.app.code_components or []:
|
1108
|
+
for v in cc.blocked_versions or []:
|
1109
|
+
blocked_versions.setdefault(cc.url, set()).add(v)
|
1110
|
+
return blocked_versions
|
1111
|
+
|
1112
|
+
def _collect_hotfix_versions(self) -> dict[str, set[str]]:
|
1113
|
+
hotfix_versions: dict[str, set[str]] = {}
|
1114
|
+
for saas_file in self.saas_files:
|
1115
|
+
for cc in saas_file.app.code_components or []:
|
1116
|
+
for v in cc.hotfix_versions or []:
|
1117
|
+
hotfix_versions.setdefault(cc.url, set()).add(v)
|
1118
|
+
return hotfix_versions
|
1119
|
+
|
1101
1120
|
@staticmethod
|
1102
1121
|
def _collect_images(resource: Resource) -> set[str]:
|
1103
1122
|
images = set()
|
@@ -1889,6 +1908,14 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1889
1908
|
if not promotion.subscribe:
|
1890
1909
|
return True
|
1891
1910
|
|
1911
|
+
if promotion.commit_sha in self.blocked_versions.get(promotion.url, set()):
|
1912
|
+
logging.error(f"Commit {promotion.commit_sha} is blocked!")
|
1913
|
+
return False
|
1914
|
+
|
1915
|
+
# hotfix must run before further gates are evaluated to override them
|
1916
|
+
if promotion.commit_sha in self.hotfix_versions.get(promotion.url, set()):
|
1917
|
+
return True
|
1918
|
+
|
1892
1919
|
now = datetime.now(timezone.utc)
|
1893
1920
|
passed_soak_days = timedelta(days=0)
|
1894
1921
|
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc877.dist-info → qontract_reconcile-0.10.1rc879.dist-info}/top_level.txt
RENAMED
File without changes
|