qontract-reconcile 0.10.2.dev299__py3-none-any.whl → 0.10.2.dev430__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 (403) hide show
  1. {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/METADATA +13 -12
  2. {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/RECORD +399 -394
  3. reconcile/acs_rbac.py +2 -2
  4. reconcile/aus/advanced_upgrade_service.py +18 -12
  5. reconcile/aus/base.py +134 -32
  6. reconcile/aus/cluster_version_data.py +15 -5
  7. reconcile/aus/models.py +3 -1
  8. reconcile/aus/ocm_addons_upgrade_scheduler_org.py +1 -0
  9. reconcile/aus/ocm_upgrade_scheduler.py +8 -1
  10. reconcile/aus/ocm_upgrade_scheduler_org.py +20 -5
  11. reconcile/aus/version_gates/sts_version_gate_handler.py +54 -1
  12. reconcile/automated_actions/config/integration.py +16 -4
  13. reconcile/aws_account_manager/integration.py +8 -8
  14. reconcile/aws_account_manager/reconciler.py +3 -3
  15. reconcile/aws_ami_cleanup/integration.py +8 -12
  16. reconcile/aws_ami_share.py +69 -62
  17. reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
  18. reconcile/aws_ecr_image_pull_secrets.py +4 -4
  19. reconcile/aws_iam_keys.py +1 -0
  20. reconcile/aws_saml_idp/integration.py +12 -4
  21. reconcile/aws_saml_roles/integration.py +32 -25
  22. reconcile/aws_version_sync/integration.py +125 -84
  23. reconcile/change_owners/bundle.py +3 -3
  24. reconcile/change_owners/change_log_tracking.py +3 -2
  25. reconcile/change_owners/change_owners.py +1 -1
  26. reconcile/change_owners/diff.py +2 -4
  27. reconcile/checkpoint.py +12 -4
  28. reconcile/cli.py +111 -18
  29. reconcile/cluster_deployment_mapper.py +2 -3
  30. reconcile/dashdotdb_dora.py +5 -12
  31. reconcile/dashdotdb_slo.py +1 -1
  32. reconcile/database_access_manager.py +125 -121
  33. reconcile/deadmanssnitch.py +1 -5
  34. reconcile/dynatrace_token_provider/integration.py +1 -1
  35. reconcile/endpoints_discovery/integration.py +4 -1
  36. reconcile/endpoints_discovery/merge_request.py +1 -1
  37. reconcile/endpoints_discovery/merge_request_manager.py +9 -11
  38. reconcile/external_resources/factories.py +5 -12
  39. reconcile/external_resources/integration.py +1 -1
  40. reconcile/external_resources/manager.py +8 -5
  41. reconcile/external_resources/meta.py +0 -1
  42. reconcile/external_resources/metrics.py +1 -1
  43. reconcile/external_resources/model.py +20 -20
  44. reconcile/external_resources/reconciler.py +7 -4
  45. reconcile/external_resources/secrets_sync.py +8 -11
  46. reconcile/external_resources/state.py +26 -16
  47. reconcile/fleet_labeler/integration.py +1 -1
  48. reconcile/gabi_authorized_users.py +8 -5
  49. reconcile/gcp_image_mirror.py +2 -2
  50. reconcile/github_org.py +1 -1
  51. reconcile/github_owners.py +4 -0
  52. reconcile/gitlab_housekeeping.py +13 -15
  53. reconcile/gitlab_members.py +6 -12
  54. reconcile/gitlab_mr_sqs_consumer.py +2 -2
  55. reconcile/gitlab_owners.py +15 -11
  56. reconcile/gitlab_permissions.py +8 -12
  57. reconcile/glitchtip_project_alerts/integration.py +3 -1
  58. reconcile/gql_definitions/acs/acs_instances.py +10 -10
  59. reconcile/gql_definitions/acs/acs_policies.py +5 -5
  60. reconcile/gql_definitions/acs/acs_rbac.py +6 -6
  61. reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +32 -32
  62. reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +26 -26
  63. reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +6 -7
  64. reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
  65. reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
  66. reconcile/gql_definitions/automated_actions/instance.py +51 -12
  67. reconcile/gql_definitions/aws_account_manager/aws_accounts.py +11 -11
  68. reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +20 -10
  69. reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +28 -68
  70. reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +20 -10
  71. reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +20 -10
  72. reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
  73. reconcile/gql_definitions/aws_version_sync/clusters.py +10 -10
  74. reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
  75. reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
  76. reconcile/gql_definitions/change_owners/queries/self_service_roles.py +9 -9
  77. reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +18 -18
  78. reconcile/gql_definitions/common/alerting_services_settings.py +9 -9
  79. reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
  80. reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
  81. reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
  82. reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
  83. reconcile/gql_definitions/common/app_interface_roles.py +120 -0
  84. reconcile/gql_definitions/common/app_interface_state_settings.py +10 -10
  85. reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
  86. reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
  87. reconcile/gql_definitions/common/apps.py +5 -5
  88. reconcile/gql_definitions/common/aws_vpc_requests.py +23 -10
  89. reconcile/gql_definitions/common/aws_vpcs.py +11 -11
  90. reconcile/gql_definitions/common/clusters.py +37 -35
  91. reconcile/gql_definitions/common/clusters_minimal.py +14 -14
  92. reconcile/gql_definitions/common/clusters_with_dms.py +6 -6
  93. reconcile/gql_definitions/common/clusters_with_peering.py +29 -30
  94. reconcile/gql_definitions/common/github_orgs.py +10 -10
  95. reconcile/gql_definitions/common/jira_settings.py +10 -10
  96. reconcile/gql_definitions/common/jiralert_settings.py +5 -5
  97. reconcile/gql_definitions/common/ldap_settings.py +5 -5
  98. reconcile/gql_definitions/common/namespaces.py +42 -44
  99. reconcile/gql_definitions/common/namespaces_minimal.py +15 -13
  100. reconcile/gql_definitions/common/ocm_env_telemeter.py +12 -12
  101. reconcile/gql_definitions/common/ocm_environments.py +19 -19
  102. reconcile/gql_definitions/common/pagerduty_instances.py +9 -9
  103. reconcile/gql_definitions/common/pgp_reencryption_settings.py +6 -6
  104. reconcile/gql_definitions/common/pipeline_providers.py +29 -29
  105. reconcile/gql_definitions/common/quay_instances.py +5 -5
  106. reconcile/gql_definitions/common/quay_orgs.py +5 -5
  107. reconcile/gql_definitions/common/reserved_networks.py +5 -5
  108. reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
  109. reconcile/gql_definitions/common/saas_files.py +44 -44
  110. reconcile/gql_definitions/common/saas_target_namespaces.py +10 -10
  111. reconcile/gql_definitions/common/saasherder_settings.py +5 -5
  112. reconcile/gql_definitions/common/slack_workspaces.py +5 -5
  113. reconcile/gql_definitions/common/smtp_client_settings.py +19 -19
  114. reconcile/gql_definitions/common/state_aws_account.py +7 -8
  115. reconcile/gql_definitions/common/users.py +5 -5
  116. reconcile/gql_definitions/common/users_with_paths.py +5 -5
  117. reconcile/gql_definitions/cost_report/app_names.py +5 -5
  118. reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
  119. reconcile/gql_definitions/cost_report/settings.py +9 -9
  120. reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +43 -43
  121. reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +10 -10
  122. reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
  123. reconcile/gql_definitions/email_sender/apps.py +5 -5
  124. reconcile/gql_definitions/email_sender/emails.py +8 -8
  125. reconcile/gql_definitions/email_sender/users.py +6 -6
  126. reconcile/gql_definitions/endpoints_discovery/apps.py +10 -10
  127. reconcile/gql_definitions/external_resources/aws_accounts.py +9 -9
  128. reconcile/gql_definitions/external_resources/external_resources_modules.py +23 -23
  129. reconcile/gql_definitions/external_resources/external_resources_namespaces.py +492 -410
  130. reconcile/gql_definitions/external_resources/external_resources_settings.py +28 -26
  131. reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
  132. reconcile/gql_definitions/fleet_labeler/fleet_labels.py +40 -40
  133. reconcile/gql_definitions/fragments/aus_organization.py +5 -5
  134. reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
  135. reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
  136. reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
  137. reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
  138. reconcile/gql_definitions/fragments/{aws_vpc_request_subnet.py → aws_organization.py} +12 -8
  139. reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
  140. reconcile/gql_definitions/fragments/aws_vpc_request.py +10 -5
  141. reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
  142. reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
  143. reconcile/gql_definitions/fragments/disable.py +5 -5
  144. reconcile/gql_definitions/fragments/email_service.py +5 -5
  145. reconcile/gql_definitions/fragments/email_user.py +5 -5
  146. reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
  147. reconcile/gql_definitions/fragments/membership_source.py +5 -5
  148. reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
  149. reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
  150. reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
  151. reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
  152. reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
  153. reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
  154. reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
  155. reconcile/gql_definitions/fragments/resource_values.py +5 -5
  156. reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
  157. reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
  158. reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
  159. reconcile/gql_definitions/fragments/terraform_state.py +5 -5
  160. reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
  161. reconcile/gql_definitions/fragments/user.py +5 -5
  162. reconcile/gql_definitions/fragments/vault_secret.py +5 -5
  163. reconcile/gql_definitions/gcp/gcp_docker_repos.py +9 -9
  164. reconcile/gql_definitions/gcp/gcp_projects.py +9 -9
  165. reconcile/gql_definitions/gitlab_members/gitlab_instances.py +9 -9
  166. reconcile/gql_definitions/gitlab_members/permissions.py +9 -9
  167. reconcile/gql_definitions/glitchtip/glitchtip_instance.py +9 -9
  168. reconcile/gql_definitions/glitchtip/glitchtip_project.py +11 -11
  169. reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +9 -9
  170. reconcile/gql_definitions/integrations/integrations.py +48 -51
  171. reconcile/gql_definitions/introspection.json +3050 -1393
  172. reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +11 -11
  173. reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +10 -10
  174. reconcile/gql_definitions/jira/jira_servers.py +5 -5
  175. reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +14 -10
  176. reconcile/gql_definitions/jumphosts/jumphosts.py +13 -13
  177. reconcile/gql_definitions/ldap_groups/roles.py +5 -5
  178. reconcile/gql_definitions/ldap_groups/settings.py +9 -9
  179. reconcile/gql_definitions/maintenance/maintenances.py +5 -5
  180. reconcile/gql_definitions/membershipsources/roles.py +5 -5
  181. reconcile/gql_definitions/ocm_labels/clusters.py +18 -19
  182. reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
  183. reconcile/gql_definitions/openshift_cluster_bots/clusters.py +22 -22
  184. reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
  185. reconcile/gql_definitions/openshift_groups/managed_roles.py +6 -6
  186. reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +10 -10
  187. reconcile/gql_definitions/quay_membership/quay_membership.py +6 -6
  188. reconcile/gql_definitions/rhcs/certs.py +33 -87
  189. reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +43 -0
  190. reconcile/gql_definitions/rhidp/organizations.py +18 -18
  191. reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
  192. reconcile/gql_definitions/service_dependencies/service_dependencies.py +8 -8
  193. reconcile/gql_definitions/sharding/aws_accounts.py +10 -10
  194. reconcile/gql_definitions/sharding/ocm_organization.py +8 -8
  195. reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
  196. reconcile/gql_definitions/skupper_network/skupper_networks.py +10 -10
  197. reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
  198. reconcile/gql_definitions/slack_usergroups/permissions.py +9 -9
  199. reconcile/gql_definitions/slack_usergroups/users.py +5 -5
  200. reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
  201. reconcile/gql_definitions/status_board/status_board.py +6 -7
  202. reconcile/gql_definitions/statuspage/statuspages.py +9 -9
  203. reconcile/gql_definitions/templating/template_collection.py +5 -5
  204. reconcile/gql_definitions/templating/templates.py +5 -5
  205. reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +6 -6
  206. reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +11 -11
  207. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +11 -11
  208. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +20 -25
  209. reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +6 -6
  210. reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +12 -12
  211. reconcile/gql_definitions/terraform_init/aws_accounts.py +23 -9
  212. reconcile/gql_definitions/terraform_repo/terraform_repo.py +9 -9
  213. reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
  214. reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +448 -402
  215. reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +23 -17
  216. reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +9 -9
  217. reconcile/gql_definitions/vault_instances/vault_instances.py +61 -61
  218. reconcile/gql_definitions/vault_policies/vault_policies.py +11 -11
  219. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +8 -8
  220. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
  221. reconcile/integrations_manager.py +3 -3
  222. reconcile/jenkins_job_builder.py +1 -1
  223. reconcile/jenkins_worker_fleets.py +80 -11
  224. reconcile/jira_permissions_validator.py +237 -122
  225. reconcile/ldap_groups/integration.py +1 -1
  226. reconcile/ocm/types.py +35 -56
  227. reconcile/ocm_aws_infrastructure_access.py +1 -1
  228. reconcile/ocm_clusters.py +4 -4
  229. reconcile/ocm_labels/integration.py +3 -2
  230. reconcile/ocm_machine_pools.py +33 -27
  231. reconcile/openshift_base.py +122 -10
  232. reconcile/openshift_cluster_bots.py +5 -5
  233. reconcile/openshift_groups.py +5 -0
  234. reconcile/openshift_limitranges.py +1 -1
  235. reconcile/openshift_namespace_labels.py +1 -1
  236. reconcile/openshift_namespaces.py +97 -101
  237. reconcile/openshift_resources_base.py +10 -5
  238. reconcile/openshift_rhcs_certs.py +77 -40
  239. reconcile/openshift_rolebindings.py +230 -130
  240. reconcile/openshift_saas_deploy.py +6 -7
  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 +8 -7
  244. reconcile/openshift_tekton_resources.py +1 -1
  245. reconcile/openshift_upgrade_watcher.py +4 -4
  246. reconcile/openshift_users.py +5 -3
  247. reconcile/oum/labelset.py +5 -3
  248. reconcile/oum/models.py +1 -4
  249. reconcile/oum/providers.py +1 -1
  250. reconcile/prometheus_rules_tester/integration.py +4 -4
  251. reconcile/quay_mirror.py +1 -1
  252. reconcile/queries.py +131 -0
  253. reconcile/requests_sender.py +8 -3
  254. reconcile/resource_scraper.py +1 -5
  255. reconcile/rhidp/common.py +5 -5
  256. reconcile/rhidp/sso_client/base.py +19 -10
  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/sendgrid_teammates.py +20 -9
  260. reconcile/skupper_network/integration.py +2 -2
  261. reconcile/slack_usergroups.py +35 -14
  262. reconcile/sql_query.py +1 -0
  263. reconcile/status.py +2 -2
  264. reconcile/status_board.py +6 -6
  265. reconcile/statuspage/atlassian.py +7 -7
  266. reconcile/statuspage/integrations/maintenances.py +4 -3
  267. reconcile/statuspage/page.py +4 -9
  268. reconcile/statuspage/status.py +5 -8
  269. reconcile/templates/rosa-classic-cluster-creation.sh.j2 +4 -0
  270. reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +3 -0
  271. reconcile/templating/lib/merge_request_manager.py +2 -2
  272. reconcile/templating/lib/rendering.py +3 -3
  273. reconcile/templating/renderer.py +12 -13
  274. reconcile/terraform_aws_route53.py +18 -8
  275. reconcile/terraform_cloudflare_dns.py +3 -3
  276. reconcile/terraform_cloudflare_resources.py +12 -13
  277. reconcile/terraform_cloudflare_users.py +3 -2
  278. reconcile/terraform_init/integration.py +187 -23
  279. reconcile/terraform_repo.py +16 -12
  280. reconcile/terraform_resources.py +18 -10
  281. reconcile/terraform_tgw_attachments.py +27 -19
  282. reconcile/terraform_users.py +29 -21
  283. reconcile/terraform_vpc_peerings.py +16 -4
  284. reconcile/terraform_vpc_resources/integration.py +32 -2
  285. reconcile/typed_queries/app_interface_roles.py +10 -0
  286. reconcile/typed_queries/aws_account_tags.py +41 -0
  287. reconcile/typed_queries/cost_report/app_names.py +1 -1
  288. reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
  289. reconcile/typed_queries/saas_files.py +13 -13
  290. reconcile/typed_queries/status_board.py +2 -2
  291. reconcile/unleash_feature_toggles/integration.py +4 -2
  292. reconcile/utils/acs/base.py +6 -3
  293. reconcile/utils/acs/policies.py +2 -2
  294. reconcile/utils/aggregated_list.py +4 -3
  295. reconcile/utils/aws_api.py +51 -20
  296. reconcile/utils/aws_api_typed/api.py +38 -9
  297. reconcile/utils/aws_api_typed/cloudformation.py +149 -0
  298. reconcile/utils/aws_api_typed/logs.py +73 -0
  299. reconcile/utils/aws_api_typed/organization.py +4 -2
  300. reconcile/utils/binary.py +7 -12
  301. reconcile/utils/datetime_util.py +67 -0
  302. reconcile/utils/deadmanssnitch_api.py +1 -1
  303. reconcile/utils/differ.py +2 -3
  304. reconcile/utils/early_exit_cache.py +11 -12
  305. reconcile/utils/expiration.py +7 -3
  306. reconcile/utils/external_resource_spec.py +24 -1
  307. reconcile/utils/filtering.py +1 -1
  308. reconcile/utils/gitlab_api.py +7 -5
  309. reconcile/utils/glitchtip/client.py +6 -2
  310. reconcile/utils/glitchtip/models.py +25 -28
  311. reconcile/utils/gpg.py +5 -3
  312. reconcile/utils/gql.py +4 -7
  313. reconcile/utils/helm.py +2 -1
  314. reconcile/utils/helpers.py +1 -1
  315. reconcile/utils/imap_client.py +1 -1
  316. reconcile/utils/instrumented_wrappers.py +1 -1
  317. reconcile/utils/internal_groups/client.py +2 -2
  318. reconcile/utils/internal_groups/models.py +8 -17
  319. reconcile/utils/jenkins_api.py +24 -1
  320. reconcile/utils/jinja2/utils.py +6 -8
  321. reconcile/utils/jira_client.py +82 -63
  322. reconcile/utils/jjb_client.py +59 -43
  323. reconcile/utils/jobcontroller/controller.py +2 -2
  324. reconcile/utils/jobcontroller/models.py +17 -1
  325. reconcile/utils/json.py +74 -0
  326. reconcile/utils/ldap_client.py +4 -3
  327. reconcile/utils/lean_terraform_client.py +3 -1
  328. reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
  329. reconcile/utils/membershipsources/models.py +16 -23
  330. reconcile/utils/membershipsources/resolver.py +4 -2
  331. reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
  332. reconcile/utils/merge_request_manager/parser.py +6 -6
  333. reconcile/utils/metrics.py +5 -5
  334. reconcile/utils/models.py +304 -82
  335. reconcile/utils/mr/__init__.py +3 -1
  336. reconcile/utils/mr/app_interface_reporter.py +6 -3
  337. reconcile/utils/mr/aws_access.py +1 -1
  338. reconcile/utils/mr/base.py +7 -13
  339. reconcile/utils/mr/clusters_updates.py +4 -2
  340. reconcile/utils/mr/notificator.py +3 -3
  341. reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py +4 -1
  342. reconcile/utils/mr/promote_qontract.py +28 -12
  343. reconcile/utils/mr/update_access_report_base.py +3 -4
  344. reconcile/utils/mr/user_maintenance.py +7 -6
  345. reconcile/utils/oc.py +445 -336
  346. reconcile/utils/oc_filters.py +3 -3
  347. reconcile/utils/ocm/addons.py +0 -1
  348. reconcile/utils/ocm/base.py +27 -20
  349. reconcile/utils/ocm/cluster_groups.py +1 -1
  350. reconcile/utils/ocm/identity_providers.py +2 -2
  351. reconcile/utils/ocm/labels.py +1 -1
  352. reconcile/utils/ocm/ocm.py +81 -71
  353. reconcile/utils/ocm/products.py +9 -3
  354. reconcile/utils/ocm/search_filters.py +3 -6
  355. reconcile/utils/ocm/service_log.py +4 -6
  356. reconcile/utils/ocm/sre_capability_labels.py +20 -13
  357. reconcile/utils/ocm_base_client.py +4 -4
  358. reconcile/utils/openshift_resource.py +83 -52
  359. reconcile/utils/openssl.py +2 -2
  360. reconcile/utils/output.py +3 -2
  361. reconcile/utils/pagerduty_api.py +10 -7
  362. reconcile/utils/promotion_state.py +6 -11
  363. reconcile/utils/raw_github_api.py +11 -8
  364. reconcile/utils/repo_owners.py +21 -29
  365. reconcile/utils/rhcsv2_certs.py +138 -35
  366. reconcile/utils/rosa/session.py +16 -0
  367. reconcile/utils/runtime/integration.py +2 -3
  368. reconcile/utils/runtime/meta.py +2 -1
  369. reconcile/utils/runtime/runner.py +2 -2
  370. reconcile/utils/saasherder/interfaces.py +13 -20
  371. reconcile/utils/saasherder/models.py +25 -21
  372. reconcile/utils/saasherder/saasherder.py +60 -32
  373. reconcile/utils/secret_reader.py +6 -6
  374. reconcile/utils/sharding.py +1 -1
  375. reconcile/utils/slack_api.py +26 -4
  376. reconcile/utils/sloth.py +224 -0
  377. reconcile/utils/sqs_gateway.py +16 -11
  378. reconcile/utils/state.py +2 -1
  379. reconcile/utils/structs.py +4 -4
  380. reconcile/utils/terraform_client.py +32 -29
  381. reconcile/utils/terrascript_aws_client.py +658 -480
  382. reconcile/utils/three_way_diff_strategy.py +1 -1
  383. reconcile/utils/throughput.py +1 -1
  384. reconcile/utils/unleash/server.py +2 -8
  385. reconcile/utils/vault.py +44 -41
  386. reconcile/utils/vcs.py +8 -8
  387. reconcile/vault_replication.py +119 -58
  388. reconcile/vpc_peerings_validator.py +2 -2
  389. tools/app_interface_reporter.py +4 -4
  390. tools/cli_commands/cost_report/cost_management_api.py +3 -3
  391. tools/cli_commands/cost_report/view.py +7 -6
  392. tools/cli_commands/erv2.py +1 -1
  393. tools/cli_commands/gpg_encrypt.py +4 -1
  394. tools/cli_commands/systems_and_tools.py +5 -1
  395. tools/qontract_cli.py +36 -21
  396. tools/sre_checkpoints/util.py +5 -3
  397. tools/template_validation.py +3 -1
  398. reconcile/gql_definitions/ocm_oidc_idp/__init__.py +0 -0
  399. reconcile/gql_definitions/ocm_subscription_labels/__init__.py +0 -0
  400. reconcile/jenkins/__init__.py +0 -0
  401. reconcile/jenkins/types.py +0 -77
  402. {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/WHEEL +0 -0
  403. {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/entry_points.txt +0 -0
@@ -4,6 +4,7 @@ from typing import Any
4
4
 
5
5
  from pydantic import BaseModel
6
6
 
7
+ from reconcile.utils.json import json_dumps
7
8
  from tools.cli_commands.cost_report.model import OptimizationReport, Report
8
9
 
9
10
  LAYOUT = """\
@@ -244,7 +245,7 @@ def render_summary(
244
245
  return template.format(
245
246
  date=get_date(reports),
246
247
  total_cost=format_cost_value(total_cost),
247
- json_table=json_table.json(indent=2),
248
+ json_table=json_dumps(json_table, indent=2, mode="python"),
248
249
  )
249
250
 
250
251
 
@@ -274,7 +275,7 @@ def render_month_over_month_change(reports: Mapping[str, Report]) -> str:
274
275
  )
275
276
  return MONTH_OVER_MONTH_CHANGE.format(
276
277
  date=get_date(reports),
277
- json_table=json_table.json(indent=2),
278
+ json_table=json_dumps(json_table, indent=2, mode="python"),
278
279
  )
279
280
 
280
281
 
@@ -304,7 +305,7 @@ def render_aws_services_cost(
304
305
  items_total=format_cost_value(report.items_total),
305
306
  items_delta_value=format_delta_value(report.items_delta_value),
306
307
  items_delta_percent=format_delta_percent(report.items_delta_percent),
307
- json_table=json_table.json(indent=2),
308
+ json_table=json_dumps(json_table, indent=2, mode="python"),
308
309
  )
309
310
 
310
311
 
@@ -316,7 +317,7 @@ def render_openshift_workloads_cost(
316
317
  items_total=format_cost_value(report.items_total),
317
318
  items_delta_value=format_delta_value(report.items_delta_value),
318
319
  items_delta_percent=format_delta_percent(report.items_delta_percent),
319
- json_table=json_table.json(indent=2),
320
+ json_table=json_dumps(json_table, indent=2, mode="python"),
320
321
  )
321
322
 
322
323
 
@@ -362,7 +363,7 @@ def render_child_apps_cost(report: Report) -> str:
362
363
  )
363
364
  return CHILD_APPS_COST.format(
364
365
  child_apps_total=format_cost_value(report.child_apps_total),
365
- json_table=json_table.json(indent=2),
366
+ json_table=json_dumps(json_table, indent=2, mode="python"),
366
367
  )
367
368
 
368
369
 
@@ -509,7 +510,7 @@ def render_optimization(
509
510
  )
510
511
  return OPTIMIZATION.format(
511
512
  app_name=report.app_name,
512
- json_table=json_table.json(indent=2),
513
+ json_table=json_dumps(json_table, indent=2, mode="python"),
513
514
  )
514
515
 
515
516
 
@@ -133,7 +133,7 @@ class Erv2Cli:
133
133
 
134
134
  @property
135
135
  def input_data(self) -> str:
136
- return self._resource.json(exclude={"data": {FLAG_RESOURCE_MANAGED_BY_ERV2}})
136
+ return self._resource.export(exclude={"data": {FLAG_RESOURCE_MANAGED_BY_ERV2}})
137
137
 
138
138
  @property
139
139
  def image(self) -> str:
@@ -5,6 +5,7 @@ from dataclasses import dataclass
5
5
  from typing import TYPE_CHECKING
6
6
 
7
7
  from reconcile import queries
8
+ from reconcile.openshift_base import OCLogMsg
8
9
  from reconcile.utils import (
9
10
  config,
10
11
  gpg,
@@ -80,6 +81,8 @@ class GPGEncryptCommand:
80
81
  init_projects=False,
81
82
  )
82
83
  oc = oc_map.get(cluster_name)
84
+ if isinstance(oc, OCLogMsg):
85
+ raise oc
83
86
  data = oc.get(namespace, "Secret", name=secret, allow_not_found=False)[
84
87
  "data"
85
88
  ]
@@ -114,7 +117,7 @@ class GPGEncryptCommand:
114
117
  f"No argument given which defines how to fetch the secret {self._command_data}"
115
118
  )
116
119
 
117
- def _get_gpg_key(self) -> str | None:
120
+ def _get_gpg_key(self) -> str:
118
121
  target_user = self._command_data.target_user
119
122
  users = queries.get_users_by(
120
123
  refs=False,
@@ -132,6 +132,7 @@ from reconcile.typed_queries.vault import get_vault_instances
132
132
  from reconcile.utils import (
133
133
  gql,
134
134
  )
135
+ from reconcile.utils.slack_api import is_gov_slack_workspace
135
136
 
136
137
 
137
138
  class SystemTool(BaseModel):
@@ -322,11 +323,14 @@ class SystemTool(BaseModel):
322
323
 
323
324
  @classmethod
324
325
  def init_from_slack_workspace(cls, s: SlackWorkspaceV1, enumeration: Any) -> Self:
326
+ # Automatically determine the correct Slack domain based on GOV_SLACK environment variable
327
+ domain = "slack-gov.com" if is_gov_slack_workspace() else "slack.com"
328
+
325
329
  return cls(
326
330
  system_type="slack",
327
331
  system_id=s.name,
328
332
  name=s.name,
329
- url=f"https://{s.name}.slack.com",
333
+ url=f"https://{s.name}.{domain}",
330
334
  description=s.description,
331
335
  enumeration=enumeration,
332
336
  )
tools/qontract_cli.py CHANGED
@@ -13,7 +13,6 @@ import tempfile
13
13
  import textwrap
14
14
  from collections import defaultdict
15
15
  from datetime import (
16
- UTC,
17
16
  datetime,
18
17
  timedelta,
19
18
  )
@@ -122,6 +121,7 @@ from reconcile.utils.binary import (
122
121
  binary,
123
122
  binary_version,
124
123
  )
124
+ from reconcile.utils.datetime_util import from_utc_iso_format, utc_now
125
125
  from reconcile.utils.early_exit_cache import (
126
126
  CacheKey,
127
127
  CacheKeyWithDigest,
@@ -141,6 +141,7 @@ from reconcile.utils.gitlab_api import (
141
141
  )
142
142
  from reconcile.utils.glitchtip.client import GlitchtipClient
143
143
  from reconcile.utils.gql import GqlApiSingleton
144
+ from reconcile.utils.json import json_dumps
144
145
  from reconcile.utils.keycloak import (
145
146
  KeycloakAPI,
146
147
  SSOClient,
@@ -417,8 +418,8 @@ def get_upgrade_policies_data(
417
418
  upgrade_next_run = None
418
419
  upgrade_emoji = "💫"
419
420
  if upgrade_next_run:
420
- dt = datetime.strptime(upgrade_next_run, "%Y-%m-%dT%H:%M:%SZ")
421
- now = datetime.utcnow()
421
+ dt = from_utc_iso_format(upgrade_next_run)
422
+ now = utc_now()
422
423
  if dt > now:
423
424
  upgrade_emoji = "⏰"
424
425
  hours_ago = (now - dt).total_seconds() / 3600
@@ -841,7 +842,7 @@ def alert_report(
841
842
  )
842
843
  sys.exit(1)
843
844
 
844
- now = datetime.utcnow()
845
+ now = utc_now()
845
846
  from_timestamp = int((now - timedelta(days=days)).timestamp())
846
847
  to_timestamp = int(now.timestamp())
847
848
 
@@ -887,7 +888,9 @@ def alert_report(
887
888
  "Triggered": str(data.triggered_alerts),
888
889
  "Resolved": str(data.resolved_alerts),
889
890
  "Median time to resolve (h:mm:ss)": median_elapsed,
890
- "Response Rate": f"{data.responsed_alerts / data.triggered_alerts * 100:.2f}%",
891
+ "Response Rate": f"{data.responsed_alerts / data.triggered_alerts * 100:.2f}%"
892
+ if data.triggered_alerts != 0
893
+ else "0.00%",
891
894
  })
892
895
 
893
896
  # TODO(mafriedm, rporres): Fix this
@@ -1299,7 +1302,7 @@ def user_credentials_migrate_output(ctx: click.Context, account_name: str) -> No
1299
1302
  skip_accounts, appsre_pgp_key, _ = tfu.get_reencrypt_settings()
1300
1303
 
1301
1304
  accounts, working_dirs, _, aws_api = tfu.setup(
1302
- False,
1305
+ None,
1303
1306
  1,
1304
1307
  skip_accounts,
1305
1308
  account_name=account_name,
@@ -1564,7 +1567,7 @@ def rosa_create_cluster_command(ctx: click.Context, cluster_name: str) -> None:
1564
1567
  billing_account = account.billing_account.uid
1565
1568
  else:
1566
1569
  with AWSApi(
1567
- 1, [account.dict(by_alias=True)], settings=settings, init_users=False
1570
+ 1, [account.model_dump(by_alias=True)], settings=settings, init_users=False
1568
1571
  ) as aws_api:
1569
1572
  billing_account = aws_api.get_organization_billing_account(account.name)
1570
1573
 
@@ -1748,7 +1751,7 @@ def aws_terraform_resources(ctx: click.Context) -> None:
1748
1751
  for ns_info in namespaces:
1749
1752
  specs = (
1750
1753
  get_external_resource_specs(
1751
- ns_info.dict(by_alias=True), provision_provider=PROVIDER_AWS
1754
+ ns_info.model_dump(by_alias=True), provision_provider=PROVIDER_AWS
1752
1755
  )
1753
1756
  or []
1754
1757
  )
@@ -1806,7 +1809,7 @@ def rds(ctx: click.Context) -> None:
1806
1809
  specs = [
1807
1810
  s
1808
1811
  for s in get_external_resource_specs(
1809
- namespace.dict(by_alias=True), provision_provider=PROVIDER_AWS
1812
+ namespace.model_dump(by_alias=True), provision_provider=PROVIDER_AWS
1810
1813
  )
1811
1814
  if s.provider == "rds"
1812
1815
  ]
@@ -2184,6 +2187,8 @@ def root_owner(
2184
2187
  init_api_resources=True,
2185
2188
  )
2186
2189
  oc = oc_map.get(cluster)
2190
+ if isinstance(oc, OCLogMsg):
2191
+ raise oc
2187
2192
  obj = oc.get(namespace, kind, name)
2188
2193
  root_owner = oc.get_obj_root_owner(
2189
2194
  namespace, obj, allow_not_found=True, allow_not_controller=True
@@ -2196,7 +2201,7 @@ def root_owner(
2196
2201
  if ctx.obj["options"]["output"] != "json":
2197
2202
  ctx.obj["options"]["output"] = "yaml"
2198
2203
 
2199
- print_output(ctx.obj["options"], root_owner)
2204
+ print_output(ctx.obj["options"], root_owner) # type: ignore
2200
2205
 
2201
2206
 
2202
2207
  @get.command()
@@ -2270,7 +2275,7 @@ def app_interface_merge_queue(ctx: click.Context) -> None:
2270
2275
  "labels",
2271
2276
  ]
2272
2277
  merge_queue_data = []
2273
- now = datetime.utcnow()
2278
+ now = utc_now()
2274
2279
  for mr in merge_requests:
2275
2280
  item = {
2276
2281
  "id": f"[{mr['mr'].iid}]({mr['mr'].web_url})",
@@ -2279,7 +2284,7 @@ def app_interface_merge_queue(ctx: click.Context) -> None:
2279
2284
  + 1, # adding 1 for human readability
2280
2285
  "approved_at": mr["approved_at"],
2281
2286
  "approved_span_minutes": (
2282
- now - datetime.strptime(mr["approved_at"], glhk.DATE_FORMAT)
2287
+ now - from_utc_iso_format(mr["approved_at"])
2283
2288
  ).total_seconds()
2284
2289
  / 60,
2285
2290
  "approved_by": mr["approved_by"],
@@ -2693,7 +2698,7 @@ def ec2_jenkins_workers(
2693
2698
  client = boto3.client("autoscaling")
2694
2699
  ec2 = boto3.resource("ec2")
2695
2700
  results = []
2696
- now = datetime.now(UTC)
2701
+ now = utc_now()
2697
2702
  columns = [
2698
2703
  "type",
2699
2704
  "id",
@@ -2953,11 +2958,11 @@ def osd_component_versions(ctx: click.Context) -> None:
2953
2958
  @get.command()
2954
2959
  @click.pass_context
2955
2960
  def maintenances(ctx: click.Context) -> None:
2956
- now = datetime.now(UTC)
2961
+ now = utc_now()
2957
2962
  maintenances = maintenances_gql.query(gql.get_api().query).maintenances or []
2958
2963
  data = [
2959
2964
  {
2960
- **m.dict(),
2965
+ **m.model_dump(),
2961
2966
  "services": ", ".join(a.name for a in m.affected_services),
2962
2967
  }
2963
2968
  for m in maintenances
@@ -3668,7 +3673,7 @@ def template(
3668
3673
  continue
3669
3674
  if openshift_resource.name != name:
3670
3675
  continue
3671
- print_output({"output": "yaml", "sort": False}, openshift_resource.body)
3676
+ print_output({"output": "yaml", "sort": False}, openshift_resource.body) # type: ignore
3672
3677
  break
3673
3678
 
3674
3679
 
@@ -4095,7 +4100,9 @@ def sre_checkpoint_metadata(
4095
4100
  ) -> None:
4096
4101
  """Check an app path for checkpoint-related metadata."""
4097
4102
  data = queries.get_app_metadata(app_path)
4098
- settings = queries.get_app_interface_settings()
4103
+ vault_settings = get_app_interface_vault_settings()
4104
+ secret_reader = create_secret_reader(use_vault=vault_settings.vault)
4105
+
4099
4106
  app = data[0]
4100
4107
 
4101
4108
  if jiradef:
@@ -4108,7 +4115,14 @@ def sre_checkpoint_metadata(
4108
4115
  # Overrides for easier testing
4109
4116
  if jiraboard:
4110
4117
  board["name"] = jiraboard
4111
- report_invalid_metadata(app, app_path, board, settings, parent_ticket, dry_run)
4118
+ report_invalid_metadata(
4119
+ app=app,
4120
+ path=app_path,
4121
+ board=board,
4122
+ secret_reader=secret_reader,
4123
+ parent=parent_ticket,
4124
+ dry_run=dry_run,
4125
+ )
4112
4126
 
4113
4127
 
4114
4128
  @root.command()
@@ -4285,7 +4299,7 @@ def create(
4285
4299
  bg="red",
4286
4300
  fg="white",
4287
4301
  )
4288
- print(sso_client.json(by_alias=True, indent=2))
4302
+ print(json_dumps(sso_client, indent=2))
4289
4303
 
4290
4304
 
4291
4305
  @sso_client.command()
@@ -4837,11 +4851,12 @@ def top_talkers(ctx: click.Context, top: int) -> None:
4837
4851
  assert project.organization # make mypy happy
4838
4852
  assert project.pk # make mypy happy
4839
4853
 
4854
+ now = utc_now()
4840
4855
  stat = client.project_statistics(
4841
4856
  organization_slug=project.organization.slug,
4842
4857
  project_pk=project.pk,
4843
- start=datetime.now(tz=UTC) - timedelta(hours=24),
4844
- end=datetime.now(tz=UTC),
4858
+ start=now - timedelta(hours=24),
4859
+ end=now,
4845
4860
  )
4846
4861
  stats.append((project, stat))
4847
4862
 
@@ -1,7 +1,9 @@
1
+ from typing import Any
2
+
1
3
  from reconcile import queries
2
4
 
3
5
 
4
- def full_name(app):
6
+ def full_name(app: dict[str, Any]) -> str:
5
7
  """Builds App full_name, prepending the App with the name
6
8
  of the parent App.
7
9
 
@@ -17,14 +19,14 @@ def full_name(app):
17
19
  return name
18
20
 
19
21
 
20
- def get_latest_sre_checkpoints():
22
+ def get_latest_sre_checkpoints() -> dict[str, str]:
21
23
  """Builds dictionary with the full_name of the app as the key and the
22
24
  date of sre_checkpoint as the value.
23
25
 
24
26
  :return: dictionary with the latest checkpoints
25
27
  :rtype: dict
26
28
  """
27
- checkpoints = {}
29
+ checkpoints: dict[str, str] = {}
28
30
  for checkpoint in queries.get_sre_checkpoints():
29
31
  name = full_name(checkpoint["app"])
30
32
  date = checkpoint["date"]
@@ -67,7 +67,9 @@ def main(templates: tuple[str]) -> None:
67
67
  tests.append(test_yaml)
68
68
 
69
69
  template_raw["templateTest"] = tests
70
- template: TemplateV1 = TemplateV1(**data_default_none(TemplateV1, template_raw))
70
+ data = data_default_none(TemplateV1, template_raw)
71
+ assert isinstance(data, dict)
72
+ template: TemplateV1 = TemplateV1(**data)
71
73
 
72
74
  # templates_to_validate = {}
73
75
  for test in template.template_test:
File without changes
File without changes
@@ -1,77 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from enum import Enum
4
-
5
- from pydantic import (
6
- BaseModel,
7
- Field,
8
- )
9
-
10
-
11
- class SSHHostKeyVerificationStrategy(Enum):
12
- MANUALLY_TRUSTED_KEY_VERIFICATION_STRATEGY = (
13
- "manuallyTrustedKeyVerificationStrategy"
14
- )
15
- MANUALLY_PROVIDED_KEY_VERIFICATION_STRATEGY = (
16
- "manuallyProvidedKeyVerificationStrategy"
17
- )
18
- NON_VERIFYING_KEY_VERIFICATION_STRATEGY = "nonVerifyingKeyVerificationStrategy"
19
- KNOWN_HOSTS_FILE_KEY_VERIFICATION_STRATEGY = "knownHostsFileKeyVerificationStrategy"
20
-
21
-
22
- class SSHConnector(BaseModel):
23
- credentials_id: str = Field(..., alias="credentialsId")
24
- launch_timeout_seconds: int | None = Field(None, alias="launchTimeoutSeconds")
25
- max_num_retries: int | None = Field(None, alias="maxNumRetries")
26
- retry_wait_time: int | None = Field(None, alias="retryWaitTime")
27
- port: int | None = 22
28
- jvm_options: str | None = Field(None, alias="jvmOptions")
29
- ssh_host_key_verification_strategy: SSHHostKeyVerificationStrategy = Field(
30
- SSHHostKeyVerificationStrategy.NON_VERIFYING_KEY_VERIFICATION_STRATEGY,
31
- alias="sshHostKeyVerificationStrategy",
32
- )
33
-
34
- class Config:
35
- use_enum_values = True
36
-
37
-
38
- class ComputerConnector(BaseModel):
39
- # alias name is defined by jcasc schema
40
- ssh_connector: SSHConnector = Field(..., alias="sSHConnector")
41
-
42
-
43
- class JenkinsWorkerFleet(BaseModel):
44
- # following options comes form https://github.com/jenkinsci/ec2-fleet-plugin/blob/master/docs/
45
- name: str
46
- fleet: str
47
- region: str
48
- min_size: int = Field(..., alias="minSize")
49
- max_size: int = Field(..., alias="maxSize")
50
- computer_connector: ComputerConnector = Field(..., alias="computerConnector")
51
- fs_root: str = Field(..., alias="fsRoot")
52
- label_string: str = Field(..., alias="labelString")
53
- num_executors: int = Field(2, alias="numExecutors")
54
- idle_minutes: int = Field(30, alias="idleMinutes")
55
- min_spare_size: int = Field(0, alias="minSpareSize")
56
- max_total_uses: int = Field(-1, alias="maxTotalUses")
57
- no_delay_provision: bool = Field(False, alias="noDelayProvision")
58
- add_node_only_if_running: bool = Field(True, alias="addNodeOnlyIfRunning")
59
- always_reconnect: bool = Field(True, alias="alwaysReconnect")
60
- private_ip_used: bool = Field(True, alias="privateIpUsed")
61
- restrict_usage: bool = Field(True, alias="restrictUsage")
62
-
63
- def __lt__(self, other: JenkinsWorkerFleet) -> bool:
64
- return self.fleet < other.fleet
65
-
66
- def __eq__(self, other: object) -> bool:
67
- if not isinstance(other, JenkinsWorkerFleet):
68
- raise NotImplementedError(
69
- "Cannot compare to non JenkinsWorkerFleet objects."
70
- )
71
- return self.fleet == other.fleet and self.region == other.region
72
-
73
- def __hash__(self) -> int:
74
- return hash(self.fleet + self.region)
75
-
76
- def differ(self, other: JenkinsWorkerFleet) -> bool:
77
- return self.dict() != other.dict()