qontract-reconcile 0.10.1rc885__py3-none-any.whl → 0.10.1rc886__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.1rc885.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc885.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/RECORD +10 -7
- tools/qontract_cli.py +19 -0
- tools/saas_promotion_state/__init__.py +0 -0
- tools/saas_promotion_state/saas_promotion_state.py +72 -0
- tools/test/conftest.py +42 -0
- tools/test/test_saas_promotion_state.py +86 -0
- {qontract_reconcile-0.10.1rc885.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc885.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc885.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc885.dist-info → qontract_reconcile-0.10.1rc886.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.1rc886
|
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.1rc885.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/RECORD
RENAMED
@@ -810,7 +810,7 @@ tools/app_interface_metrics_exporter.py,sha256=zkwkxdAUAxjdc-pzx2_oJXG25fo0Fnyd5
|
|
810
810
|
tools/app_interface_reporter.py,sha256=uy9eRHf6EdvD8ZY2WYdroGXm18DOdnqVZyxaWN3Bm_0,17724
|
811
811
|
tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QEjzdqs,2948
|
812
812
|
tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
|
813
|
-
tools/qontract_cli.py,sha256=
|
813
|
+
tools/qontract_cli.py,sha256=PYFiVIc37qFOl3UxXt04o-V50Leu37pAFndTRMu_WBs,121059
|
814
814
|
tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
|
815
815
|
tools/template_validation.py,sha256=qpKYaTgk0GOPGa2Ct5_5sKdwIHtCAKIBGzsMPuJU5fw,3371
|
816
816
|
tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -830,16 +830,19 @@ tools/saas_metrics_exporter/commit_distance/__init__.py,sha256=47DEQpj8HBSa-_TIm
|
|
830
830
|
tools/saas_metrics_exporter/commit_distance/channel.py,sha256=XEAh3eL8TmgMe7V2BsyxuXYWgvBBVdSJETd6Ec7cI04,2171
|
831
831
|
tools/saas_metrics_exporter/commit_distance/commit_distance.py,sha256=pUWaZfZf0TYOwAW0gDdU8cYgTR586J_8S_DWxqHMWNA,3116
|
832
832
|
tools/saas_metrics_exporter/commit_distance/metrics.py,sha256=5-y6n-sGACAS3eJ5ndY-2BFxcd0fxLfhvZmmBHu4JuA,426
|
833
|
+
tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
834
|
+
tools/saas_promotion_state/saas_promotion_state.py,sha256=_jP9E8-VcWho6FIOGdcjNN6uvMVhpdXOMHw59qNnEmE,2855
|
833
835
|
tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
|
834
836
|
tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
|
835
837
|
tools/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
836
|
-
tools/test/conftest.py,sha256=
|
838
|
+
tools/test/conftest.py,sha256=YLtiauk_StNFE-lirLnfG_BpJmlB2NGMZISE9A4zwvk,2421
|
837
839
|
tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvftCWEEf-g1mfXOtgCog-g,1271
|
838
840
|
tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jrss,4941
|
841
|
+
tools/test/test_saas_promotion_state.py,sha256=48Qe5UA5WTI5NVgL7Nz0TSS77osetcijfHNCNdsHfSI,2726
|
839
842
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
840
843
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
841
|
-
qontract_reconcile-0.10.
|
842
|
-
qontract_reconcile-0.10.
|
843
|
-
qontract_reconcile-0.10.
|
844
|
-
qontract_reconcile-0.10.
|
845
|
-
qontract_reconcile-0.10.
|
844
|
+
qontract_reconcile-0.10.1rc886.dist-info/METADATA,sha256=4g0A5WsMn09DnEGihlWR7RXjtoeEjfpaBn6La8GQvmQ,2273
|
845
|
+
qontract_reconcile-0.10.1rc886.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
846
|
+
qontract_reconcile-0.10.1rc886.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
|
847
|
+
qontract_reconcile-0.10.1rc886.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
848
|
+
qontract_reconcile-0.10.1rc886.dist-info/RECORD,,
|
tools/qontract_cli.py
CHANGED
@@ -3658,6 +3658,25 @@ def gpg_encrypt(
|
|
3658
3658
|
).execute()
|
3659
3659
|
|
3660
3660
|
|
3661
|
+
@root.command()
|
3662
|
+
@click.option("--channel", help="the channel that state is part of")
|
3663
|
+
@click.option("--sha", help="the commit sha we want state for")
|
3664
|
+
@environ(["APP_INTERFACE_STATE_BUCKET"])
|
3665
|
+
def get_promotion_state(channel: str, sha: str):
|
3666
|
+
from tools.saas_promotion_state.saas_promotion_state import (
|
3667
|
+
SaasPromotionState,
|
3668
|
+
)
|
3669
|
+
|
3670
|
+
promotion_state = SaasPromotionState.create(promotion_state=None, saas_files=None)
|
3671
|
+
for publisher_id, state in promotion_state.get(channel=channel, sha=sha).items():
|
3672
|
+
print()
|
3673
|
+
if not state:
|
3674
|
+
print(f"No state found for {publisher_id=}")
|
3675
|
+
else:
|
3676
|
+
print(f"State for {publisher_id=}:")
|
3677
|
+
print(state)
|
3678
|
+
|
3679
|
+
|
3661
3680
|
@root.command()
|
3662
3681
|
@click.option("--change-type-name")
|
3663
3682
|
@click.option("--role-name")
|
File without changes
|
@@ -0,0 +1,72 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from collections.abc import Iterable
|
4
|
+
|
5
|
+
from reconcile.openshift_saas_deploy import (
|
6
|
+
QONTRACT_INTEGRATION as OPENSHIFT_SAAS_DEPLOY,
|
7
|
+
)
|
8
|
+
from reconcile.typed_queries.app_interface_vault_settings import (
|
9
|
+
get_app_interface_vault_settings,
|
10
|
+
)
|
11
|
+
from reconcile.typed_queries.saas_files import SaasFile, get_saas_files
|
12
|
+
from reconcile.utils.promotion_state import PromotionData, PromotionState
|
13
|
+
from reconcile.utils.secret_reader import create_secret_reader
|
14
|
+
from reconcile.utils.state import init_state
|
15
|
+
|
16
|
+
|
17
|
+
class SaasPromotionState:
|
18
|
+
def __init__(
|
19
|
+
self, promotion_state: PromotionState, saas_files: Iterable[SaasFile]
|
20
|
+
) -> None:
|
21
|
+
self._promotion_state = promotion_state
|
22
|
+
self._saas_files = saas_files
|
23
|
+
|
24
|
+
def _publisher_ids_for_channel(
|
25
|
+
self, channel: str, saas_files: Iterable[SaasFile]
|
26
|
+
) -> list[str]:
|
27
|
+
publisher_uids: list[str] = []
|
28
|
+
for saas_file in saas_files:
|
29
|
+
for resource_template in saas_file.resource_templates:
|
30
|
+
for target in resource_template.targets:
|
31
|
+
if not target.promotion:
|
32
|
+
continue
|
33
|
+
for publish_channel in target.promotion.publish or []:
|
34
|
+
if publish_channel == channel:
|
35
|
+
publisher_uids.append(
|
36
|
+
target.uid(
|
37
|
+
parent_saas_file_name=saas_file.name,
|
38
|
+
parent_resource_template_name=resource_template.name,
|
39
|
+
)
|
40
|
+
)
|
41
|
+
return publisher_uids
|
42
|
+
|
43
|
+
def get(self, channel: str, sha: str) -> dict[str, PromotionData | None]:
|
44
|
+
return {
|
45
|
+
publisher_id: self._promotion_state.get_promotion_data(
|
46
|
+
sha=sha,
|
47
|
+
channel=channel,
|
48
|
+
use_cache=False,
|
49
|
+
target_uid=publisher_id,
|
50
|
+
pre_check_sha_exists=False,
|
51
|
+
)
|
52
|
+
for publisher_id in self._publisher_ids_for_channel(
|
53
|
+
channel=channel, saas_files=self._saas_files
|
54
|
+
)
|
55
|
+
}
|
56
|
+
|
57
|
+
@staticmethod
|
58
|
+
def create(
|
59
|
+
promotion_state: PromotionState | None, saas_files: Iterable[SaasFile] | None
|
60
|
+
) -> SaasPromotionState:
|
61
|
+
if not promotion_state:
|
62
|
+
vault_settings = get_app_interface_vault_settings()
|
63
|
+
secret_reader = create_secret_reader(use_vault=vault_settings.vault)
|
64
|
+
saas_deploy_state = init_state(
|
65
|
+
integration=OPENSHIFT_SAAS_DEPLOY, secret_reader=secret_reader
|
66
|
+
)
|
67
|
+
promotion_state = PromotionState(state=saas_deploy_state)
|
68
|
+
if not saas_files:
|
69
|
+
saas_files = get_saas_files()
|
70
|
+
return SaasPromotionState(
|
71
|
+
promotion_state=promotion_state, saas_files=saas_files
|
72
|
+
)
|
tools/test/conftest.py
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
from collections.abc import (
|
2
2
|
Callable,
|
3
|
+
Iterable,
|
4
|
+
Mapping,
|
3
5
|
MutableMapping,
|
4
6
|
)
|
7
|
+
from pathlib import Path
|
5
8
|
from typing import Any
|
6
9
|
|
7
10
|
import pytest
|
8
11
|
from pydantic import BaseModel
|
9
12
|
from pydantic.error_wrappers import ValidationError
|
10
13
|
|
14
|
+
from reconcile.typed_queries.saas_files import SaasFile
|
11
15
|
from reconcile.utils.models import data_default_none
|
12
16
|
|
13
17
|
|
@@ -15,6 +19,44 @@ class GQLClassFactoryError(Exception):
|
|
15
19
|
pass
|
16
20
|
|
17
21
|
|
22
|
+
@pytest.fixture
|
23
|
+
def saas_files_builder(
|
24
|
+
gql_class_factory: Callable[[type[SaasFile], Mapping], SaasFile],
|
25
|
+
) -> Callable[[Iterable[MutableMapping]], list[SaasFile]]:
|
26
|
+
def builder(data: Iterable[MutableMapping]) -> list[SaasFile]:
|
27
|
+
for d in data:
|
28
|
+
if "app" not in d:
|
29
|
+
d["app"] = {}
|
30
|
+
if "pipelinesProvider" not in d:
|
31
|
+
d["pipelinesProvider"] = {}
|
32
|
+
if "managedResourceTypes" not in d:
|
33
|
+
d["managedResourceTypes"] = []
|
34
|
+
if "imagePatterns" not in d:
|
35
|
+
d["imagePatterns"] = []
|
36
|
+
for rt in d.get("resourceTemplates", []):
|
37
|
+
for t in rt.get("targets", []):
|
38
|
+
ns = t["namespace"]
|
39
|
+
if "name" not in ns:
|
40
|
+
ns["name"] = "some_name"
|
41
|
+
if "environment" not in ns:
|
42
|
+
ns["environment"] = {}
|
43
|
+
if "app" not in ns:
|
44
|
+
ns["app"] = {}
|
45
|
+
if "cluster" not in ns:
|
46
|
+
ns["cluster"] = {}
|
47
|
+
return [gql_class_factory(SaasFile, d) for d in data]
|
48
|
+
|
49
|
+
return builder
|
50
|
+
|
51
|
+
|
52
|
+
@pytest.fixture
|
53
|
+
def fx() -> Callable:
|
54
|
+
def _fx(name: str) -> str:
|
55
|
+
return (Path(__file__).parent / "fixtures" / name).read_text()
|
56
|
+
|
57
|
+
return _fx
|
58
|
+
|
59
|
+
|
18
60
|
@pytest.fixture
|
19
61
|
def gql_class_factory() -> (
|
20
62
|
Callable[
|
@@ -0,0 +1,86 @@
|
|
1
|
+
from collections.abc import (
|
2
|
+
Callable,
|
3
|
+
Iterable,
|
4
|
+
Mapping,
|
5
|
+
)
|
6
|
+
from unittest.mock import (
|
7
|
+
create_autospec,
|
8
|
+
)
|
9
|
+
|
10
|
+
from reconcile.typed_queries.saas_files import SaasFile
|
11
|
+
from reconcile.utils.promotion_state import PromotionData, PromotionState
|
12
|
+
from tools.saas_promotion_state.saas_promotion_state import (
|
13
|
+
SaasPromotionState,
|
14
|
+
)
|
15
|
+
|
16
|
+
|
17
|
+
def test_saas_promotion_state(
|
18
|
+
saas_files_builder: Callable[[Iterable[Mapping]], list[SaasFile]],
|
19
|
+
) -> None:
|
20
|
+
saas_files = saas_files_builder([
|
21
|
+
{
|
22
|
+
"path": "/saas1.yml",
|
23
|
+
"name": "saas_1",
|
24
|
+
"resourceTemplates": [
|
25
|
+
{
|
26
|
+
"name": "template_1",
|
27
|
+
"url": "repo1/url",
|
28
|
+
"targets": [
|
29
|
+
{
|
30
|
+
"ref": "main",
|
31
|
+
"namespace": {"path": "/namespace1.yml"},
|
32
|
+
"promotion": {
|
33
|
+
"publish": ["channel-a"],
|
34
|
+
},
|
35
|
+
}
|
36
|
+
],
|
37
|
+
}
|
38
|
+
],
|
39
|
+
},
|
40
|
+
{
|
41
|
+
"path": "/saas2.yml",
|
42
|
+
"name": "saas_2",
|
43
|
+
"resourceTemplates": [
|
44
|
+
{
|
45
|
+
"name": "template_2",
|
46
|
+
"url": "repo2/url",
|
47
|
+
"targets": [
|
48
|
+
{
|
49
|
+
"ref": "main",
|
50
|
+
"namespace": {"path": "/namespace2.yml"},
|
51
|
+
"promotion": {
|
52
|
+
"publish": ["channel-b"],
|
53
|
+
"subscribe": ["channel-a"],
|
54
|
+
},
|
55
|
+
},
|
56
|
+
{
|
57
|
+
"ref": "main",
|
58
|
+
"namespace": {"path": "/namespace3.yml"},
|
59
|
+
},
|
60
|
+
],
|
61
|
+
}
|
62
|
+
],
|
63
|
+
},
|
64
|
+
])
|
65
|
+
|
66
|
+
expected = PromotionData(
|
67
|
+
check_in="test1",
|
68
|
+
saas_file="test2",
|
69
|
+
success=True,
|
70
|
+
target_config_hash="test3",
|
71
|
+
)
|
72
|
+
promotion_state = create_autospec(spec=PromotionState)
|
73
|
+
promotion_state.get_promotion_data.return_value = expected
|
74
|
+
saas_promotion_state = SaasPromotionState.create(
|
75
|
+
promotion_state=promotion_state, saas_files=saas_files
|
76
|
+
)
|
77
|
+
result = saas_promotion_state.get(channel="channel-a", sha="main")
|
78
|
+
|
79
|
+
assert result == {"616af45d7fad7f4eea8d52b8b5e8a058cef82ab0": expected}
|
80
|
+
promotion_state.get_promotion_data.assert_called_once_with(
|
81
|
+
sha="main",
|
82
|
+
channel="channel-a",
|
83
|
+
use_cache=False,
|
84
|
+
target_uid="616af45d7fad7f4eea8d52b8b5e8a058cef82ab0",
|
85
|
+
pre_check_sha_exists=False,
|
86
|
+
)
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc885.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/top_level.txt
RENAMED
File without changes
|