qontract-reconcile 0.10.1rc888__py3-none-any.whl → 0.10.1rc890__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.1
2
2
  Name: qontract-reconcile
3
- Version: 0.10.1rc888
3
+ Version: 0.10.1rc890
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
@@ -812,7 +812,7 @@ tools/app_interface_metrics_exporter.py,sha256=zkwkxdAUAxjdc-pzx2_oJXG25fo0Fnyd5
812
812
  tools/app_interface_reporter.py,sha256=uy9eRHf6EdvD8ZY2WYdroGXm18DOdnqVZyxaWN3Bm_0,17724
813
813
  tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QEjzdqs,2948
814
814
  tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
815
- tools/qontract_cli.py,sha256=PYFiVIc37qFOl3UxXt04o-V50Leu37pAFndTRMu_WBs,121059
815
+ tools/qontract_cli.py,sha256=lEK5alQqOXQryqdpWwrZPmWUWx3YYL60armyCu4Dguc,123885
816
816
  tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
817
817
  tools/template_validation.py,sha256=qpKYaTgk0GOPGa2Ct5_5sKdwIHtCAKIBGzsMPuJU5fw,3371
818
818
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -833,18 +833,18 @@ tools/saas_metrics_exporter/commit_distance/channel.py,sha256=XEAh3eL8TmgMe7V2Bs
833
833
  tools/saas_metrics_exporter/commit_distance/commit_distance.py,sha256=pUWaZfZf0TYOwAW0gDdU8cYgTR586J_8S_DWxqHMWNA,3116
834
834
  tools/saas_metrics_exporter/commit_distance/metrics.py,sha256=5-y6n-sGACAS3eJ5ndY-2BFxcd0fxLfhvZmmBHu4JuA,426
835
835
  tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
836
- tools/saas_promotion_state/saas_promotion_state.py,sha256=_jP9E8-VcWho6FIOGdcjNN6uvMVhpdXOMHw59qNnEmE,2855
836
+ tools/saas_promotion_state/saas_promotion_state.py,sha256=5LJ9rygZ304vxfsIuRfuxueoYRb72EZNKKITEcJ4Mtk,3908
837
837
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
838
838
  tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
839
839
  tools/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
840
840
  tools/test/conftest.py,sha256=YLtiauk_StNFE-lirLnfG_BpJmlB2NGMZISE9A4zwvk,2421
841
841
  tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvftCWEEf-g1mfXOtgCog-g,1271
842
842
  tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jrss,4941
843
- tools/test/test_saas_promotion_state.py,sha256=48Qe5UA5WTI5NVgL7Nz0TSS77osetcijfHNCNdsHfSI,2726
843
+ tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
844
844
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
845
845
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
846
- qontract_reconcile-0.10.1rc888.dist-info/METADATA,sha256=Jdlu3njSekBAi5WRIsJmrYWirEt8p6xfH9Iz73XPjK8,2273
847
- qontract_reconcile-0.10.1rc888.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
848
- qontract_reconcile-0.10.1rc888.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
849
- qontract_reconcile-0.10.1rc888.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
850
- qontract_reconcile-0.10.1rc888.dist-info/RECORD,,
846
+ qontract_reconcile-0.10.1rc890.dist-info/METADATA,sha256=uW1T4A_Joz3NL5bMmkHBoXkqSD773MmIw8yuIq6cD-E,2273
847
+ qontract_reconcile-0.10.1rc890.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
848
+ qontract_reconcile-0.10.1rc890.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
849
+ qontract_reconcile-0.10.1rc890.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
850
+ qontract_reconcile-0.10.1rc890.dist-info/RECORD,,
tools/qontract_cli.py CHANGED
@@ -56,6 +56,14 @@ from reconcile.cli import (
56
56
  config_file,
57
57
  use_jump_host,
58
58
  )
59
+ from reconcile.external_resources.manager import (
60
+ FLAG_RESOURCE_MANAGED_BY_ERV2,
61
+ setup_factories,
62
+ )
63
+ from reconcile.external_resources.model import (
64
+ ExternalResourcesInventory,
65
+ load_module_inventory,
66
+ )
59
67
  from reconcile.gql_definitions.advanced_upgrade_service.aus_clusters import (
60
68
  query as aus_clusters_query,
61
69
  )
@@ -72,6 +80,11 @@ from reconcile.typed_queries.app_interface_vault_settings import (
72
80
  get_app_interface_vault_settings,
73
81
  )
74
82
  from reconcile.typed_queries.clusters import get_clusters
83
+ from reconcile.typed_queries.external_resources import (
84
+ get_modules,
85
+ get_namespaces,
86
+ get_settings,
87
+ )
75
88
  from reconcile.typed_queries.saas_files import get_saas_files
76
89
  from reconcile.typed_queries.slo_documents import get_slo_documents
77
90
  from reconcile.typed_queries.status_board import get_status_board
@@ -3677,6 +3690,27 @@ def get_promotion_state(channel: str, sha: str):
3677
3690
  print(state)
3678
3691
 
3679
3692
 
