qontract-reconcile 0.10.2.dev361__py3-none-any.whl → 0.10.2.dev474__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 (371) hide show
  1. {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/METADATA +14 -13
  2. {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/RECORD +371 -364
  3. {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/WHEEL +1 -1
  4. reconcile/acs_rbac.py +2 -2
  5. reconcile/aus/advanced_upgrade_service.py +18 -12
  6. reconcile/aus/aus_sts_gate_handler.py +59 -0
  7. reconcile/aus/base.py +137 -34
  8. reconcile/aus/cluster_version_data.py +15 -5
  9. reconcile/aus/models.py +3 -1
  10. reconcile/aus/ocm_addons_upgrade_scheduler_org.py +1 -0
  11. reconcile/aus/ocm_upgrade_scheduler.py +8 -1
  12. reconcile/aus/ocm_upgrade_scheduler_org.py +20 -5
  13. reconcile/aus/version_gate_approver.py +1 -16
  14. reconcile/aus/version_gates/sts_version_gate_handler.py +5 -72
  15. reconcile/automated_actions/config/integration.py +16 -4
  16. reconcile/aws_account_manager/integration.py +21 -9
  17. reconcile/aws_account_manager/reconciler.py +3 -3
  18. reconcile/aws_account_manager/utils.py +1 -1
  19. reconcile/aws_ami_cleanup/integration.py +8 -12
  20. reconcile/aws_ami_share.py +69 -62
  21. reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
  22. reconcile/aws_ecr_image_pull_secrets.py +1 -1
  23. reconcile/aws_iam_keys.py +1 -0
  24. reconcile/aws_saml_idp/integration.py +12 -4
  25. reconcile/aws_saml_roles/integration.py +30 -23
  26. reconcile/aws_version_sync/integration.py +6 -12
  27. reconcile/change_owners/README.md +1 -1
  28. reconcile/change_owners/bundle.py +3 -3
  29. reconcile/change_owners/change_log_tracking.py +3 -2
  30. reconcile/change_owners/change_owners.py +108 -42
  31. reconcile/change_owners/decision.py +1 -1
  32. reconcile/change_owners/diff.py +0 -2
  33. reconcile/checkpoint.py +11 -3
  34. reconcile/cli.py +94 -11
  35. reconcile/dashdotdb_dora.py +5 -12
  36. reconcile/dashdotdb_slo.py +1 -1
  37. reconcile/database_access_manager.py +123 -117
  38. reconcile/dynatrace_token_provider/integration.py +1 -1
  39. reconcile/endpoints_discovery/integration.py +4 -1
  40. reconcile/endpoints_discovery/merge_request.py +1 -1
  41. reconcile/endpoints_discovery/merge_request_manager.py +8 -8
  42. reconcile/external_resources/factories.py +4 -6
  43. reconcile/external_resources/integration.py +1 -1
  44. reconcile/external_resources/manager.py +8 -6
  45. reconcile/external_resources/meta.py +0 -1
  46. reconcile/external_resources/metrics.py +1 -1
  47. reconcile/external_resources/model.py +19 -15
  48. reconcile/external_resources/reconciler.py +7 -4
  49. reconcile/external_resources/secrets_sync.py +6 -10
  50. reconcile/external_resources/state.py +26 -16
  51. reconcile/fleet_labeler/integration.py +1 -1
  52. reconcile/gabi_authorized_users.py +5 -2
  53. reconcile/gcp_image_mirror.py +2 -2
  54. reconcile/github_org.py +1 -1
  55. reconcile/github_owners.py +4 -0
  56. reconcile/gitlab_housekeeping.py +13 -15
  57. reconcile/gitlab_members.py +6 -12
  58. reconcile/gitlab_owners.py +15 -11
  59. reconcile/gitlab_permissions.py +8 -12
  60. reconcile/glitchtip_project_alerts/integration.py +3 -1
  61. reconcile/gql_definitions/acs/acs_instances.py +5 -5
  62. reconcile/gql_definitions/acs/acs_policies.py +5 -5
  63. reconcile/gql_definitions/acs/acs_rbac.py +5 -5
  64. reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +5 -5
  65. reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +5 -5
  66. reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +5 -5
  67. reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
  68. reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
  69. reconcile/gql_definitions/automated_actions/instance.py +46 -7
  70. reconcile/gql_definitions/aws_account_manager/aws_accounts.py +14 -5
  71. reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +15 -5
  72. reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +27 -66
  73. reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +15 -5
  74. reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +15 -5
  75. reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
  76. reconcile/gql_definitions/aws_version_sync/clusters.py +5 -5
  77. reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
  78. reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
  79. reconcile/gql_definitions/change_owners/queries/self_service_roles.py +5 -5
  80. reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +5 -5
  81. reconcile/gql_definitions/common/alerting_services_settings.py +5 -5
  82. reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
  83. reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
  84. reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
  85. reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
  86. reconcile/gql_definitions/common/app_interface_roles.py +5 -5
  87. reconcile/gql_definitions/common/app_interface_state_settings.py +5 -5
  88. reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
  89. reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
  90. reconcile/gql_definitions/common/apps.py +5 -5
  91. reconcile/gql_definitions/common/aws_vpc_requests.py +18 -5
  92. reconcile/gql_definitions/common/aws_vpcs.py +5 -5
  93. reconcile/gql_definitions/common/clusters.py +7 -5
  94. reconcile/gql_definitions/common/clusters_minimal.py +5 -5
  95. reconcile/gql_definitions/common/clusters_with_dms.py +5 -5
  96. reconcile/gql_definitions/common/clusters_with_peering.py +5 -5
  97. reconcile/gql_definitions/common/github_orgs.py +5 -5
  98. reconcile/gql_definitions/common/jira_settings.py +5 -5
  99. reconcile/gql_definitions/common/jiralert_settings.py +5 -5
  100. reconcile/gql_definitions/common/ldap_settings.py +5 -5
  101. reconcile/gql_definitions/common/namespaces.py +5 -5
  102. reconcile/gql_definitions/common/namespaces_minimal.py +7 -5
  103. reconcile/gql_definitions/common/ocm_env_telemeter.py +5 -5
  104. reconcile/gql_definitions/common/ocm_environments.py +5 -5
  105. reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
  106. reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -5
  107. reconcile/gql_definitions/common/pipeline_providers.py +5 -5
  108. reconcile/gql_definitions/common/quay_instances.py +5 -5
  109. reconcile/gql_definitions/common/quay_orgs.py +5 -5
  110. reconcile/gql_definitions/common/reserved_networks.py +5 -5
  111. reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
  112. reconcile/gql_definitions/common/saas_files.py +5 -5
  113. reconcile/gql_definitions/common/saas_target_namespaces.py +5 -5
  114. reconcile/gql_definitions/common/saasherder_settings.py +5 -5
  115. reconcile/gql_definitions/common/slack_workspaces.py +5 -5
  116. reconcile/gql_definitions/common/smtp_client_settings.py +5 -5
  117. reconcile/gql_definitions/common/state_aws_account.py +5 -5
  118. reconcile/gql_definitions/common/users.py +5 -5
  119. reconcile/gql_definitions/common/users_with_paths.py +5 -5
  120. reconcile/gql_definitions/cost_report/app_names.py +5 -5
  121. reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
  122. reconcile/gql_definitions/cost_report/settings.py +5 -5
  123. reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +5 -5
  124. reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +5 -5
  125. reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
  126. reconcile/gql_definitions/email_sender/apps.py +5 -5
  127. reconcile/gql_definitions/email_sender/emails.py +5 -5
  128. reconcile/gql_definitions/email_sender/users.py +5 -5
  129. reconcile/gql_definitions/endpoints_discovery/apps.py +5 -5
  130. reconcile/gql_definitions/external_resources/aws_accounts.py +5 -5
  131. reconcile/gql_definitions/external_resources/external_resources_modules.py +5 -5
  132. reconcile/gql_definitions/external_resources/external_resources_namespaces.py +38 -7
  133. reconcile/gql_definitions/external_resources/external_resources_settings.py +5 -5
  134. reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
  135. reconcile/gql_definitions/fleet_labeler/fleet_labels.py +5 -5
  136. reconcile/gql_definitions/fragments/aus_organization.py +5 -5
  137. reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
  138. reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
  139. reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
  140. reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
  141. reconcile/gql_definitions/fragments/aws_organization.py +33 -0
  142. reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
  143. reconcile/gql_definitions/fragments/aws_vpc_request.py +12 -5
  144. reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
  145. reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
  146. reconcile/gql_definitions/fragments/disable.py +5 -5
  147. reconcile/gql_definitions/fragments/email_service.py +5 -5
  148. reconcile/gql_definitions/fragments/email_user.py +5 -5
  149. reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
  150. reconcile/gql_definitions/fragments/membership_source.py +5 -5
  151. reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
  152. reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
  153. reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
  154. reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
  155. reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
  156. reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
  157. reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
  158. reconcile/gql_definitions/fragments/resource_values.py +5 -5
  159. reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
  160. reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
  161. reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
  162. reconcile/gql_definitions/fragments/terraform_state.py +5 -5
  163. reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
  164. reconcile/gql_definitions/fragments/user.py +5 -5
  165. reconcile/gql_definitions/fragments/vault_secret.py +5 -5
  166. reconcile/gql_definitions/gcp/gcp_docker_repos.py +5 -5
  167. reconcile/gql_definitions/gcp/gcp_projects.py +5 -5
  168. reconcile/gql_definitions/gitlab_members/gitlab_instances.py +5 -5
  169. reconcile/gql_definitions/gitlab_members/permissions.py +5 -5
  170. reconcile/gql_definitions/glitchtip/glitchtip_instance.py +5 -5
  171. reconcile/gql_definitions/glitchtip/glitchtip_project.py +5 -5
  172. reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +5 -5
  173. reconcile/gql_definitions/integrations/integrations.py +5 -5
  174. reconcile/gql_definitions/introspection.json +775 -136
  175. reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +5 -5
  176. reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +5 -5
  177. reconcile/gql_definitions/jira/jira_servers.py +5 -5
  178. reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +9 -5
  179. reconcile/gql_definitions/jumphosts/jumphosts.py +5 -5
  180. reconcile/gql_definitions/ldap_groups/roles.py +5 -5
  181. reconcile/gql_definitions/ldap_groups/settings.py +5 -5
  182. reconcile/gql_definitions/maintenance/maintenances.py +5 -5
  183. reconcile/gql_definitions/membershipsources/roles.py +5 -5
  184. reconcile/gql_definitions/ocm_labels/clusters.py +5 -5
  185. reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
  186. reconcile/gql_definitions/openshift_cluster_bots/clusters.py +5 -5
  187. reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
  188. reconcile/gql_definitions/openshift_groups/managed_roles.py +5 -5
  189. reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +5 -5
  190. reconcile/gql_definitions/quay_membership/quay_membership.py +5 -5
  191. reconcile/gql_definitions/rhcs/certs.py +25 -79
  192. reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +43 -0
  193. reconcile/gql_definitions/rhidp/organizations.py +5 -5
  194. reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
  195. reconcile/gql_definitions/service_dependencies/service_dependencies.py +5 -5
  196. reconcile/gql_definitions/sharding/aws_accounts.py +5 -5
  197. reconcile/gql_definitions/sharding/ocm_organization.py +5 -5
  198. reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
  199. reconcile/gql_definitions/skupper_network/skupper_networks.py +5 -5
  200. reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
  201. reconcile/gql_definitions/slack_usergroups/permissions.py +5 -5
  202. reconcile/gql_definitions/slack_usergroups/users.py +5 -5
  203. reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
  204. reconcile/gql_definitions/status_board/status_board.py +5 -5
  205. reconcile/gql_definitions/statuspage/statuspages.py +5 -5
  206. reconcile/gql_definitions/templating/template_collection.py +5 -5
  207. reconcile/gql_definitions/templating/templates.py +5 -5
  208. reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +5 -5
  209. reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +5 -5
  210. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +5 -5
  211. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +5 -5
  212. reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +5 -5
  213. reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +5 -5
  214. reconcile/gql_definitions/terraform_init/aws_accounts.py +19 -5
  215. reconcile/gql_definitions/terraform_repo/terraform_repo.py +5 -5
  216. reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
  217. reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +37 -7
  218. reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +15 -5
  219. reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +5 -5
  220. reconcile/gql_definitions/vault_instances/vault_instances.py +5 -5
  221. reconcile/gql_definitions/vault_policies/vault_policies.py +5 -5
  222. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +8 -5
  223. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +6 -5
  224. reconcile/integrations_manager.py +3 -3
  225. reconcile/jenkins_worker_fleets.py +10 -8
  226. reconcile/jira_permissions_validator.py +237 -122
  227. reconcile/ldap_groups/integration.py +1 -1
  228. reconcile/ocm/types.py +35 -57
  229. reconcile/ocm_aws_infrastructure_access.py +1 -1
  230. reconcile/ocm_clusters.py +4 -4
  231. reconcile/ocm_labels/integration.py +3 -2
  232. reconcile/ocm_machine_pools.py +33 -27
  233. reconcile/openshift_base.py +113 -4
  234. reconcile/openshift_cluster_bots.py +1 -1
  235. reconcile/openshift_namespace_labels.py +1 -1
  236. reconcile/openshift_namespaces.py +96 -101
  237. reconcile/openshift_resources_base.py +6 -2
  238. reconcile/openshift_rhcs_certs.py +74 -37
  239. reconcile/openshift_rolebindings.py +7 -11
  240. reconcile/openshift_saas_deploy.py +4 -5
  241. reconcile/openshift_saas_deploy_change_tester.py +9 -7
  242. reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
  243. reconcile/openshift_serviceaccount_tokens.py +2 -2
  244. reconcile/openshift_upgrade_watcher.py +4 -4
  245. reconcile/oum/labelset.py +5 -3
  246. reconcile/oum/models.py +1 -4
  247. reconcile/prometheus_rules_tester/integration.py +3 -3
  248. reconcile/quay_base.py +25 -6
  249. reconcile/quay_membership.py +55 -29
  250. reconcile/quay_mirror.py +1 -1
  251. reconcile/quay_mirror_org.py +6 -4
  252. reconcile/quay_permissions.py +81 -75
  253. reconcile/quay_repos.py +35 -37
  254. reconcile/queries.py +132 -1
  255. reconcile/rhidp/common.py +3 -5
  256. reconcile/rhidp/sso_client/base.py +16 -5
  257. reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
  258. reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
  259. reconcile/skupper_network/integration.py +2 -2
  260. reconcile/slack_usergroups.py +35 -14
  261. reconcile/sql_query.py +1 -0
  262. reconcile/status_board.py +6 -6
  263. reconcile/statuspage/atlassian.py +7 -7
  264. reconcile/statuspage/integrations/maintenances.py +4 -3
  265. reconcile/statuspage/page.py +4 -9
  266. reconcile/statuspage/status.py +5 -8
  267. reconcile/templates/rosa-classic-cluster-creation.sh.j2 +1 -1
  268. reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +1 -1
  269. reconcile/templating/lib/rendering.py +3 -3
  270. reconcile/templating/renderer.py +2 -2
  271. reconcile/templating/validator.py +4 -4
  272. reconcile/terraform_aws_route53.py +7 -1
  273. reconcile/terraform_cloudflare_dns.py +3 -3
  274. reconcile/terraform_cloudflare_resources.py +5 -5
  275. reconcile/terraform_cloudflare_users.py +3 -2
  276. reconcile/terraform_init/integration.py +187 -23
  277. reconcile/terraform_repo.py +16 -12
  278. reconcile/terraform_resources.py +6 -6
  279. reconcile/terraform_tgw_attachments.py +27 -19
  280. reconcile/terraform_users.py +7 -0
  281. reconcile/terraform_vpc_peerings.py +14 -3
  282. reconcile/terraform_vpc_resources/integration.py +20 -8
  283. reconcile/terraform_vpc_resources/merge_request.py +12 -2
  284. reconcile/terraform_vpc_resources/merge_request_manager.py +43 -19
  285. reconcile/typed_queries/aws_account_tags.py +41 -0
  286. reconcile/typed_queries/cost_report/app_names.py +1 -1
  287. reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
  288. reconcile/typed_queries/saas_files.py +20 -15
  289. reconcile/typed_queries/status_board.py +2 -2
  290. reconcile/unleash_feature_toggles/integration.py +4 -2
  291. reconcile/utils/acs/base.py +6 -3
  292. reconcile/utils/acs/policies.py +2 -2
  293. reconcile/utils/aws_api.py +51 -20
  294. reconcile/utils/aws_api_typed/api.py +38 -9
  295. reconcile/utils/aws_api_typed/cloudformation.py +149 -0
  296. reconcile/utils/aws_api_typed/logs.py +73 -0
  297. reconcile/utils/aws_api_typed/organization.py +4 -2
  298. reconcile/utils/binary.py +7 -12
  299. reconcile/utils/datetime_util.py +67 -0
  300. reconcile/utils/deadmanssnitch_api.py +1 -1
  301. reconcile/utils/differ.py +2 -3
  302. reconcile/utils/early_exit_cache.py +11 -12
  303. reconcile/utils/environ.py +5 -0
  304. reconcile/utils/expiration.py +7 -3
  305. reconcile/utils/external_resource_spec.py +2 -0
  306. reconcile/utils/filtering.py +1 -1
  307. reconcile/utils/gitlab_api.py +19 -5
  308. reconcile/utils/glitchtip/client.py +6 -2
  309. reconcile/utils/glitchtip/models.py +25 -28
  310. reconcile/utils/gql.py +4 -7
  311. reconcile/utils/helpers.py +1 -1
  312. reconcile/utils/instrumented_wrappers.py +1 -1
  313. reconcile/utils/internal_groups/client.py +2 -2
  314. reconcile/utils/internal_groups/models.py +8 -17
  315. reconcile/utils/jinja2/utils.py +6 -101
  316. reconcile/utils/jira_client.py +82 -63
  317. reconcile/utils/jjb_client.py +26 -13
  318. reconcile/utils/jobcontroller/controller.py +2 -2
  319. reconcile/utils/jobcontroller/models.py +17 -1
  320. reconcile/utils/json.py +43 -1
  321. reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
  322. reconcile/utils/membershipsources/models.py +16 -23
  323. reconcile/utils/membershipsources/resolver.py +4 -2
  324. reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
  325. reconcile/utils/merge_request_manager/parser.py +6 -6
  326. reconcile/utils/metrics.py +5 -5
  327. reconcile/utils/models.py +304 -82
  328. reconcile/utils/mr/app_interface_reporter.py +2 -2
  329. reconcile/utils/mr/notificator.py +3 -3
  330. reconcile/utils/mr/update_access_report_base.py +3 -4
  331. reconcile/utils/mr/user_maintenance.py +3 -2
  332. reconcile/utils/oc.py +252 -201
  333. reconcile/utils/oc_filters.py +3 -3
  334. reconcile/utils/ocm/addons.py +0 -1
  335. reconcile/utils/ocm/base.py +17 -20
  336. reconcile/utils/ocm/cluster_groups.py +1 -1
  337. reconcile/utils/ocm/identity_providers.py +2 -2
  338. reconcile/utils/ocm/labels.py +1 -1
  339. reconcile/utils/ocm/products.py +8 -8
  340. reconcile/utils/ocm/search_filters.py +3 -6
  341. reconcile/utils/ocm/service_log.py +4 -6
  342. reconcile/utils/ocm/sre_capability_labels.py +20 -13
  343. reconcile/utils/openshift_resource.py +8 -3
  344. reconcile/utils/pagerduty_api.py +10 -7
  345. reconcile/utils/promotion_state.py +6 -11
  346. reconcile/utils/quay_api.py +74 -87
  347. reconcile/utils/raw_github_api.py +1 -1
  348. reconcile/utils/rhcsv2_certs.py +86 -23
  349. reconcile/utils/rosa/session.py +16 -0
  350. reconcile/utils/runtime/integration.py +2 -3
  351. reconcile/utils/runtime/runner.py +2 -2
  352. reconcile/utils/saasherder/interfaces.py +13 -20
  353. reconcile/utils/saasherder/models.py +23 -20
  354. reconcile/utils/saasherder/saasherder.py +50 -27
  355. reconcile/utils/slack_api.py +2 -2
  356. reconcile/utils/sloth.py +171 -2
  357. reconcile/utils/structs.py +1 -1
  358. reconcile/utils/terraform_client.py +5 -4
  359. reconcile/utils/terrascript_aws_client.py +274 -124
  360. reconcile/utils/unleash/server.py +2 -8
  361. reconcile/utils/vault.py +5 -12
  362. reconcile/utils/vcs.py +8 -8
  363. reconcile/vault_replication.py +107 -42
  364. reconcile/vpc_peerings_validator.py +13 -0
  365. tools/app_interface_reporter.py +4 -4
  366. tools/cli_commands/cost_report/cost_management_api.py +3 -3
  367. tools/cli_commands/cost_report/view.py +7 -6
  368. tools/cli_commands/erv2.py +1 -1
  369. tools/qontract_cli.py +28 -17
  370. tools/template_validation.py +3 -1
  371. {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,5 @@
1
1
  from collections import defaultdict
2
+ from dataclasses import dataclass
2
3
 
3
4
  from reconcile.aus.healthchecks import (
4
5
  AUSClusterHealthCheckProvider,
@@ -13,6 +14,7 @@ from reconcile.aus.node_pool_spec import get_node_pool_specs_by_org_cluster
13
14
  from reconcile.aus.ocm_upgrade_scheduler import OCMClusterUpgradeSchedulerIntegration
14
15
  from reconcile.gql_definitions.fragments.aus_organization import AUSOCMOrganization
15
16
  from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
17
+ from reconcile.utils.ocm.base import LabelContainer
16
18
  from reconcile.utils.ocm.clusters import (
17
19
  OCMCluster,
18
20
  discover_clusters_for_organizations,
@@ -22,6 +24,12 @@ from reconcile.utils.ocm_base_client import init_ocm_base_client
22
24
  QONTRACT_INTEGRATION = "ocm-upgrade-scheduler-org"
23
25
 
24
26
 
27
+ @dataclass
28
+ class ClusterUpgradeSpecWithLabels:
29
+ cluster: OCMCluster
30
+ cluster_labels: LabelContainer
31
+
32
+
25
33
  class OCMClusterUpgradeSchedulerOrgIntegration(OCMClusterUpgradeSchedulerIntegration):
26
34
  @property
27
35
  def name(self) -> str:
@@ -60,7 +68,10 @@ class OCMClusterUpgradeSchedulerOrgIntegration(OCMClusterUpgradeSchedulerIntegra
60
68
  specs=self._build_cluster_upgrade_specs(
61
69
  org=org,
62
70
  clusters_by_name={
63
- c.ocm_cluster.name: c.ocm_cluster
71
+ c.ocm_cluster.name: ClusterUpgradeSpecWithLabels(
72
+ cluster=c.ocm_cluster,
73
+ cluster_labels=c.labels,
74
+ )
64
75
  for c in clusters_by_org[org.org_id]
65
76
  },
66
77
  cluster_health_provider=build_cluster_health_providers_for_organization(
@@ -79,7 +90,7 @@ class OCMClusterUpgradeSchedulerOrgIntegration(OCMClusterUpgradeSchedulerIntegra
79
90
  def _build_cluster_upgrade_specs(
80
91
  self,
81
92
  org: AUSOCMOrganization,
82
- clusters_by_name: dict[str, OCMCluster],
93
+ clusters_by_name: dict[str, ClusterUpgradeSpecWithLabels],
83
94
  cluster_health_provider: AUSClusterHealthCheckProvider,
84
95
  node_pool_specs_by_cluster_id: dict[str, list[NodePoolSpec]],
85
96
  ) -> list[ClusterUpgradeSpec]:
@@ -87,12 +98,16 @@ class OCMClusterUpgradeSchedulerOrgIntegration(OCMClusterUpgradeSchedulerIntegra
87
98
  ClusterUpgradeSpec(
88
99
  org=org,
89
100
  upgradePolicy=cluster.upgrade_policy,
90
- cluster=clusters_by_name[cluster.name],
101
+ cluster=clusters_by_name[cluster.name].cluster,
102
+ cluster_labels=clusters_by_name[cluster.name].cluster_labels,
91
103
  health=cluster_health_provider.cluster_health(
92
- cluster_external_id=clusters_by_name[cluster.name].external_id,
104
+ cluster_external_id=clusters_by_name[
105
+ cluster.name
106
+ ].cluster.external_id,
93
107
  org_id=org.org_id,
94
108
  ),
95
- nodePools=node_pool_specs_by_cluster_id.get(ocm_cluster.id) or [],
109
+ nodePools=node_pool_specs_by_cluster_id.get(ocm_cluster.cluster.id)
110
+ or [],
96
111
  )
97
112
  for cluster in org.upgrade_policy_clusters or []
98
113
  # clusters that are not in the UUID dict will be ignored because
@@ -14,9 +14,6 @@ from reconcile.gql_definitions.common.ocm_environments import (
14
14
  )
15
15
  from reconcile.utils import gql
16
16
  from reconcile.utils.grouping import group_by
17
- from reconcile.utils.jobcontroller.controller import (
18
- build_job_controller,
19
- )
20
17
  from reconcile.utils.ocm.base import (
21
18
  ClusterDetails,
22
19
  LabelContainer,
@@ -63,19 +60,7 @@ class VersionGateApprover(QontractReconcileIntegration[VersionGateApproverParams
63
60
 
64
61
  def initialize_handlers(self, query_func: Callable) -> None:
65
62
  self.handlers: dict[str, GateHandler] = {
66
- sts_version_gate_handler.GATE_LABEL: sts_version_gate_handler.STSGateHandler(
67
- job_controller=build_job_controller(
68
- integration=QONTRACT_INTEGRATION,
69
- integration_version=QONTRACT_INTEGRATION_VERSION,
70
- cluster=self.params.job_controller_cluster,
71
- namespace=self.params.job_controller_namespace,
72
- secret_reader=self.secret_reader,
73
- dry_run=False,
74
- ),
75
- aws_iam_role=self.params.rosa_role,
76
- rosa_job_service_account=self.params.rosa_job_service_account,
77
- rosa_job_image=self.params.rosa_job_image,
78
- ),
63
+ sts_version_gate_handler.GATE_LABEL: sts_version_gate_handler.STSGateHandler(),
79
64
  ocp_gate_handler.GATE_LABEL: ocp_gate_handler.OCPGateHandler(),
80
65
  ingress_gate_handler.GATE_LABEL: ingress_gate_handler.IngressGateHandler(),
81
66
  }
@@ -1,27 +1,16 @@
1
- import logging
2
-
3
1
  from reconcile.aus.version_gates.handler import GateHandler
4
- from reconcile.utils.jobcontroller.controller import K8sJobController
5
2
  from reconcile.utils.ocm.base import OCMCluster, OCMVersionGate
6
3
  from reconcile.utils.ocm_base_client import OCMBaseClient
7
- from reconcile.utils.rosa.rosa_cli import RosaCliError
8
- from reconcile.utils.rosa.session import RosaSession
9
4
 
10
5
  GATE_LABEL = "api.openshift.com/gate-sts"
11
6
 
12
7
 
13
8
  class STSGateHandler(GateHandler):
14
- def __init__(
15
- self,
16
- job_controller: K8sJobController,
17
- aws_iam_role: str,
18
- rosa_job_service_account: str | None = None,
19
- rosa_job_image: str | None = None,
20
- ) -> None:
21
- self.job_controller = job_controller
22
- self.aws_iam_role = aws_iam_role
23
- self.rosa_job_image = rosa_job_image
24
- self.rosa_job_service_account = rosa_job_service_account
9
+ """
10
+ This handler is used to handle the STS version gate.
11
+ Right now we just ack all gate-sts gates
12
+ The actual job of role upgrade is now a part of AUS and is handled by the AUSSTSGateHandler.
13
+ """
25
14
 
26
15
  @staticmethod
27
16
  def gate_applicable_to_cluster(cluster: OCMCluster) -> bool:
@@ -41,60 +30,4 @@ class STSGateHandler(GateHandler):
41
30
  gate: OCMVersionGate,
42
31
  dry_run: bool,
43
32
  ) -> bool:
44
- if (
45
- not cluster.id
46
- or not cluster.aws
47
- or not cluster.aws.sts
48
- or not cluster.is_sts()
49
- ):
50
- # checked already but mypy :/
51
- return False
52
-
53
- if cluster.is_rosa_hypershift():
54
- # thanks to hypershift managed policies, there is nothing to do for us here
55
- # returning True will ack the version gate
56
- return True
57
- if not cluster.is_rosa_classic():
58
- # we manage roels only for rosa classic clusters
59
- # returning here will prevent OSD STS clusters to be handled right now
60
- logging.error(
61
- f"Cluster {cluster.id} is not a ROSA cluster. "
62
- "STS version gates are only handled for ROSA classic clusters."
63
- )
64
- return False
65
-
66
- rosa = RosaSession(
67
- aws_account_id=cluster.aws.aws_account_id,
68
- aws_region=cluster.region.id,
69
- aws_iam_role=self.aws_iam_role,
70
- ocm_org_id=ocm_org_id,
71
- ocm_api=ocm_api,
72
- job_controller=self.job_controller,
73
- image=self.rosa_job_image,
74
- service_account=self.rosa_job_service_account,
75
- )
76
-
77
- try:
78
- # account role handling
79
- account_role_prefix = cluster.aws.account_role_prefix
80
- if not account_role_prefix:
81
- raise Exception(
82
- f"Can't upgrade account roles. Cluster {cluster.name} does not define spec.aws.account_role_prefix"
83
- )
84
- rosa.upgrade_account_roles(
85
- role_prefix=account_role_prefix,
86
- minor_version=gate.version_raw_id_prefix,
87
- channel_group=cluster.version.channel_group,
88
- dry_run=dry_run,
89
- )
90
-
91
- # operator role handling
92
- rosa.upgrade_operator_roles(
93
- cluster_id=cluster.id,
94
- dry_run=dry_run,
95
- )
96
- except RosaCliError as e:
97
- logging.error(f"Failed to upgrade roles for cluster {cluster.name}: {e}")
98
- e.write_logs_to_logger(logging.error)
99
- return False
100
33
  return True
@@ -7,7 +7,7 @@ from collections.abc import (
7
7
  from typing import Any
8
8
 
9
9
  import yaml
10
- from kubernetes.client import ( # type: ignore[attr-defined]
10
+ from kubernetes.client import (
11
11
  ApiClient,
12
12
  V1ConfigMap,
13
13
  V1ObjectMeta,
@@ -20,6 +20,7 @@ from reconcile.gql_definitions.automated_actions.instance import (
20
20
  AutomatedActionExternalResourceFlushElastiCacheV1,
21
21
  AutomatedActionExternalResourceRdsRebootV1,
22
22
  AutomatedActionExternalResourceRdsSnapshotV1,
23
+ AutomatedActionOpenshiftTriggerCronjobV1,
23
24
  AutomatedActionOpenshiftWorkloadDeleteV1,
24
25
  AutomatedActionOpenshiftWorkloadRestartArgumentV1,
25
26
  AutomatedActionOpenshiftWorkloadRestartV1,
@@ -82,7 +83,7 @@ class AutomatedActionsConfigIntegration(
82
83
  query_func = gql.get_api().query
83
84
  return {
84
85
  "automated_actions_instances": [
85
- c.dict() for c in self.get_automated_actions_instances(query_func)
86
+ c.model_dump() for c in self.get_automated_actions_instances(query_func)
86
87
  ]
87
88
  }
88
89
 
@@ -167,7 +168,7 @@ class AutomatedActionsConfigIntegration(
167
168
  case AutomatedActionActionListV1():
168
169
  # no special handling needed, just dump the values
169
170
  parameters.extend(
170
- arg.dict(exclude_none=True, exclude_defaults=True)
171
+ arg.model_dump(exclude_none=True, exclude_defaults=True)
171
172
  for arg in action.action_list_arguments or []
172
173
  )
173
174
  case AutomatedActionExternalResourceFlushElastiCacheV1():
@@ -205,6 +206,17 @@ class AutomatedActionsConfigIntegration(
205
206
  "account": f"^{rds_snapshot_er.provisioner.name}$",
206
207
  "identifier": rds_snapshot_arg.identifier,
207
208
  })
209
+ case AutomatedActionOpenshiftTriggerCronjobV1():
210
+ parameters.extend(
211
+ {
212
+ # all parameter values are regexes in the OPA policy
213
+ # therefore, cluster and namespace must be fixed to the current strings
214
+ "cluster": f"^{arg.namespace.cluster.name}$",
215
+ "namespace": f"^{arg.namespace.name}$",
216
+ "cronjob": arg.cronjob,
217
+ }
218
+ for arg in action.openshift_trigger_cronjob_arguments
219
+ )
208
220
  case AutomatedActionOpenshiftWorkloadDeleteV1():
209
221
  parameters.extend(
210
222
  {
@@ -257,7 +269,7 @@ class AutomatedActionsConfigIntegration(
257
269
  {
258
270
  "users": {user.username: sorted(user.roles) for user in users},
259
271
  "roles": {
260
- role: [policy.dict() for policy in policies]
272
+ role: [policy.model_dump() for policy in policies]
261
273
  for role, policies in roles.items()
262
274
  },
263
275
  },
@@ -1,5 +1,4 @@
1
1
  from collections.abc import Callable, Iterable
2
- from datetime import UTC, datetime
3
2
  from typing import Any
4
3
 
5
4
  import jinja2
@@ -26,6 +25,7 @@ from reconcile.typed_queries.gitlab_instances import get_gitlab_instances
26
25
  from reconcile.utils import gql, metrics
27
26
  from reconcile.utils.aws_api_typed.api import AWSApi, AWSStaticCredentials
28
27
  from reconcile.utils.aws_api_typed.iam import AWSAccessKey
28
+ from reconcile.utils.datetime_util import utc_now
29
29
  from reconcile.utils.defer import defer
30
30
  from reconcile.utils.disabled_integrations import integration_is_enabled
31
31
  from reconcile.utils.runtime.integration import (
@@ -42,14 +42,14 @@ QONTRACT_INTEGRATION_VERSION = make_semver(1, 0, 0)
42
42
 
43
43
 
44
44
  class AwsAccountMgmtIntegrationParams(PydanticRunParams):
45
- account_name: str | None
45
+ account_name: str | None = None
46
46
  flavor: str
47
47
  organization_account_role: str = "OrganizationAccountAccessRole"
48
48
  default_tags: dict[str, str] = {}
49
49
  initial_user_name: str = "terraform"
50
50
  initial_user_policy_arn: str = "arn:aws:iam::aws:policy/AdministratorAccess"
51
51
  initial_user_secret_vault_path: str = (
52
- "app-sre-v2/creds/terraform/{account_name}/config"
52
+ "app-sre-v2/creds/terraform/{account_name}/config" # noqa: RUF027
53
53
  )
54
54
  # To avoid the accidental deletion of the resource file, explicitly set the
55
55
  # qontract.cli option in the integration extraArgs!
@@ -76,9 +76,9 @@ class AwsAccountMgmtIntegration(
76
76
  query_func, account_name=self.params.account_name
77
77
  )
78
78
  return {
79
- "payer_accounts": [account.dict() for account in payer_accounts],
79
+ "payer_accounts": [account.model_dump() for account in payer_accounts],
80
80
  "non_organization_accounts": [
81
- account.dict() for account in non_organization_accounts
81
+ account.model_dump() for account in non_organization_accounts
82
82
  ],
83
83
  }
84
84
 
@@ -98,10 +98,10 @@ class AwsAccountMgmtIntegration(
98
98
  lstrip_blocks=True,
99
99
  keep_trailing_newline=True,
100
100
  ).render({
101
- "accountRequest": account_request.dict(by_alias=True),
101
+ "accountRequest": account_request.model_dump(by_alias=True),
102
102
  "uid": uid,
103
103
  "settings": settings,
104
- "timestamp": int(datetime.now(tz=UTC).timestamp()),
104
+ "timestamp": int(utc_now().timestamp()),
105
105
  })
106
106
  return tmpl
107
107
 
@@ -128,12 +128,24 @@ class AwsAccountMgmtIntegration(
128
128
  for payer_account in payer_accounts
129
129
  for org_account in payer_account.organization_accounts or []
130
130
  }
131
-
132
131
  non_organization_accounts = [
133
132
  account
134
133
  for account in all_aws_accounts
135
134
  if account.name not in all_organization_account_names
136
135
  ]
136
+
137
+ # check account requests for invalid emails
138
+ used_emails: set[str] = {
139
+ owner.email
140
+ for account in all_aws_accounts
141
+ for owner in account.account_owners
142
+ }
143
+ for payer_account in payer_accounts:
144
+ for account_request in payer_account.account_requests or []:
145
+ if account_request.account_owner.email in used_emails:
146
+ raise ValueError(
147
+ f"Invalid email for account request {account_request.name} in payer account {payer_account.name}. Email {account_request.account_owner.email} is already used."
148
+ )
137
149
  return payer_accounts, non_organization_accounts
138
150
 
139
151
  def save_access_key(self, account: str, access_key: AWSAccessKey) -> None:
@@ -187,7 +199,7 @@ class AwsAccountMgmtIntegration(
187
199
  template=account_template,
188
200
  account_request=account_request,
189
201
  uid=uid,
190
- settings=self.params.dict(),
202
+ settings=self.params.model_dump(),
191
203
  ),
192
204
  account_request_file_path=f"data/{account_request.path.strip('/')}",
193
205
  )
@@ -35,7 +35,7 @@ class Quota(Protocol):
35
35
  quota_code: str
36
36
  value: float
37
37
 
38
- def dict(self) -> dict[str, Any]: ...
38
+ def model_dump(self) -> dict[str, Any]: ...
39
39
 
40
40
 
41
41
  class Contact(Protocol):
@@ -44,7 +44,7 @@ class Contact(Protocol):
44
44
  email: str
45
45
  phone_number: str
46
46
 
47
- def dict(self) -> dict[str, Any]: ...
47
+ def model_dump(self) -> dict[str, Any]: ...
48
48
 
49
49
 
50
50
  class AWSReconciler:
@@ -167,7 +167,7 @@ class AWSReconciler:
167
167
  self, aws_api: AWSApi, name: str, quotas: Iterable[Quota]
168
168
  ) -> list[str] | None:
169
169
  """Request service quota changes."""
170
- quotas_dict = [q.dict() for q in quotas]
170
+ quotas_dict = [q.model_dump() for q in quotas]
171
171
  with self.state.transaction(
172
172
  state_key(name, TASK_REQUEST_SERVICE_QUOTA)
173
173
  ) as _state:
@@ -24,7 +24,7 @@ def validate(account: AWSAccountV1) -> bool:
24
24
  raise ExceptionGroup("Multiple quotas are referenced in the account", errors)
25
25
 
26
26
  if account.organization_accounts or account.account_requests:
27
- # it's payer account
27
+ # it's a "payer account"
28
28
  if not account.premium_support:
29
29
  raise ValueError(
30
30
  f"Premium support is required for payer account {account.name}"
@@ -26,6 +26,7 @@ from reconcile.typed_queries.app_interface_vault_settings import (
26
26
  )
27
27
  from reconcile.utils import gql
28
28
  from reconcile.utils.aws_api import AWSApi
29
+ from reconcile.utils.datetime_util import from_utc_iso_format, utc_now
29
30
  from reconcile.utils.defer import defer
30
31
  from reconcile.utils.parse_dhms_duration import dhms_to_seconds
31
32
  from reconcile.utils.secret_reader import create_secret_reader
@@ -39,15 +40,12 @@ QONTRACT_INTEGRATION = "aws_ami_cleanup"
39
40
  MANAGED_TAG = {"Key": "managed_by_integration", "Value": QONTRACT_INTEGRATION}
40
41
 
41
42
 
42
- class AWSAmi(BaseModel):
43
+ class AWSAmi(BaseModel, frozen=True):
43
44
  name: str
44
45
  image_id: str
45
46
  creation_date: datetime
46
47
  snapshot_ids: list[str]
47
48
 
48
- class Config:
49
- frozen = True
50
-
51
49
 
52
50
  def get_aws_amis_from_launch_templates(ec2_client: EC2Client) -> set[str]:
53
51
  amis = set()
@@ -77,7 +75,7 @@ def get_aws_amis(
77
75
  owner: str,
78
76
  regex: str,
79
77
  age_in_seconds: int,
80
- utc_now: datetime,
78
+ now: datetime,
81
79
  ) -> list[AWSAmi]:
82
80
  """Get amis that match regex older than given age"""
83
81
 
@@ -89,10 +87,8 @@ def get_aws_amis(
89
87
  if not re.search(pattern, image["Name"]):
90
88
  continue
91
89
 
92
- creation_date = datetime.strptime(
93
- image["CreationDate"], "%Y-%m-%dT%H:%M:%S.%fZ"
94
- )
95
- current_delta = utc_now - creation_date
90
+ creation_date = from_utc_iso_format(image["CreationDate"])
91
+ current_delta = now - creation_date
96
92
  delete_delta = timedelta(seconds=age_in_seconds)
97
93
 
98
94
  if current_delta < delete_delta:
@@ -135,7 +131,7 @@ def get_region(
135
131
 
136
132
  @defer
137
133
  def run(dry_run: bool, thread_pool_size: int, defer: Callable | None = None) -> None:
138
- utc_now = datetime.utcnow()
134
+ now = utc_now()
139
135
  gqlapi = gql.get_api()
140
136
  aws_accounts = aws_accounts_query(gqlapi.query).accounts
141
137
 
@@ -177,7 +173,7 @@ def run(dry_run: bool, thread_pool_size: int, defer: Callable | None = None) ->
177
173
  # Build AWSApi object. We will use all those accounts listed in ami_accounts since
178
174
  # we will also need to look for used AMIs.
179
175
  accounts_dicted = [
180
- account.dict(by_alias=True)
176
+ account.model_dump(by_alias=True)
181
177
  for account in aws_accounts or []
182
178
  if account.name in ami_accounts
183
179
  ]
@@ -222,7 +218,7 @@ def run(dry_run: bool, thread_pool_size: int, defer: Callable | None = None) ->
222
218
  owner=account.uid,
223
219
  regex=cleanup_config.regex,
224
220
  age_in_seconds=age_in_seconds,
225
- utc_now=utc_now,
221
+ now=now,
226
222
  )
227
223
 
228
224
  for ami in aws_amis:
@@ -1,17 +1,19 @@
1
1
  import logging
2
+ import re
2
3
  from collections.abc import (
3
- Callable,
4
4
  Iterable,
5
5
  Mapping,
6
6
  )
7
7
  from typing import Any
8
8
 
9
9
  from reconcile import queries
10
+ from reconcile.typed_queries.aws_account_tags import get_aws_account_tags
11
+ from reconcile.typed_queries.external_resources import get_settings
10
12
  from reconcile.utils.aws_api import AWSApi
11
- from reconcile.utils.defer import defer
12
13
 
13
14
  QONTRACT_INTEGRATION = "aws-ami-share"
14
- MANAGED_TAG = {"Key": "managed_by_integration", "Value": QONTRACT_INTEGRATION}
15
+
16
+ MANAGED_TAG = {"managed_by_integration": QONTRACT_INTEGRATION}
15
17
 
16
18
 
17
19
  def filter_accounts(accounts: Iterable[dict[str, Any]]) -> list[dict[str, Any]]:
@@ -37,65 +39,70 @@ def get_region(
37
39
  return region
38
40
 
39
41
 
40
- @defer
41
- def run(dry_run: bool, defer: Callable | None = None) -> None:
42
+ def share_ami(
43
+ dry_run: bool,
44
+ src_account: Mapping[str, Any],
45
+ share: Mapping[str, Any],
46
+ default_tags: dict[str, str],
47
+ aws_api: AWSApi,
48
+ ) -> None:
49
+ dst_account = share["account"]
50
+ regex = re.compile(share["regex"])
51
+ region = get_region(share, src_account, dst_account)
52
+ src_amis = aws_api.get_amis_details(src_account, src_account, regex, region)
53
+ dst_amis = aws_api.get_amis_details(dst_account, src_account, regex, region)
54
+
55
+ for ami_id, src_ami_tags in src_amis.items():
56
+ dst_ami_tags = dst_amis.get(ami_id)
57
+ if dst_ami_tags is None:
58
+ logging.info([
59
+ "share_ami",
60
+ src_account["name"],
61
+ dst_account["name"],
62
+ ami_id,
63
+ ])
64
+ if not dry_run:
65
+ aws_api.share_ami(src_account, dst_account["uid"], ami_id, region)
66
+ dst_account_tags = default_tags | get_aws_account_tags(
67
+ dst_account.get("organization", None)
68
+ )
69
+ desired_tags = src_ami_tags | dst_account_tags | MANAGED_TAG
70
+ current_tags = dst_ami_tags or {}
71
+
72
+ if desired_tags != current_tags:
73
+ logging.info([
74
+ "tag_shared_ami",
75
+ dst_account["name"],
76
+ ami_id,
77
+ desired_tags,
78
+ ])
79
+ if not dry_run:
80
+ aws_api.create_tags(dst_account, ami_id, desired_tags)
81
+
82
+
83
+ def run(dry_run: bool) -> None:
42
84
  accounts = queries.get_aws_accounts(sharing=True)
43
85
  sharing_accounts = filter_accounts(accounts)
44
86
  settings = queries.get_app_interface_settings()
45
- aws_api = AWSApi(1, sharing_accounts, settings=settings, init_users=False)
46
- if defer:
47
- defer(aws_api.cleanup)
48
-
49
- for src_account in sharing_accounts:
50
- sharing = src_account.get("sharing")
51
- if not sharing:
52
- continue
53
- for share in sharing:
54
- if share["provider"] != "ami":
55
- continue
56
- dst_account = share["account"]
57
- regex = share["regex"]
58
- region = get_region(share, src_account, dst_account)
59
- src_amis = aws_api.get_amis_details(src_account, src_account, regex, region)
60
- dst_amis = aws_api.get_amis_details(dst_account, src_account, regex, region)
61
-
62
- for src_ami in src_amis:
63
- src_ami_id = src_ami["image_id"]
64
- found_dst_amis = [d for d in dst_amis if d["image_id"] == src_ami_id]
65
- if not found_dst_amis:
66
- logging.info([
67
- "share_ami",
68
- src_account["name"],
69
- dst_account["name"],
70
- src_ami_id,
71
- ])
72
- if not dry_run:
73
- aws_api.share_ami(
74
- src_account, dst_account["uid"], src_ami_id, region
75
- )
76
- # we assume an unshared ami does not have tags
77
- found_dst_amis = [{"image_id": src_ami_id, "tags": []}]
78
-
79
- dst_ami = found_dst_amis[0]
80
- dst_ami_id = dst_ami["image_id"]
81
- dst_ami_tags = dst_ami["tags"]
82
- if MANAGED_TAG not in dst_ami_tags:
83
- logging.info([
84
- "tag_shared_ami",
85
- dst_account["name"],
86
- dst_ami_id,
87
- MANAGED_TAG,
88
- ])
89
- if not dry_run:
90
- aws_api.create_tag(dst_account, dst_ami_id, MANAGED_TAG)
91
- src_ami_tags = src_ami["tags"]
92
- for src_tag in src_ami_tags:
93
- if src_tag not in dst_ami_tags:
94
- logging.info([
95
- "tag_shared_ami",
96
- dst_account["name"],
97
- dst_ami_id,
98
- src_tag,
99
- ])
100
- if not dry_run:
101
- aws_api.create_tag(dst_account, dst_ami_id, src_tag)
87
+ try:
88
+ default_tags = get_settings().default_tags
89
+ except ValueError:
90
+ # no external resources settings found
91
+ default_tags = {}
92
+
93
+ with AWSApi(
94
+ 1,
95
+ sharing_accounts,
96
+ settings=settings,
97
+ init_users=False,
98
+ ) as aws_api:
99
+ for src_account in sharing_accounts:
100
+ for share in src_account.get("sharing") or []:
101
+ if share["provider"] == "ami":
102
+ share_ami(
103
+ dry_run=dry_run,
104
+ src_account=src_account,
105
+ share=share,
106
+ default_tags=default_tags,
107
+ aws_api=aws_api,
108
+ )