qontract-reconcile 0.10.1rc884__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.
Files changed (279) hide show
  1. {qontract_reconcile-0.10.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/METADATA +1 -1
  2. {qontract_reconcile-0.10.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/RECORD +279 -276
  3. reconcile/acs_rbac.py +1 -2
  4. reconcile/aus/advanced_upgrade_service.py +14 -14
  5. reconcile/aus/aus_label_source.py +1 -2
  6. reconcile/aus/base.py +23 -26
  7. reconcile/aus/cluster_version_data.py +4 -4
  8. reconcile/aus/models.py +2 -3
  9. reconcile/aus/version_gate_approver.py +2 -6
  10. reconcile/aus/version_gates/__init__.py +1 -3
  11. reconcile/aus/version_gates/sts_version_gate_handler.py +2 -3
  12. reconcile/aws_account_manager/integration.py +2 -2
  13. reconcile/aws_ami_cleanup/integration.py +3 -4
  14. reconcile/aws_iam_password_reset.py +2 -5
  15. reconcile/aws_version_sync/integration.py +2 -2
  16. reconcile/blackbox_exporter_endpoint_monitoring.py +2 -5
  17. reconcile/change_owners/approver.py +4 -5
  18. reconcile/change_owners/bundle.py +20 -22
  19. reconcile/change_owners/change_types.py +23 -24
  20. reconcile/change_owners/changes.py +13 -16
  21. reconcile/change_owners/decision.py +2 -5
  22. reconcile/change_owners/diff.py +11 -15
  23. reconcile/change_owners/self_service_roles.py +1 -2
  24. reconcile/change_owners/tester.py +7 -10
  25. reconcile/checkpoint.py +2 -5
  26. reconcile/cli.py +9 -12
  27. reconcile/closedbox_endpoint_monitoring_base.py +8 -11
  28. reconcile/cluster_deployment_mapper.py +2 -5
  29. reconcile/cna/assets/asset.py +4 -7
  30. reconcile/cna/assets/null.py +2 -5
  31. reconcile/cna/integration.py +2 -3
  32. reconcile/cna/state.py +2 -5
  33. reconcile/dashdotdb_base.py +8 -11
  34. reconcile/dashdotdb_cso.py +3 -6
  35. reconcile/dashdotdb_dora.py +10 -14
  36. reconcile/dashdotdb_dvo.py +10 -13
  37. reconcile/dashdotdb_slo.py +5 -8
  38. reconcile/database_access_manager.py +5 -6
  39. reconcile/dynatrace_token_provider/integration.py +2 -5
  40. reconcile/external_resources/integration.py +1 -1
  41. reconcile/external_resources/manager.py +4 -4
  42. reconcile/external_resources/model.py +3 -3
  43. reconcile/external_resources/secrets_sync.py +5 -5
  44. reconcile/external_resources/state.py +5 -5
  45. reconcile/gabi_authorized_users.py +3 -6
  46. reconcile/gcr_mirror.py +1 -1
  47. reconcile/github_org.py +1 -3
  48. reconcile/github_repo_invites.py +2 -5
  49. reconcile/gitlab_housekeeping.py +7 -11
  50. reconcile/gitlab_labeler.py +1 -2
  51. reconcile/gitlab_members.py +2 -5
  52. reconcile/gitlab_permissions.py +1 -3
  53. reconcile/glitchtip/integration.py +2 -5
  54. reconcile/glitchtip_project_alerts/integration.py +3 -6
  55. reconcile/glitchtip_project_dsn/integration.py +4 -7
  56. reconcile/integrations_manager.py +5 -8
  57. reconcile/jenkins/types.py +5 -6
  58. reconcile/jenkins_job_builder.py +9 -12
  59. reconcile/jenkins_roles.py +1 -1
  60. reconcile/jira_watcher.py +2 -2
  61. reconcile/ldap_groups/integration.py +2 -5
  62. reconcile/ocm/types.py +21 -26
  63. reconcile/ocm_addons_upgrade_tests_trigger.py +3 -6
  64. reconcile/ocm_clusters.py +8 -8
  65. reconcile/ocm_internal_notifications/integration.py +1 -2
  66. reconcile/ocm_labels/integration.py +2 -5
  67. reconcile/ocm_machine_pools.py +11 -15
  68. reconcile/ocm_upgrade_scheduler_org_updater.py +2 -5
  69. reconcile/openshift_base.py +27 -29
  70. reconcile/openshift_groups.py +15 -20
  71. reconcile/openshift_namespace_labels.py +8 -14
  72. reconcile/openshift_namespaces.py +5 -8
  73. reconcile/openshift_network_policies.py +2 -4
  74. reconcile/openshift_resources_base.py +19 -29
  75. reconcile/openshift_saas_deploy.py +9 -10
  76. reconcile/openshift_saas_deploy_change_tester.py +7 -10
  77. reconcile/openshift_saas_deploy_trigger_base.py +4 -7
  78. reconcile/openshift_saas_deploy_trigger_cleaner.py +5 -8
  79. reconcile/openshift_saas_deploy_trigger_configs.py +1 -2
  80. reconcile/openshift_saas_deploy_trigger_images.py +1 -2
  81. reconcile/openshift_saas_deploy_trigger_moving_commits.py +1 -2
  82. reconcile/openshift_saas_deploy_trigger_upstream_jobs.py +1 -2
  83. reconcile/openshift_tekton_resources.py +7 -11
  84. reconcile/openshift_upgrade_watcher.py +10 -13
  85. reconcile/openshift_users.py +8 -11
  86. reconcile/oum/base.py +3 -4
  87. reconcile/oum/labelset.py +1 -2
  88. reconcile/oum/metrics.py +2 -2
  89. reconcile/oum/models.py +1 -2
  90. reconcile/oum/standalone.py +2 -3
  91. reconcile/prometheus_rules_tester/integration.py +6 -9
  92. reconcile/quay_membership.py +1 -2
  93. reconcile/quay_mirror.py +12 -13
  94. reconcile/quay_mirror_org.py +10 -10
  95. reconcile/queries.py +4 -7
  96. reconcile/resource_scraper.py +3 -4
  97. reconcile/rhidp/common.py +2 -2
  98. reconcile/saas_auto_promotions_manager/integration.py +5 -6
  99. reconcile/saas_auto_promotions_manager/merge_request_manager/batcher.py +1 -2
  100. reconcile/saas_auto_promotions_manager/publisher.py +5 -6
  101. reconcile/saas_auto_promotions_manager/subscriber.py +3 -4
  102. reconcile/saas_file_validator.py +2 -5
  103. reconcile/signalfx_endpoint_monitoring.py +2 -5
  104. reconcile/skupper_network/integration.py +3 -6
  105. reconcile/skupper_network/models.py +3 -5
  106. reconcile/slack_base.py +4 -7
  107. reconcile/slack_usergroups.py +15 -17
  108. reconcile/sql_query.py +5 -9
  109. reconcile/status_board.py +4 -5
  110. reconcile/statuspage/atlassian.py +14 -15
  111. reconcile/statuspage/integrations/maintenances.py +3 -3
  112. reconcile/statuspage/page.py +8 -8
  113. reconcile/statuspage/state.py +4 -5
  114. reconcile/statuspage/status.py +7 -8
  115. reconcile/templating/lib/rendering.py +8 -8
  116. reconcile/templating/renderer.py +10 -11
  117. reconcile/templating/validator.py +4 -4
  118. reconcile/terraform_aws_route53.py +3 -6
  119. reconcile/terraform_cloudflare_dns.py +9 -12
  120. reconcile/terraform_cloudflare_resources.py +9 -11
  121. reconcile/terraform_cloudflare_users.py +8 -11
  122. reconcile/terraform_init/integration.py +2 -2
  123. reconcile/terraform_repo.py +11 -14
  124. reconcile/terraform_resources.py +20 -21
  125. reconcile/terraform_tgw_attachments.py +32 -36
  126. reconcile/terraform_users.py +6 -7
  127. reconcile/terraform_vpc_resources/integration.py +5 -5
  128. reconcile/test/conftest.py +7 -10
  129. reconcile/test/fixtures.py +1 -1
  130. reconcile/test/saas_auto_promotions_manager/conftest.py +2 -2
  131. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/conftest.py +2 -2
  132. reconcile/test/test_database_access_manager.py +3 -6
  133. reconcile/test/test_gitlab_labeler.py +2 -5
  134. reconcile/test/test_jump_host.py +5 -8
  135. reconcile/test/test_ocm_machine_pools.py +1 -4
  136. reconcile/test/test_openshift_base.py +3 -6
  137. reconcile/test/test_openshift_cluster_bots.py +5 -5
  138. reconcile/test/test_openshift_namespace_labels.py +2 -3
  139. reconcile/test/test_openshift_saas_deploy_trigger_cleaner.py +2 -2
  140. reconcile/test/test_saasherder.py +9 -12
  141. reconcile/test/test_slack_base.py +4 -6
  142. reconcile/test/test_status_board.py +4 -7
  143. reconcile/test/test_terraform_tgw_attachments.py +14 -20
  144. reconcile/typed_queries/alerting_services_settings.py +1 -2
  145. reconcile/typed_queries/app_interface_custom_messages.py +2 -3
  146. reconcile/typed_queries/app_interface_deadmanssnitch_settings.py +1 -3
  147. reconcile/typed_queries/app_interface_repo_url.py +1 -2
  148. reconcile/typed_queries/app_interface_state_settings.py +1 -3
  149. reconcile/typed_queries/app_interface_vault_settings.py +1 -2
  150. reconcile/typed_queries/aws_vpc_requests.py +1 -3
  151. reconcile/typed_queries/aws_vpcs.py +1 -3
  152. reconcile/typed_queries/clusters.py +2 -4
  153. reconcile/typed_queries/clusters_minimal.py +1 -3
  154. reconcile/typed_queries/clusters_with_dms.py +1 -3
  155. reconcile/typed_queries/external_resources.py +3 -4
  156. reconcile/typed_queries/pagerduty_instances.py +1 -2
  157. reconcile/typed_queries/repos.py +2 -3
  158. reconcile/typed_queries/reserved_networks.py +1 -3
  159. reconcile/typed_queries/saas_files.py +49 -59
  160. reconcile/typed_queries/slo_documents.py +1 -3
  161. reconcile/typed_queries/status_board.py +3 -7
  162. reconcile/typed_queries/tekton_pipeline_providers.py +1 -2
  163. reconcile/typed_queries/terraform_namespaces.py +1 -2
  164. reconcile/typed_queries/terraform_tgw_attachments/aws_accounts.py +1 -3
  165. reconcile/utils/acs/base.py +2 -3
  166. reconcile/utils/acs/notifiers.py +3 -3
  167. reconcile/utils/acs/policies.py +3 -3
  168. reconcile/utils/aggregated_list.py +1 -1
  169. reconcile/utils/amtool.py +1 -2
  170. reconcile/utils/aws_api.py +28 -31
  171. reconcile/utils/binary.py +1 -3
  172. reconcile/utils/clusterhealth/providerbase.py +1 -2
  173. reconcile/utils/clusterhealth/telemeter.py +2 -2
  174. reconcile/utils/deadmanssnitch_api.py +1 -2
  175. reconcile/utils/disabled_integrations.py +4 -6
  176. reconcile/utils/environ.py +1 -1
  177. reconcile/utils/expiration.py +3 -7
  178. reconcile/utils/external_resource_spec.py +3 -4
  179. reconcile/utils/external_resources.py +4 -7
  180. reconcile/utils/filtering.py +1 -2
  181. reconcile/utils/git.py +3 -9
  182. reconcile/utils/git_secrets.py +5 -5
  183. reconcile/utils/github_api.py +5 -9
  184. reconcile/utils/gitlab_api.py +2 -3
  185. reconcile/utils/glitchtip/client.py +2 -4
  186. reconcile/utils/glitchtip/models.py +8 -11
  187. reconcile/utils/gql.py +26 -35
  188. reconcile/utils/grouping.py +1 -3
  189. reconcile/utils/imap_client.py +2 -5
  190. reconcile/utils/internal_groups/client.py +1 -2
  191. reconcile/utils/internal_groups/models.py +8 -9
  192. reconcile/utils/jenkins_api.py +4 -4
  193. reconcile/utils/jinja2/extensions.py +1 -1
  194. reconcile/utils/jinja2/filters.py +4 -4
  195. reconcile/utils/jinja2/utils.py +16 -16
  196. reconcile/utils/jira_client.py +10 -11
  197. reconcile/utils/jjb_client.py +14 -17
  198. reconcile/utils/jobcontroller/controller.py +5 -5
  199. reconcile/utils/jobcontroller/models.py +2 -2
  200. reconcile/utils/jsonpath.py +4 -5
  201. reconcile/utils/jump_host.py +7 -8
  202. reconcile/utils/keycloak.py +3 -7
  203. reconcile/utils/ldap_client.py +2 -3
  204. reconcile/utils/lean_terraform_client.py +13 -17
  205. reconcile/utils/membershipsources/app_interface_resolver.py +1 -1
  206. reconcile/utils/membershipsources/models.py +19 -22
  207. reconcile/utils/metrics.py +13 -15
  208. reconcile/utils/mr/base.py +7 -11
  209. reconcile/utils/mr/glitchtip_access_reporter.py +2 -2
  210. reconcile/utils/mr/notificator.py +1 -2
  211. reconcile/utils/oc.py +32 -38
  212. reconcile/utils/oc_connection_parameters.py +24 -25
  213. reconcile/utils/oc_filters.py +2 -3
  214. reconcile/utils/oc_map.py +9 -15
  215. reconcile/utils/ocm/addons.py +7 -10
  216. reconcile/utils/ocm/base.py +38 -39
  217. reconcile/utils/ocm/clusters.py +6 -9
  218. reconcile/utils/ocm/label_sources.py +1 -2
  219. reconcile/utils/ocm/labels.py +3 -6
  220. reconcile/utils/ocm/ocm.py +11 -14
  221. reconcile/utils/ocm/products.py +1 -3
  222. reconcile/utils/ocm/search_filters.py +16 -17
  223. reconcile/utils/ocm/service_log.py +2 -3
  224. reconcile/utils/ocm/sre_capability_labels.py +4 -8
  225. reconcile/utils/ocm/subscriptions.py +1 -3
  226. reconcile/utils/ocm/syncsets.py +2 -4
  227. reconcile/utils/ocm/upgrades.py +5 -9
  228. reconcile/utils/ocm_base_client.py +13 -16
  229. reconcile/utils/openshift_resource.py +5 -11
  230. reconcile/utils/output.py +2 -3
  231. reconcile/utils/pagerduty_api.py +4 -5
  232. reconcile/utils/prometheus.py +2 -2
  233. reconcile/utils/promotion_state.py +4 -5
  234. reconcile/utils/promtool.py +2 -8
  235. reconcile/utils/quay_api.py +12 -22
  236. reconcile/utils/raw_github_api.py +3 -5
  237. reconcile/utils/rosa/rosa_cli.py +6 -6
  238. reconcile/utils/rosa/session.py +6 -7
  239. reconcile/utils/runtime/desired_state_diff.py +3 -8
  240. reconcile/utils/runtime/environment.py +4 -7
  241. reconcile/utils/runtime/integration.py +4 -4
  242. reconcile/utils/runtime/meta.py +1 -2
  243. reconcile/utils/runtime/runner.py +7 -10
  244. reconcile/utils/runtime/sharding.py +22 -27
  245. reconcile/utils/saasherder/interfaces.py +63 -69
  246. reconcile/utils/saasherder/models.py +30 -35
  247. reconcile/utils/saasherder/saasherder.py +37 -53
  248. reconcile/utils/secret_reader.py +17 -19
  249. reconcile/utils/slack_api.py +15 -17
  250. reconcile/utils/smtp_client.py +1 -2
  251. reconcile/utils/sqs_gateway.py +1 -3
  252. reconcile/utils/state.py +1 -2
  253. reconcile/utils/terraform/config_client.py +4 -5
  254. reconcile/utils/terraform_client.py +3 -8
  255. reconcile/utils/terrascript/cloudflare_client.py +4 -10
  256. reconcile/utils/terrascript/cloudflare_resources.py +10 -13
  257. reconcile/utils/terrascript/models.py +2 -3
  258. reconcile/utils/terrascript/resources.py +1 -2
  259. reconcile/utils/terrascript_aws_client.py +30 -38
  260. reconcile/utils/unleash/client.py +4 -7
  261. reconcile/utils/unleash/server.py +2 -2
  262. reconcile/utils/vault.py +8 -11
  263. reconcile/utils/vaultsecretref.py +2 -3
  264. reconcile/utils/vcs.py +7 -8
  265. reconcile/vault_replication.py +4 -8
  266. reconcile/vpc_peerings_validator.py +4 -9
  267. release/version.py +6 -7
  268. tools/app_interface_reporter.py +2 -2
  269. tools/cli_commands/gpg_encrypt.py +3 -6
  270. tools/cli_commands/systems_and_tools.py +4 -7
  271. tools/qontract_cli.py +31 -17
  272. tools/saas_promotion_state/__init__.py +0 -0
  273. tools/saas_promotion_state/saas_promotion_state.py +72 -0
  274. tools/template_validation.py +1 -1
  275. tools/test/conftest.py +45 -6
  276. tools/test/test_saas_promotion_state.py +86 -0
  277. {qontract_reconcile-0.10.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/WHEEL +0 -0
  278. {qontract_reconcile-0.10.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/entry_points.txt +0 -0
  279. {qontract_reconcile-0.10.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/top_level.txt +0 -0
tools/qontract_cli.py CHANGED
@@ -9,16 +9,13 @@ import re
9
9
  import sys
10
10
  from collections import defaultdict
11
11
  from datetime import (
12
+ UTC,
12
13
  datetime,
13
14
  timedelta,
14
- timezone,
15
15
  )
16
16
  from operator import itemgetter
17
17
  from statistics import median
18
- from typing import (
19
- Any,
20
- Optional,
21
- )
18
+ from typing import Any
22
19
 
23
20
  import boto3
24
21
  import click
@@ -352,8 +349,8 @@ def get_upgrade_policies_data(
352
349
 
353
350
  def soaking_str(
354
351
  soaking: dict[str, Any],
355
- upgrade_policy: Optional[AbstractUpgradePolicy],
356
- upgradeable_version: Optional[str],
352
+ upgrade_policy: AbstractUpgradePolicy | None,
353
+ upgradeable_version: str | None,
357
354
  ) -> str:
358
355
  if upgrade_policy:
359
356
  upgrade_version = upgrade_policy.version
@@ -764,9 +761,9 @@ def ocm_addon_upgrade_policies(ctx: click.core.Context) -> None:
764
761
  @click.pass_context
765
762
  def sd_app_sre_alert_report(
766
763
  ctx: click.core.Context,
767
- days: Optional[int],
768
- from_timestamp: Optional[int],
769
- to_timestamp: Optional[int],
764
+ days: int | None,
765
+ from_timestamp: int | None,
766
+ to_timestamp: int | None,
770
767
  ) -> None:
771
768
  import tools.sd_app_sre_alert_report as report
772
769
 
@@ -1407,9 +1404,7 @@ def rosa_create_cluster_command(ctx, cluster_name):
1407
1404
  @click.argument("jumphost_hostname", required=False)
1408
1405
  @click.argument("cluster_name", required=False)
1409
1406
  @click.pass_context
1410
- def sshuttle_command(
1411
- ctx, jumphost_hostname: Optional[str], cluster_name: Optional[str]
1412
- ):
1407
+ def sshuttle_command(ctx, jumphost_hostname: str | None, cluster_name: str | None):
1413
1408
  jumphosts_query_data = queries.get_jumphosts(hostname=jumphost_hostname)
1414
1409
  jumphosts = jumphosts_query_data.jumphosts or []
1415
1410
  for jh in jumphosts:
@@ -2358,7 +2353,7 @@ def ec2_jenkins_workers(ctx, aws_access_key_id, aws_secret_access_key, aws_regio
2358
2353
  client = boto3.client("autoscaling")
2359
2354
  ec2 = boto3.resource("ec2")
2360
2355
  results = []
2361
- now = datetime.now(timezone.utc)
2356
+ now = datetime.now(UTC)
2362
2357
  DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
2363
2358
  columns = [
2364
2359
  "type",
@@ -2511,7 +2506,7 @@ def alerts(ctx, file_path):
2511
2506
  case _:
2512
2507
  return BIG_NUMBER
2513
2508
 
2514
- with open(file_path, "r", encoding="locale") as f:
2509
+ with open(file_path, encoding="locale") as f:
2515
2510
  content = json.loads(f.read())
2516
2511
 
2517
2512
  columns = [
@@ -2605,7 +2600,7 @@ def osd_component_versions(ctx):
2605
2600
  @get.command()
2606
2601
  @click.pass_context
2607
2602
  def maintenances(ctx):
2608
- now = datetime.now(timezone.utc)
2603
+ now = datetime.now(UTC)
2609
2604
  maintenances = maintenances_gql.query(gql.get_api().query).maintenances or []
2610
2605
  data = [
2611
2606
  {
@@ -3474,7 +3469,7 @@ def saas_dev(ctx, app_name=None, saas_file_name=None, env_name=None) -> None:
3474
3469
  @click.option("--app-name", default=None, help="app to act on.")
3475
3470
  @click.pass_context
3476
3471
  def saas_targets(
3477
- ctx, saas_file_name: Optional[str] = None, app_name: Optional[str] = None
3472
+ ctx, saas_file_name: str | None = None, app_name: str | None = None
3478
3473
  ) -> None:
3479
3474
  """Resolve namespaceSelectors and print all resulting targets of a saas file."""
3480
3475
  console = Console()
@@ -3663,6 +3658,25 @@ def gpg_encrypt(
3663
3658
  ).execute()
3664
3659
 
3665
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
+
3666
3680
  @root.command()
3667
3681
  @click.option("--change-type-name")
3668
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
+ )
@@ -27,7 +27,7 @@ def load_clean_yaml(path: str) -> dict:
27
27
 
28
28
  def load_yaml(to_load: str) -> dict:
29
29
  ruamel_instance = create_ruamel_instance()
30
- with open(to_load, "r", encoding="utf-8") as file:
30
+ with open(to_load, encoding="utf-8") as file:
31
31
  return ruamel_instance.load(file)
32
32
 
33
33
 
tools/test/conftest.py CHANGED
@@ -1,16 +1,17 @@
1
1
  from collections.abc import (
2
2
  Callable,
3
+ Iterable,
4
+ Mapping,
3
5
  MutableMapping,
4
6
  )
5
- from typing import (
6
- Any,
7
- Optional,
8
- )
7
+ from pathlib import Path
8
+ from typing import Any
9
9
 
10
10
  import pytest
11
11
  from pydantic import BaseModel
12
12
  from pydantic.error_wrappers import ValidationError
13
13
 
14
+ from reconcile.typed_queries.saas_files import SaasFile
14
15
  from reconcile.utils.models import data_default_none
15
16
 
16
17
 
@@ -18,17 +19,55 @@ class GQLClassFactoryError(Exception):
18
19
  pass
19
20
 
20
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
+
21
60
  @pytest.fixture
22
61
  def gql_class_factory() -> (
23
62
  Callable[
24
- [type[BaseModel], Optional[MutableMapping[str, Any]]],
63
+ [type[BaseModel], MutableMapping[str, Any] | None],
25
64
  BaseModel,
26
65
  ]
27
66
  ):
28
67
  """Create a GQL class from a fixture and set default values to None."""
29
68
 
30
69
  def _gql_class_factory(
31
- klass: type[BaseModel], data: Optional[MutableMapping[str, Any]] = None
70
+ klass: type[BaseModel], data: MutableMapping[str, Any] | None = None
32
71
  ) -> BaseModel:
33
72
  try:
34
73
  return klass(**data_default_none(klass, data or {}))
@@ -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
+ )