3693
+ @root.command()
3694
+ @click.option("--channel", help="the channel that state is part of")
3695
+ @click.option("--sha", help="the commit sha we want state for")
3696
+ @click.option("--publisher-id", help="the publisher id we want state for")
3697
+ @environ(["APP_INTERFACE_STATE_BUCKET"])
3698
+ def mark_promotion_state_successful(channel: str, sha: str, publisher_id: str):
3699
+ from tools.saas_promotion_state.saas_promotion_state import (
3700
+ SaasPromotionState,
3701
+ )
3702
+
3703
+ promotion_state = SaasPromotionState.create(promotion_state=None, saas_files=None)
3704
+ print(f"Current states for {publisher_id=}")
3705
+ print(promotion_state.get(channel=channel, sha=sha).get(publisher_id, None))
3706
+ print()
3707
+ print("Pushing new state ...")
3708
+ promotion_state.set_successful(channel=channel, sha=sha, publisher_uid=publisher_id)
3709
+ print()
3710
+ print(f"New state for {publisher_id=}")
3711
+ print(promotion_state.get(channel=channel, sha=sha).get(publisher_id, None))
3712
+
3713
+
3680
3714
  @root.command()
3681
3715
  @click.option("--change-type-name")
3682
3716
  @click.option("--role-name")
@@ -3784,5 +3818,45 @@ def remove(ctx, sso_client_vault_secret_path: str):
3784
3818
  )
3785
3819
 
3786
3820
 
3821
+ @root.group()
3822
+ @click.pass_context
3823
+ def external_resources(ctx):
3824
+ """External resources commands"""
3825
+
3826
+
3827
+ @external_resources.command()
3828
+ @click.argument("provision-provider", required=True)
3829
+ @click.argument("provisioner", required=True)
3830
+ @click.argument("provider", required=True)
3831
+ @click.argument("identifier", required=True)
3832
+ @click.pass_context
3833
+ def get_input(
3834
+ ctx, provision_provider: str, provisioner: str, provider: str, identifier: str
3835
+ ):
3836
+ """Gets the input data for an external resource asset. Input data is what is used
3837
+ in the Reconciliation Job to manage the resource.
3838
+
3839
+ e.g: qontract-reconcile --config=<config> external-resources get-input aws app-sre-stage rds dashdotdb-stage
3840
+ """
3841
+ namespaces = [ns for ns in get_namespaces() if ns.external_resources]
3842
+ er_inventory = ExternalResourcesInventory(namespaces)
3843
+
3844
+ spec = er_inventory.get_inventory_spec(
3845
+ provision_provider=provision_provider,
3846
+ provisioner=provisioner,
3847
+ provider=provider,
3848
+ identifier=identifier,
3849
+ )
3850
+ vault_settings = get_app_interface_vault_settings()
3851
+ secret_reader = create_secret_reader(use_vault=vault_settings.vault)
3852
+ er_settings = get_settings()[0]
3853
+ m_inventory = load_module_inventory(get_modules())
3854
+ factories = setup_factories(er_settings, m_inventory, er_inventory, secret_reader)
3855
+ f = factories.get_factory(spec.provision_provider)
3856
+ resource = f.create_external_resource(spec)
3857
+ f.validate_external_resource(resource)
3858
+ print(resource.json(exclude={"data": {FLAG_RESOURCE_MANAGED_BY_ERV2}}))
3859
+
3860
+
3787
3861
  if __name__ == "__main__":
3788
3862
  root() # pylint: disable=no-value-for-parameter
@@ -14,6 +14,14 @@ from reconcile.utils.secret_reader import create_secret_reader
14
14
  from reconcile.utils.state import init_state
15
15
 
16
16
 
17
+ class SaasPromotionStateException(Exception):
18
+ pass
19
+
20
+
21
+ class SaasPromotionStateMissingException(Exception):
22
+ pass
23
+
24
+
17
25
  class SaasPromotionState:
18
26
  def __init__(
19
27
  self, promotion_state: PromotionState, saas_files: Iterable[SaasFile]
@@ -54,6 +62,31 @@ class SaasPromotionState:
54
62
  )
55
63
  }
56
64
 
65
+ def set_successful(self, channel: str, sha: str, publisher_uid: str) -> None:
66
+ current_data = self._promotion_state.get_promotion_data(
67
+ sha=sha,
68
+ channel=channel,
69
+ target_uid=publisher_uid,
70
+ use_cache=False,
71
+ pre_check_sha_exists=False,
72
+ )
73
+
74
+ if not current_data:
75
+ raise SaasPromotionStateMissingException(
76
+ f"No promotion state in S3 for given {publisher_uid=} {sha=} {channel=}"
77
+ )
78
+
79
+ if current_data.success:
80
+ raise SaasPromotionStateException(
81
+ f"The current promotion state is already marked successful for given {publisher_uid=} {sha=} {channel=}",
82
+ current_data,
83
+ )
84
+
85
+ current_data.success = True
86
+ self._promotion_state.publish_promotion_data(
87
+ data=current_data, sha=sha, channel=channel, target_uid=publisher_uid
88
+ )
89
+
57
90
  @staticmethod
58
91
  def create(
59
92
  promotion_state: PromotionState | None, saas_files: Iterable[SaasFile] | None
@@ -7,14 +7,18 @@ from unittest.mock import (
7
7
  create_autospec,
8
8
  )
9
9
 
10
+ from pytest import raises
11
+
10
12
  from reconcile.typed_queries.saas_files import SaasFile
11
13
  from reconcile.utils.promotion_state import PromotionData, PromotionState
12
14
  from tools.saas_promotion_state.saas_promotion_state import (
13
15
  SaasPromotionState,
16
+ SaasPromotionStateException,
17
+ SaasPromotionStateMissingException,
14
18
  )
15
19
 
16
20
 
17
- def test_saas_promotion_state(
21
+ def test_get_saas_promotion_state(
18
22
  saas_files_builder: Callable[[Iterable[Mapping]], list[SaasFile]],
19
23
  ) -> None:
20
24
  saas_files = saas_files_builder([
@@ -84,3 +88,100 @@ def test_saas_promotion_state(
84
88
  target_uid="616af45d7fad7f4eea8d52b8b5e8a058cef82ab0",
85
89
  pre_check_sha_exists=False,
86
90
  )
91
+
92
+
93
+ def test_set_saas_promotion_state_success(
94
+ saas_files_builder: Callable[[Iterable[Mapping]], list[SaasFile]],
95
+ ) -> None:
96
+ saas_files = saas_files_builder([{"resourceTemplates": []}])
97
+
98
+ current_data = PromotionData(
99
+ check_in="test1",
100
+ saas_file="test2",
101
+ success=False,
102
+ target_config_hash="test3",
103
+ )
104
+ promotion_state = create_autospec(spec=PromotionState)
105
+ promotion_state.get_promotion_data.return_value = current_data
106
+ saas_promotion_state = SaasPromotionState.create(
107
+ promotion_state=promotion_state, saas_files=saas_files
108
+ )
109
+ saas_promotion_state.set_successful(
110
+ channel="test-channel", sha="test-sha", publisher_uid="test-uid"
111
+ )
112
+
113
+ promotion_state.get_promotion_data.assert_called_once_with(
114
+ sha="test-sha",
115
+ channel="test-channel",
116
+ use_cache=False,
117
+ target_uid="test-uid",
118
+ pre_check_sha_exists=False,
119
+ )
120
+ promotion_state.publish_promotion_data.assert_called_once_with(
121
+ data=PromotionData(
122
+ check_in="test1",
123
+ saas_file="test2",
124
+ success=True,
125
+ target_config_hash="test3",
126
+ ),
127
+ channel="test-channel",
128
+ sha="test-sha",
129
+ target_uid="test-uid",
130
+ )
131
+
132
+
133
+ def test_set_saas_promotion_state_missing(
134
+ saas_files_builder: Callable[[Iterable[Mapping]], list[SaasFile]],
135
+ ) -> None:
136
+ saas_files = saas_files_builder([{"resourceTemplates": []}])
137
+ promotion_state = create_autospec(spec=PromotionState)
138
+ promotion_state.get_promotion_data.return_value = None
139
+ saas_promotion_state = SaasPromotionState.create(
140
+ promotion_state=promotion_state, saas_files=saas_files
141
+ )
142
+
143
+ with raises(SaasPromotionStateMissingException):
144
+ saas_promotion_state.set_successful(
145
+ channel="test-channel", sha="test-sha", publisher_uid="test-uid"
146
+ )
147
+
148
+ promotion_state.get_promotion_data.assert_called_once_with(
149
+ sha="test-sha",
150
+ channel="test-channel",
151
+ use_cache=False,
152
+ target_uid="test-uid",
153
+ pre_check_sha_exists=False,
154
+ )
155
+ promotion_state.publish_promotion_data.assert_not_called()
156
+
157
+
158
+ def test_set_saas_promotion_state_already_successful(
159
+ saas_files_builder: Callable[[Iterable[Mapping]], list[SaasFile]],
160
+ ) -> None:
161
+ saas_files = saas_files_builder([{"resourceTemplates": []}])
162
+
163
+ current_data = PromotionData(
164
+ check_in="test1",
165
+ saas_file="test2",
166
+ success=True,
167
+ target_config_hash="test3",
168
+ )
169
+ promotion_state = create_autospec(spec=PromotionState)
170
+ promotion_state.get_promotion_data.return_value = current_data
171
+ saas_promotion_state = SaasPromotionState.create(
172
+ promotion_state=promotion_state, saas_files=saas_files
173
+ )
174
+
175
+ with raises(SaasPromotionStateException):
176
+ saas_promotion_state.set_successful(
177
+ channel="test-channel", sha="test-sha", publisher_uid="test-uid"
178
+ )
179
+
180
+ promotion_state.get_promotion_data.assert_called_once_with(
181
+ sha="test-sha",
182
+ channel="test-channel",
183
+ use_cache=False,
184
+ target_uid="test-uid",
185
+ pre_check_sha_exists=False,
186
+ )
187
+ promotion_state.publish_promotion_data.assert_not_called()