qontract-reconcile 0.10.2.dev349__py3-none-any.whl → 0.10.2.dev414__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 (356) hide show
  1. {qontract_reconcile-0.10.2.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/METADATA +12 -11
  2. {qontract_reconcile-0.10.2.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/RECORD +356 -350
  3. reconcile/acs_rbac.py +2 -2
  4. reconcile/aus/advanced_upgrade_service.py +15 -12
  5. reconcile/aus/base.py +26 -27
  6. reconcile/aus/cluster_version_data.py +15 -5
  7. reconcile/aus/models.py +1 -1
  8. reconcile/automated_actions/config/integration.py +15 -3
  9. reconcile/aws_account_manager/integration.py +8 -8
  10. reconcile/aws_account_manager/reconciler.py +3 -3
  11. reconcile/aws_ami_cleanup/integration.py +8 -12
  12. reconcile/aws_ami_share.py +69 -62
  13. reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
  14. reconcile/aws_ecr_image_pull_secrets.py +2 -2
  15. reconcile/aws_iam_keys.py +7 -41
  16. reconcile/aws_saml_idp/integration.py +12 -4
  17. reconcile/aws_saml_roles/integration.py +32 -25
  18. reconcile/aws_version_sync/integration.py +6 -12
  19. reconcile/change_owners/bundle.py +3 -3
  20. reconcile/change_owners/change_log_tracking.py +3 -2
  21. reconcile/change_owners/change_owners.py +1 -1
  22. reconcile/change_owners/diff.py +2 -4
  23. reconcile/checkpoint.py +11 -3
  24. reconcile/cli.py +33 -8
  25. reconcile/dashdotdb_dora.py +5 -12
  26. reconcile/dashdotdb_slo.py +1 -1
  27. reconcile/database_access_manager.py +123 -117
  28. reconcile/dynatrace_token_provider/integration.py +1 -1
  29. reconcile/endpoints_discovery/integration.py +4 -1
  30. reconcile/endpoints_discovery/merge_request.py +1 -1
  31. reconcile/endpoints_discovery/merge_request_manager.py +9 -11
  32. reconcile/external_resources/factories.py +5 -12
  33. reconcile/external_resources/integration.py +1 -1
  34. reconcile/external_resources/manager.py +24 -10
  35. reconcile/external_resources/meta.py +0 -1
  36. reconcile/external_resources/metrics.py +1 -1
  37. reconcile/external_resources/model.py +13 -13
  38. reconcile/external_resources/reconciler.py +7 -4
  39. reconcile/external_resources/secrets_sync.py +6 -8
  40. reconcile/external_resources/state.py +60 -17
  41. reconcile/fleet_labeler/integration.py +1 -1
  42. reconcile/gabi_authorized_users.py +8 -5
  43. reconcile/gcp_image_mirror.py +2 -2
  44. reconcile/github_org.py +1 -1
  45. reconcile/github_owners.py +4 -0
  46. reconcile/gitlab_housekeeping.py +13 -15
  47. reconcile/gitlab_members.py +6 -12
  48. reconcile/gitlab_mr_sqs_consumer.py +2 -2
  49. reconcile/gitlab_owners.py +15 -11
  50. reconcile/gitlab_permissions.py +8 -12
  51. reconcile/glitchtip_project_alerts/integration.py +3 -1
  52. reconcile/gql_definitions/acs/acs_instances.py +5 -5
  53. reconcile/gql_definitions/acs/acs_policies.py +5 -5
  54. reconcile/gql_definitions/acs/acs_rbac.py +5 -5
  55. reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +5 -5
  56. reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +5 -5
  57. reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +5 -5
  58. reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
  59. reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
  60. reconcile/gql_definitions/automated_actions/instance.py +46 -7
  61. reconcile/gql_definitions/aws_account_manager/aws_accounts.py +5 -5
  62. reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +15 -5
  63. reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +27 -66
  64. reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +15 -5
  65. reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +15 -5
  66. reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
  67. reconcile/gql_definitions/aws_version_sync/clusters.py +5 -5
  68. reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
  69. reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
  70. reconcile/gql_definitions/change_owners/queries/self_service_roles.py +5 -5
  71. reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +5 -5
  72. reconcile/gql_definitions/common/alerting_services_settings.py +5 -5
  73. reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
  74. reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
  75. reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
  76. reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
  77. reconcile/gql_definitions/common/app_interface_roles.py +5 -5
  78. reconcile/gql_definitions/common/app_interface_state_settings.py +5 -5
  79. reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
  80. reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
  81. reconcile/gql_definitions/common/apps.py +5 -5
  82. reconcile/gql_definitions/common/aws_vpc_requests.py +15 -5
  83. reconcile/gql_definitions/common/aws_vpcs.py +5 -5
  84. reconcile/gql_definitions/common/clusters.py +7 -5
  85. reconcile/gql_definitions/common/clusters_minimal.py +5 -5
  86. reconcile/gql_definitions/common/clusters_with_dms.py +5 -5
  87. reconcile/gql_definitions/common/clusters_with_peering.py +5 -5
  88. reconcile/gql_definitions/common/github_orgs.py +5 -5
  89. reconcile/gql_definitions/common/jira_settings.py +5 -5
  90. reconcile/gql_definitions/common/jiralert_settings.py +5 -5
  91. reconcile/gql_definitions/common/ldap_settings.py +5 -5
  92. reconcile/gql_definitions/common/namespaces.py +5 -5
  93. reconcile/gql_definitions/common/namespaces_minimal.py +7 -5
  94. reconcile/gql_definitions/common/ocm_env_telemeter.py +5 -5
  95. reconcile/gql_definitions/common/ocm_environments.py +5 -5
  96. reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
  97. reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -5
  98. reconcile/gql_definitions/common/pipeline_providers.py +5 -5
  99. reconcile/gql_definitions/common/quay_instances.py +5 -5
  100. reconcile/gql_definitions/common/quay_orgs.py +5 -5
  101. reconcile/gql_definitions/common/reserved_networks.py +5 -5
  102. reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
  103. reconcile/gql_definitions/common/saas_files.py +5 -5
  104. reconcile/gql_definitions/common/saas_target_namespaces.py +5 -5
  105. reconcile/gql_definitions/common/saasherder_settings.py +5 -5
  106. reconcile/gql_definitions/common/slack_workspaces.py +5 -5
  107. reconcile/gql_definitions/common/smtp_client_settings.py +5 -5
  108. reconcile/gql_definitions/common/state_aws_account.py +5 -5
  109. reconcile/gql_definitions/common/users.py +5 -5
  110. reconcile/gql_definitions/common/users_with_paths.py +5 -5
  111. reconcile/gql_definitions/cost_report/app_names.py +5 -5
  112. reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
  113. reconcile/gql_definitions/cost_report/settings.py +5 -5
  114. reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +5 -5
  115. reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +5 -5
  116. reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
  117. reconcile/gql_definitions/email_sender/apps.py +5 -5
  118. reconcile/gql_definitions/email_sender/emails.py +5 -5
  119. reconcile/gql_definitions/email_sender/users.py +5 -5
  120. reconcile/gql_definitions/endpoints_discovery/apps.py +5 -5
  121. reconcile/gql_definitions/external_resources/aws_accounts.py +5 -5
  122. reconcile/gql_definitions/external_resources/external_resources_modules.py +5 -5
  123. reconcile/gql_definitions/external_resources/external_resources_namespaces.py +89 -6
  124. reconcile/gql_definitions/external_resources/external_resources_settings.py +7 -5
  125. reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
  126. reconcile/gql_definitions/fleet_labeler/fleet_labels.py +5 -5
  127. reconcile/gql_definitions/fragments/aus_organization.py +5 -5
  128. reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
  129. reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
  130. reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
  131. reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
  132. reconcile/gql_definitions/fragments/aws_organization.py +33 -0
  133. reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
  134. reconcile/gql_definitions/fragments/aws_vpc_request.py +7 -5
  135. reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
  136. reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
  137. reconcile/gql_definitions/fragments/disable.py +5 -5
  138. reconcile/gql_definitions/fragments/email_service.py +5 -5
  139. reconcile/gql_definitions/fragments/email_user.py +5 -5
  140. reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
  141. reconcile/gql_definitions/fragments/membership_source.py +5 -5
  142. reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
  143. reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
  144. reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
  145. reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
  146. reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
  147. reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
  148. reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
  149. reconcile/gql_definitions/fragments/resource_values.py +5 -5
  150. reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
  151. reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
  152. reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
  153. reconcile/gql_definitions/fragments/terraform_state.py +5 -5
  154. reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
  155. reconcile/gql_definitions/fragments/user.py +5 -5
  156. reconcile/gql_definitions/fragments/vault_secret.py +5 -5
  157. reconcile/gql_definitions/gcp/gcp_docker_repos.py +5 -5
  158. reconcile/gql_definitions/gcp/gcp_projects.py +5 -5
  159. reconcile/gql_definitions/gitlab_members/gitlab_instances.py +5 -5
  160. reconcile/gql_definitions/gitlab_members/permissions.py +5 -5
  161. reconcile/gql_definitions/glitchtip/glitchtip_instance.py +5 -5
  162. reconcile/gql_definitions/glitchtip/glitchtip_project.py +5 -5
  163. reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +5 -5
  164. reconcile/gql_definitions/integrations/integrations.py +5 -5
  165. reconcile/gql_definitions/introspection.json +2137 -1053
  166. reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +5 -5
  167. reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +5 -5
  168. reconcile/gql_definitions/jira/jira_servers.py +5 -5
  169. reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +9 -5
  170. reconcile/gql_definitions/jumphosts/jumphosts.py +5 -5
  171. reconcile/gql_definitions/ldap_groups/roles.py +5 -5
  172. reconcile/gql_definitions/ldap_groups/settings.py +5 -5
  173. reconcile/gql_definitions/maintenance/maintenances.py +5 -5
  174. reconcile/gql_definitions/membershipsources/roles.py +5 -5
  175. reconcile/gql_definitions/ocm_labels/clusters.py +5 -5
  176. reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
  177. reconcile/gql_definitions/openshift_cluster_bots/clusters.py +5 -5
  178. reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
  179. reconcile/gql_definitions/openshift_groups/managed_roles.py +5 -5
  180. reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +5 -5
  181. reconcile/gql_definitions/quay_membership/quay_membership.py +5 -5
  182. reconcile/gql_definitions/rhcs/certs.py +5 -5
  183. reconcile/gql_definitions/rhidp/organizations.py +5 -5
  184. reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
  185. reconcile/gql_definitions/service_dependencies/service_dependencies.py +5 -5
  186. reconcile/gql_definitions/sharding/aws_accounts.py +5 -5
  187. reconcile/gql_definitions/sharding/ocm_organization.py +5 -5
  188. reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
  189. reconcile/gql_definitions/skupper_network/skupper_networks.py +5 -5
  190. reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
  191. reconcile/gql_definitions/slack_usergroups/permissions.py +5 -5
  192. reconcile/gql_definitions/slack_usergroups/users.py +5 -5
  193. reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
  194. reconcile/gql_definitions/status_board/status_board.py +5 -5
  195. reconcile/gql_definitions/statuspage/statuspages.py +5 -5
  196. reconcile/gql_definitions/templating/template_collection.py +5 -5
  197. reconcile/gql_definitions/templating/templates.py +5 -5
  198. reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +5 -5
  199. reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +5 -5
  200. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +5 -5
  201. reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +5 -5
  202. reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +5 -5
  203. reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +5 -5
  204. reconcile/gql_definitions/terraform_init/aws_accounts.py +19 -5
  205. reconcile/gql_definitions/terraform_repo/terraform_repo.py +5 -5
  206. reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
  207. reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +38 -6
  208. reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +15 -5
  209. reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +5 -5
  210. reconcile/gql_definitions/vault_instances/vault_instances.py +5 -5
  211. reconcile/gql_definitions/vault_policies/vault_policies.py +5 -5
  212. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +5 -5
  213. reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
  214. reconcile/integrations_manager.py +3 -3
  215. reconcile/jenkins_worker_fleets.py +10 -8
  216. reconcile/jira_permissions_validator.py +237 -122
  217. reconcile/ldap_groups/integration.py +1 -1
  218. reconcile/ocm/types.py +35 -56
  219. reconcile/ocm_aws_infrastructure_access.py +1 -1
  220. reconcile/ocm_clusters.py +4 -4
  221. reconcile/ocm_labels/integration.py +3 -2
  222. reconcile/ocm_machine_pools.py +23 -23
  223. reconcile/openshift_base.py +53 -2
  224. reconcile/openshift_cluster_bots.py +3 -2
  225. reconcile/openshift_namespace_labels.py +1 -1
  226. reconcile/openshift_namespaces.py +97 -101
  227. reconcile/openshift_resources_base.py +6 -2
  228. reconcile/openshift_rhcs_certs.py +5 -5
  229. reconcile/openshift_rolebindings.py +7 -11
  230. reconcile/openshift_saas_deploy.py +6 -7
  231. reconcile/openshift_saas_deploy_change_tester.py +9 -7
  232. reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
  233. reconcile/openshift_serviceaccount_tokens.py +2 -2
  234. reconcile/openshift_upgrade_watcher.py +4 -4
  235. reconcile/oum/labelset.py +5 -3
  236. reconcile/oum/models.py +1 -4
  237. reconcile/prometheus_rules_tester/integration.py +3 -3
  238. reconcile/quay_mirror.py +1 -1
  239. reconcile/queries.py +131 -1
  240. reconcile/rhidp/common.py +3 -5
  241. reconcile/rhidp/sso_client/base.py +1 -1
  242. reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
  243. reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
  244. reconcile/skupper_network/integration.py +2 -2
  245. reconcile/slack_usergroups.py +35 -14
  246. reconcile/sql_query.py +1 -0
  247. reconcile/status_board.py +6 -6
  248. reconcile/statuspage/atlassian.py +7 -7
  249. reconcile/statuspage/integrations/maintenances.py +4 -3
  250. reconcile/statuspage/page.py +4 -9
  251. reconcile/statuspage/status.py +5 -8
  252. reconcile/templates/rosa-classic-cluster-creation.sh.j2 +4 -0
  253. reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +3 -0
  254. reconcile/templating/lib/rendering.py +3 -3
  255. reconcile/templating/renderer.py +4 -3
  256. reconcile/terraform_aws_route53.py +7 -1
  257. reconcile/terraform_cloudflare_dns.py +3 -3
  258. reconcile/terraform_cloudflare_resources.py +5 -5
  259. reconcile/terraform_cloudflare_users.py +3 -2
  260. reconcile/terraform_init/integration.py +187 -23
  261. reconcile/terraform_repo.py +16 -12
  262. reconcile/terraform_resources.py +17 -7
  263. reconcile/terraform_tgw_attachments.py +27 -19
  264. reconcile/terraform_users.py +7 -0
  265. reconcile/terraform_vpc_peerings.py +14 -3
  266. reconcile/terraform_vpc_resources/integration.py +10 -1
  267. reconcile/typed_queries/aws_account_tags.py +41 -0
  268. reconcile/typed_queries/cost_report/app_names.py +1 -1
  269. reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
  270. reconcile/typed_queries/saas_files.py +13 -13
  271. reconcile/typed_queries/status_board.py +2 -2
  272. reconcile/unleash_feature_toggles/integration.py +4 -2
  273. reconcile/utils/acs/base.py +6 -3
  274. reconcile/utils/acs/policies.py +2 -2
  275. reconcile/utils/aggregated_list.py +4 -3
  276. reconcile/utils/aws_api.py +51 -54
  277. reconcile/utils/aws_api_typed/api.py +38 -9
  278. reconcile/utils/aws_api_typed/cloudformation.py +149 -0
  279. reconcile/utils/aws_api_typed/logs.py +73 -0
  280. reconcile/utils/aws_api_typed/organization.py +4 -2
  281. reconcile/utils/datetime_util.py +67 -0
  282. reconcile/utils/deadmanssnitch_api.py +1 -1
  283. reconcile/utils/differ.py +2 -3
  284. reconcile/utils/early_exit_cache.py +11 -12
  285. reconcile/utils/expiration.py +7 -3
  286. reconcile/utils/external_resource_spec.py +24 -1
  287. reconcile/utils/filtering.py +1 -1
  288. reconcile/utils/gitlab_api.py +7 -5
  289. reconcile/utils/glitchtip/client.py +6 -2
  290. reconcile/utils/glitchtip/models.py +25 -28
  291. reconcile/utils/gql.py +4 -7
  292. reconcile/utils/helm.py +2 -1
  293. reconcile/utils/helpers.py +1 -1
  294. reconcile/utils/instrumented_wrappers.py +1 -1
  295. reconcile/utils/internal_groups/client.py +2 -2
  296. reconcile/utils/internal_groups/models.py +8 -17
  297. reconcile/utils/jinja2/utils.py +6 -101
  298. reconcile/utils/jira_client.py +82 -63
  299. reconcile/utils/jjb_client.py +9 -12
  300. reconcile/utils/jobcontroller/controller.py +1 -1
  301. reconcile/utils/jobcontroller/models.py +17 -1
  302. reconcile/utils/json.py +70 -0
  303. reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
  304. reconcile/utils/membershipsources/models.py +16 -23
  305. reconcile/utils/membershipsources/resolver.py +4 -2
  306. reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
  307. reconcile/utils/merge_request_manager/parser.py +6 -6
  308. reconcile/utils/metrics.py +5 -5
  309. reconcile/utils/models.py +304 -82
  310. reconcile/utils/mr/app_interface_reporter.py +2 -2
  311. reconcile/utils/mr/base.py +2 -2
  312. reconcile/utils/mr/notificator.py +3 -3
  313. reconcile/utils/mr/update_access_report_base.py +3 -4
  314. reconcile/utils/mr/user_maintenance.py +3 -2
  315. reconcile/utils/oc.py +118 -97
  316. reconcile/utils/oc_filters.py +3 -3
  317. reconcile/utils/ocm/addons.py +0 -1
  318. reconcile/utils/ocm/base.py +17 -20
  319. reconcile/utils/ocm/cluster_groups.py +1 -1
  320. reconcile/utils/ocm/identity_providers.py +2 -2
  321. reconcile/utils/ocm/labels.py +1 -1
  322. reconcile/utils/ocm/products.py +9 -3
  323. reconcile/utils/ocm/search_filters.py +3 -6
  324. reconcile/utils/ocm/service_log.py +4 -6
  325. reconcile/utils/ocm/sre_capability_labels.py +20 -13
  326. reconcile/utils/openshift_resource.py +10 -5
  327. reconcile/utils/output.py +3 -2
  328. reconcile/utils/pagerduty_api.py +10 -7
  329. reconcile/utils/promotion_state.py +6 -11
  330. reconcile/utils/raw_github_api.py +1 -1
  331. reconcile/utils/rhcsv2_certs.py +1 -4
  332. reconcile/utils/runtime/integration.py +2 -3
  333. reconcile/utils/runtime/runner.py +2 -2
  334. reconcile/utils/saasherder/interfaces.py +13 -20
  335. reconcile/utils/saasherder/models.py +25 -21
  336. reconcile/utils/saasherder/saasherder.py +35 -24
  337. reconcile/utils/slack_api.py +26 -4
  338. reconcile/utils/sloth.py +171 -2
  339. reconcile/utils/sqs_gateway.py +2 -1
  340. reconcile/utils/state.py +2 -1
  341. reconcile/utils/structs.py +1 -1
  342. reconcile/utils/terraform_client.py +5 -4
  343. reconcile/utils/terrascript_aws_client.py +171 -114
  344. reconcile/utils/unleash/server.py +2 -8
  345. reconcile/utils/vault.py +5 -12
  346. reconcile/utils/vcs.py +8 -8
  347. reconcile/vault_replication.py +107 -42
  348. tools/app_interface_reporter.py +4 -4
  349. tools/cli_commands/cost_report/cost_management_api.py +3 -3
  350. tools/cli_commands/cost_report/view.py +7 -6
  351. tools/cli_commands/erv2.py +3 -1
  352. tools/cli_commands/systems_and_tools.py +5 -1
  353. tools/qontract_cli.py +31 -18
  354. tools/template_validation.py +3 -1
  355. {qontract_reconcile-0.10.2.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/WHEEL +0 -0
  356. {qontract_reconcile-0.10.2.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/entry_points.txt +0 -0
reconcile/utils/gql.py CHANGED
@@ -109,7 +109,7 @@ class GqlApi:
109
109
  if int_name:
110
110
  integrations = self.query(INTEGRATIONS_QUERY, skip_validation=True)
111
111
 
112
- for integration in integrations["integrations"]:
112
+ for integration in integrations["integrations"] if integrations else []:
113
113
  if integration["name"] == int_name:
114
114
  self._valid_schemas = integration["schemas"]
115
115
  break
@@ -142,7 +142,7 @@ class GqlApi:
142
142
  query: str,
143
143
  variables: dict[str, Any] | None = None,
144
144
  skip_validation: bool = False,
145
- ) -> dict[str, Any] | None:
145
+ ) -> dict[str, Any]:
146
146
  try:
147
147
  result = self.client.execute(
148
148
  gql(query), variables, get_execution_result=True
@@ -172,11 +172,8 @@ class GqlApi:
172
172
  if forbidden_schemas:
173
173
  raise GqlApiErrorForbiddenSchemaError(forbidden_schemas)
174
174
 
175
- # This is to appease mypy. This exception won't be thrown as this condition
176
- # is already handled above with AssertionError
177
- if result["data"] is None:
178
- raise GqlApiError("`data` not received in GraphQL payload")
179
-
175
+ # make mypy happy
176
+ assert "data" in result and result["data"] is not None
180
177
  return result["data"]
181
178
 
182
179
  def get_template(self, path: str) -> dict[str, str]:
reconcile/utils/helm.py CHANGED
@@ -11,6 +11,7 @@ from typing import Any
11
11
  import yaml
12
12
 
13
13
  from reconcile.utils import git
14
+ from reconcile.utils.json import json_dumps
14
15
  from reconcile.utils.runtime.sharding import ShardSpec
15
16
 
16
17
 
@@ -70,7 +71,7 @@ def do_template(
70
71
  with tempfile.NamedTemporaryFile(
71
72
  mode="w+", encoding="locale"
72
73
  ) as values_file:
73
- values_file.write(json.dumps(values, cls=JSONEncoder))
74
+ values_file.write(json_dumps(values, cls=JSONEncoder))
74
75
  values_file.flush()
75
76
  cmd = [
76
77
  "helm",
@@ -47,7 +47,7 @@ def flatten(
47
47
  Item = TypeVar("Item")
48
48
 
49
49
 
50
- def find_duplicates(items: Iterable[Item]) -> list[Item]:
50
+ def find_duplicates[Item](items: Iterable[Item]) -> list[Item]:
51
51
  return [item for item, count in Counter(items).items() if count > 1]
52
52
 
53
53
 
@@ -40,7 +40,7 @@ class InstrumentedImage(Image):
40
40
 
41
41
 
42
42
  class InstrumentedSkopeo(Skopeo):
43
- def copy(self, *args: Any, **kwargs: Any) -> bytes | str:
43
+ def copy(self, *args: Any, **kwargs: Any) -> None:
44
44
  metrics.copy_count.labels(
45
45
  integration=INTEGRATION_NAME, shard=SHARDS, shard_id=SHARD_ID
46
46
  ).inc()
@@ -140,7 +140,7 @@ class InternalGroupsClient:
140
140
  with self._api as api:
141
141
  return Group(
142
142
  **api.create_group(
143
- data=group.dict(by_alias=True),
143
+ data=group.model_dump(by_alias=True),
144
144
  )
145
145
  )
146
146
 
@@ -155,6 +155,6 @@ class InternalGroupsClient:
155
155
  return Group(
156
156
  **api.update_group(
157
157
  name=group.name,
158
- data=group.dict(by_alias=True),
158
+ data=group.model_dump(by_alias=True),
159
159
  )
160
160
  )
@@ -27,7 +27,7 @@ class Entity(BaseModel):
27
27
  return hash(self.id)
28
28
 
29
29
 
30
- class Group(BaseModel):
30
+ class Group(BaseModel, validate_by_name=True, validate_by_alias=True):
31
31
  name: str
32
32
  description: str
33
33
  member_approval_type: str = Field("self-service", alias="memberApprovalType")
@@ -35,16 +35,18 @@ class Group(BaseModel):
35
35
  owners: list[Entity]
36
36
  display_name: str = Field(..., alias="displayName")
37
37
  notes: str | None = None
38
- rover_group_member_query: str | None = Field(None, alias="roverGroupMemberQuery")
38
+ rover_group_member_query: str | None = Field(
39
+ None, alias="roverGroupMemberQuery", exclude=True
40
+ )
39
41
  rover_group_inclusions: list[Entity] | None = Field(
40
- None, alias="roverGroupInclusions"
42
+ None, alias="roverGroupInclusions", exclude=True
41
43
  )
42
44
  rover_group_exclusions: list[Entity] | None = Field(
43
- None, alias="roverGroupExclusions"
45
+ None, alias="roverGroupExclusions", exclude=True
44
46
  )
45
47
  members: list[Entity] = []
46
- member_of: list[str] | None = Field(None, alias="memberOf")
47
- namespace: str | None = None
48
+ member_of: list[str] | None = Field(None, alias="memberOf", exclude=True)
49
+ namespace: str | None = Field(None, exclude=True)
48
50
 
49
51
  def __eq__(self, other: object) -> bool:
50
52
  if not isinstance(other, Group):
@@ -58,14 +60,3 @@ class Group(BaseModel):
58
60
  and self.notes == other.notes
59
61
  and set(self.members) == set(other.members)
60
62
  )
61
-
62
- class Config:
63
- allow_population_by_field_name = True
64
- # exclude read-only fields in the json/dict dumps
65
- fields = {
66
- "rover_group_member_query": {"exclude": True},
67
- "rover_group_inclusions": {"exclude": True},
68
- "rover_group_exclusions": {"exclude": True},
69
- "member_of": {"exclude": True},
70
- "namespace": {"exclude": True},
71
- }
@@ -1,13 +1,10 @@
1
1
  import datetime
2
2
  import os
3
- import subprocess
4
- import tempfile
5
3
  from collections.abc import Mapping
6
4
  from functools import cache
7
5
  from typing import Any, Self
8
6
 
9
7
  import jinja2
10
- import yaml
11
8
  from github import Github
12
9
  from jinja2.sandbox import SandboxedEnvironment
13
10
  from pydantic import BaseModel
@@ -18,6 +15,7 @@ from reconcile.checkpoint import url_makes_sense
18
15
  from reconcile.github_org import get_default_config
19
16
  from reconcile.utils import gql
20
17
  from reconcile.utils.aws_api import AWSApi
18
+ from reconcile.utils.datetime_util import utc_now
21
19
  from reconcile.utils.github_api import GithubRepositoryApi
22
20
  from reconcile.utils.helpers import flatten
23
21
  from reconcile.utils.jinja2.extensions import B64EncodeExtension, RaiseErrorExtension
@@ -38,7 +36,7 @@ from reconcile.utils.secret_reader import (
38
36
  SecretReader,
39
37
  SecretReaderBase,
40
38
  )
41
- from reconcile.utils.sloth import process_sloth_output
39
+ from reconcile.utils.sloth import generate_sloth_rules
42
40
  from reconcile.utils.vault import SecretFieldNotFoundError
43
41
 
44
42
 
@@ -47,14 +45,11 @@ class Jinja2TemplateError(Exception):
47
45
  super().__init__("error processing jinja2 template: " + str(msg))
48
46
 
49
47
 
50
- class TemplateRenderOptions(BaseModel):
48
+ class TemplateRenderOptions(BaseModel, frozen=True):
51
49
  trim_blocks: bool
52
50
  lstrip_blocks: bool
53
51
  keep_trailing_newline: bool
54
52
 
55
- class Config:
56
- frozen = True
57
-
58
53
  @classmethod
59
54
  def create(
60
55
  cls,
@@ -77,7 +72,7 @@ def compile_jinja2_template(
77
72
  ) -> Any:
78
73
  if not template_render_options:
79
74
  template_render_options = TemplateRenderOptions.create()
80
- env: dict[str, Any] = template_render_options.dict()
75
+ env: dict[str, Any] = template_render_options.model_dump()
81
76
  if extra_curly:
82
77
  env.update({
83
78
  "block_start_string": "{{%",
@@ -192,89 +187,6 @@ def list_s3_objects(
192
187
  )
193
188
 
194
189
 
195
- def sloth_alerts(
196
- service: str,
197
- slo_name: str,
198
- objective: float,
199
- error_query: str,
200
- total_query: str,
201
- version: str = "prometheus/v1",
202
- ) -> str:
203
- """Generate Prometheus rules using sloth: https://sloth.dev
204
-
205
- Args:
206
- service: Service name identifier
207
- slo_name: Name of the SLO
208
- objective: Target percentage (e.g. 99.9)
209
- error_query: Prometheus query for error events
210
- total_query: Prometheus query for total events
211
- version: Spec version (default: "prometheus/v1")
212
-
213
- Returns:
214
- Generated Prometheus rules as YAML string
215
- """
216
- # Build the SLO definition
217
- slo = {
218
- "name": slo_name,
219
- "objective": objective,
220
- "description": f"{slo_name} SLO for {service}",
221
- "sli": {
222
- "events": {
223
- "error_query": error_query.replace("{{window}}", "{{.window}}"),
224
- "total_query": total_query.replace("{{window}}", "{{.window}}"),
225
- }
226
- },
227
- "alerting": {
228
- "name": f"{service.title()}{slo_name.title()}",
229
- "annotations": {
230
- "summary": f"High error rate on '{service}' {slo_name}",
231
- "message": f"High error rate on '{service}' {slo_name}",
232
- },
233
- "page_alert": {
234
- "labels": {
235
- "severity": "critical",
236
- "service": service,
237
- "slo": slo_name,
238
- }
239
- },
240
- "ticket_alert": {
241
- "labels": {
242
- "severity": "medium",
243
- "service": service,
244
- "slo": slo_name,
245
- }
246
- },
247
- },
248
- }
249
-
250
- spec = {
251
- "version": version,
252
- "service": service,
253
- "slos": [slo],
254
- }
255
-
256
- with (
257
- tempfile.NamedTemporaryFile(
258
- encoding="utf-8", mode="w", suffix=".yml"
259
- ) as input_file,
260
- tempfile.NamedTemporaryFile(
261
- encoding="utf-8", mode="w", suffix=".yml"
262
- ) as output_file,
263
- ):
264
- yaml.dump(spec, input_file, allow_unicode=True)
265
- cmd = ["sloth", "generate", "-i", input_file.name, "-o", output_file.name]
266
- try:
267
- subprocess.run(cmd, capture_output=True, check=True, text=True)
268
- except subprocess.CalledProcessError as e:
269
- error_msg = f"{e}"
270
- if e.stdout:
271
- error_msg += f"\nstdout: {e.stdout}"
272
- if e.stderr:
273
- error_msg += f"\nstderr: {e.stderr}"
274
- raise SlothGenerateError(error_msg) from e
275
- return process_sloth_output(output_file.name)
276
-
277
-
278
190
  @retry()
279
191
  def lookup_secret(
280
192
  path: str,
@@ -345,10 +257,8 @@ def process_jinja2_template(
345
257
  "s3": lookup_s3_object,
346
258
  "s3_ls": list_s3_objects,
347
259
  "flatten_dict": flatten,
348
- "yesterday": lambda: (datetime.datetime.now() - datetime.timedelta(1)).strftime(
349
- "%Y-%m-%d"
350
- ),
351
- "sloth_alerts": sloth_alerts,
260
+ "yesterday": lambda: (utc_now() - datetime.timedelta(1)).strftime("%Y-%m-%d"),
261
+ "sloth_alerts": generate_sloth_rules,
352
262
  })
353
263
  if "_template_mocks" in vars:
354
264
  for k, v in vars["_template_mocks"].items():
@@ -381,11 +291,6 @@ def process_extracurlyjinja2_template(
381
291
  )
382
292
 
383
293
 
384
- class SlothGenerateError(Exception):
385
- def __init__(self, msg: Any):
386
- super().__init__("sloth generate failed: " + str(msg))
387
-
388
-
389
294
  class FetchSecretError(Exception):
390
295
  def __init__(self, msg: Any):
391
296
  super().__init__("error fetching secret: " + str(msg))
@@ -17,10 +17,8 @@ from jira.resources import CustomFieldOption as JiraCustomFieldOption
17
17
  from jira.resources import Resource
18
18
  from pydantic import BaseModel
19
19
 
20
- from reconcile.utils.secret_reader import SecretReader
21
-
22
20
  if TYPE_CHECKING:
23
- from collections.abc import Iterable, Mapping
21
+ from collections.abc import Iterable
24
22
 
25
23
 
26
24
  class JiraWatcherSettings(Protocol):
@@ -87,36 +85,18 @@ class IssueField(BaseModel):
87
85
  options: list[FieldOption | CustomFieldOption]
88
86
 
89
87
 
88
+ CREATE_ISSUES = "CREATE_ISSUES"
89
+ TRANSITION_ISSUES = "TRANSITION_ISSUES"
90
+ PERMISSIONS = [CREATE_ISSUES, TRANSITION_ISSUES]
91
+
92
+
90
93
  class JiraClient:
91
94
  """Wrapper around Jira client."""
92
95
 
93
96
  DEFAULT_CONNECT_TIMEOUT = 60
94
97
  DEFAULT_READ_TIMEOUT = 60
95
98
 
96
- def __init__(
97
- self,
98
- jira_board: Mapping[str, Any] | None = None,
99
- settings: Mapping | None = None,
100
- jira_api: JIRA | None = None,
101
- project: str | None = None,
102
- server: str | None = None,
103
- ):
104
- """
105
- Note: jira_board and settings is to be deprecated. Use JiraClient.create() instead.
106
- """
107
- if jira_api and jira_board:
108
- raise RuntimeError(
109
- "jira_board parameter is deprecated. Use JiraClient.create() instead."
110
- )
111
- if not (jira_api and project):
112
- # kept for backwards-compatibility
113
- if not jira_board:
114
- raise RuntimeError(
115
- "JiraClient needs jira_api and project or jira_board."
116
- )
117
- self._deprecated_init(jira_board=jira_board, settings=settings)
118
- return
119
-
99
+ def __init__(self, jira_api: JIRA, project: str, server: str):
120
100
  self.server = server
121
101
  self.project = project
122
102
  self.jira = jira_api
@@ -128,47 +108,31 @@ class JiraClient:
128
108
  self.project_issue_types = functools.cache(self._project_issue_types)
129
109
  self.project_issue_fields = functools.cache(self._project_issue_fields)
130
110
 
131
- def _deprecated_init(
132
- self, jira_board: Mapping[str, Any], settings: Mapping | None
133
- ) -> None:
134
- secret_reader = SecretReader(settings=settings)
135
- self.project = jira_board["name"]
136
- jira_server = jira_board["server"]
137
- self.server = jira_server["serverUrl"]
138
- token = jira_server["token"]
139
- token_auth = secret_reader.read(token)
140
- read_timeout = 60
141
- connect_timeout = 60
142
- if settings and settings["jiraWatcher"]:
143
- read_timeout = settings["jiraWatcher"]["readTimeout"]
144
- connect_timeout = settings["jiraWatcher"]["connectTimeout"]
145
- if not self.server:
146
- raise RuntimeError("JiraClient.server is not set.")
147
-
148
- self.jira = JIRA(
149
- self.server,
150
- token_auth=token_auth,
151
- timeout=(read_timeout, connect_timeout),
152
- logging=False,
153
- )
154
-
155
111
  @staticmethod
156
112
  def create(
157
113
  project_name: str,
158
114
  token: str,
115
+ email: str | None,
159
116
  server_url: str,
160
117
  jira_watcher_settings: JiraWatcherSettings | None = None,
161
118
  ) -> JiraClient:
119
+ """Create a Jira client for the given project."""
162
120
  read_timeout = JiraClient.DEFAULT_READ_TIMEOUT
163
121
  connect_timeout = JiraClient.DEFAULT_CONNECT_TIMEOUT
164
122
  if jira_watcher_settings:
165
123
  read_timeout = jira_watcher_settings.read_timeout
166
124
  connect_timeout = jira_watcher_settings.connect_timeout
125
+
126
+ # Jira Cloud uses email+API token for basic auth
127
+ # Jira Server/Data Center can use token auth (personal access token)
128
+ auth_params: dict[str, Any] = (
129
+ {"basic_auth": (email, token)} if email else {"token_auth": token}
130
+ )
167
131
  jira_api = JIRA(
168
132
  server=server_url,
169
- token_auth=token,
170
133
  timeout=(read_timeout, connect_timeout),
171
134
  logging=False,
135
+ **auth_params,
172
136
  )
173
137
  return JiraClient(
174
138
  jira_api=jira_api,
@@ -176,7 +140,13 @@ class JiraClient:
176
140
  server=server_url,
177
141
  )
178
142
 
143
+ @property
144
+ def is_cloud(self) -> bool:
145
+ """Return whether we are on a Cloud based Jira instance."""
146
+ return self.jira.deploymentType == "Cloud"
147
+
179
148
  def get_issues(self, fields: Iterable | None = None) -> list[Issue]:
149
+ """Return all issues for our project."""
180
150
  block_size = 100
181
151
  block_num = 0
182
152
 
@@ -227,20 +197,34 @@ class JiraClient:
227
197
  return issue
228
198
 
229
199
  def _my_permissions(self, project: str) -> dict[str, Any]:
200
+ """Return my permissions for the given project.
201
+
202
+ Don't use this function directly, use self.my_permissions which is cached."""
203
+ if self.is_cloud:
204
+ return self.jira.my_permissions(
205
+ projectKey=project, permissions=",".join(PERMISSIONS)
206
+ )["permissions"]
230
207
  return self.jira.my_permissions(projectKey=project)["permissions"]
231
208
 
232
209
  def can_i(self, permission: str) -> bool:
210
+ """Return whether I have the given permission in the project."""
233
211
  return bool(
234
212
  self.my_permissions(project=self.project)[permission]["havePermission"]
235
213
  )
236
214
 
237
215
  def can_create_issues(self) -> bool:
238
- return self.can_i("CREATE_ISSUES")
216
+ """Return whether I can create issues in the project."""
217
+ return self.can_i(CREATE_ISSUES)
239
218
 
240
219
  def can_transition_issues(self) -> bool:
241
- return self.can_i("TRANSITION_ISSUES")
220
+ """Return whether I can transition issues in the project."""
221
+ return self.can_i(TRANSITION_ISSUES)
242
222
 
243
223
  def _project_issue_types(self, project: str) -> list[IssueType]:
224
+ """Return all available issue types (e.g. Task, Bug) for the project.
225
+
226
+ Don't use this function directly, use self.project_issue_types which is cached.
227
+ """
244
228
  # Don't use self.project here, because of function.cache usage
245
229
  return [
246
230
  IssueType(id=t.id, name=t.name, statuses=[s.name for s in t.statuses])
@@ -248,6 +232,7 @@ class JiraClient:
248
232
  ]
249
233
 
250
234
  def get_issue_type(self, issue_type: str) -> IssueType | None:
235
+ """Return a issue type (e.g. Task) for the project if it exists."""
251
236
  for _issue_type in self.project_issue_types(self.project):
252
237
  if _issue_type.name == issue_type:
253
238
  return _issue_type
@@ -255,15 +240,23 @@ class JiraClient:
255
240
 
256
241
  @staticmethod
257
242
  def _get_allowed_issue_field_options(
258
- allowed_values: list[Resource],
243
+ allowed_values: list[Resource] | list[dict[str, str]],
259
244
  ) -> list[FieldOption | CustomFieldOption]:
260
- """Return a list of allowed values for a field."""
261
- return [
262
- CustomFieldOption(value=v.value)
263
- if isinstance(v, JiraCustomFieldOption)
264
- else FieldOption(name=v.name)
265
- for v in allowed_values
266
- ]
245
+ """Return a list of allowed values for a field. E.g. Minor, Major ... for Priority in a Task."""
246
+ items: list[FieldOption | CustomFieldOption] = []
247
+ for v in allowed_values:
248
+ match v:
249
+ case dict() if "value" in v:
250
+ items.append(CustomFieldOption(value=v["value"]))
251
+ case dict() if "name" in v:
252
+ items.append(FieldOption(name=v["name"]))
253
+ case JiraCustomFieldOption():
254
+ items.append(CustomFieldOption(value=v.value))
255
+ case Resource():
256
+ items.append(FieldOption(name=v.name))
257
+ case _:
258
+ logging.warning(f"Unknown allowed value type: {type(v)}")
259
+ return items
267
260
 
268
261
  def _project_issue_fields(
269
262
  self, project: str, issue_type_id: str
@@ -273,6 +266,27 @@ class JiraClient:
273
266
  This API endpoint needs createIssue project permissions.
274
267
  """
275
268
  # Don't use self.project here, because of function.cache usage
269
+ if self.is_cloud:
270
+ metadata = self.jira.createmeta(
271
+ projectKeys=self.project,
272
+ issuetypeIds=[issue_type_id],
273
+ expand="projects.issuetypes.fields",
274
+ )
275
+ if not metadata["projects"] or not metadata["projects"][0]["issuetypes"]:
276
+ return []
277
+ return [
278
+ IssueField(
279
+ name=field["name"],
280
+ id=field_id,
281
+ options=self._get_allowed_issue_field_options(
282
+ field.get("allowedValues", [])
283
+ ),
284
+ )
285
+ for field_id, field in metadata["projects"][0]["issuetypes"][0][
286
+ "fields"
287
+ ].items()
288
+ ]
289
+
276
290
  return [
277
291
  IssueField(
278
292
  name=field.name,
@@ -304,6 +318,10 @@ class JiraClient:
304
318
 
305
319
  def project_priority_scheme(self) -> list[str]:
306
320
  """Return a list of all priority IDs for the project."""
321
+ if self.is_cloud:
322
+ # Cloud does not have a way to retrieve project specific priority schemes
323
+ return []
324
+
307
325
  scheme = self.jira.project_priority_scheme(self.project)
308
326
  return scheme.optionIds
309
327
 
@@ -329,4 +347,5 @@ class JiraClient:
329
347
 
330
348
  @property
331
349
  def is_archived(self) -> bool:
332
- return self.jira.project(self.project).archived
350
+ """Return whether the project is archived."""
351
+ return getattr(self.jira.project(self.project), "archived", False)
@@ -1,6 +1,5 @@
1
1
  import difflib
2
2
  import filecmp
3
- import json
4
3
  import logging
5
4
  import os
6
5
  import re
@@ -10,6 +9,7 @@ import tempfile
10
9
  import xml.etree.ElementTree as ET
11
10
  from collections.abc import Iterable, Mapping
12
11
  from os import path
12
+ from pathlib import Path
13
13
  from subprocess import (
14
14
  PIPE,
15
15
  STDOUT,
@@ -18,14 +18,14 @@ from subprocess import (
18
18
  from typing import Any
19
19
 
20
20
  import yaml
21
- from jenkins_jobs.builder import JenkinsManager
22
21
  from jenkins_jobs.errors import JenkinsJobsException
23
- from jenkins_jobs.parser import YamlParser
24
- from jenkins_jobs.registry import ModuleRegistry
22
+ from jenkins_jobs.loader import load_files
23
+ from jenkins_jobs.roots import Roots
25
24
  from sretoolbox.utils import retry
26
25
 
27
26
  from reconcile.utils import throughput
28
27
  from reconcile.utils.helpers import toggle_logger
28
+ from reconcile.utils.json import json_dumps
29
29
  from reconcile.utils.secret_reader import SecretReaderBase
30
30
  from reconcile.utils.state import State
31
31
  from reconcile.utils.vcs import GITHUB_BASE_URL
@@ -292,13 +292,10 @@ class JJB:
292
292
 
293
293
  args = ["--conf", ini_path, "test", config_path]
294
294
  jjb = self.get_jjb(args)
295
- builder = JenkinsManager(jjb.jjb_config)
296
- registry = ModuleRegistry(jjb.jjb_config, builder.plugins_list)
297
- parser = YamlParser(jjb.jjb_config)
298
- parser.load_files(jjb.options.path)
299
- jobs, _ = parser.expandYaml(registry, jjb.options.names)
300
-
301
- return jobs
295
+ roots = Roots(jjb.jjb_config)
296
+ load_files(jjb.jjb_config, roots, [Path(config_path)])
297
+ job_view_data_list = roots.generate_jobs()
298
+ return [job.data for job in job_view_data_list]
302
299
 
303
300
  def get_job_webhooks_data(self) -> dict[str, list[dict[str, Any]]]:
304
301
  job_webhooks_data: dict[str, list[dict[str, Any]]] = {}
@@ -399,7 +396,7 @@ class JJB:
399
396
  found = True
400
397
  if not found:
401
398
  raise ValueError(f"job name {job_name} is not found")
402
- print(json.dumps(all_jobs, indent=2))
399
+ print(json_dumps(all_jobs, indent=2))
403
400
 
404
401
  def get_job_by_repo_url(self, repo_url: str, job_type: str) -> dict[str, Any]:
405
402
  for jobs in self.get_all_jobs(job_types=[job_type]).values():
@@ -100,7 +100,7 @@ class K8sJobController:
100
100
  """
101
101
  new_cache = {}
102
102
  for item in self.oc.get_items(
103
- kind="Job",
103
+ kind="Job.batch",
104
104
  namespace=self.namespace,
105
105
  ):
106
106
  openshift_resource = OpenshiftResource(
@@ -38,6 +38,8 @@ class JobValidationError(Exception):
38
38
 
39
39
 
40
40
  JOB_GENERATION_ANNOTATION = "qontract-reconcile/job.generation"
41
+ MAX_JOB_NAME_LENGTH = 63
42
+ UNIT_OF_WORK_DIGEST_LENGTH = 10
41
43
 
42
44
 
43
45
  class K8sJob(ABC):
@@ -72,7 +74,21 @@ class K8sJob(ABC):
72
74
  """
73
75
 
74
76
  def name(self) -> str:
75
- return f"{self.name_prefix()}-{self.unit_of_work_digest()}"
77
+ """
78
+ Generate the full job name by combining the name prefix with a digest.
79
+
80
+ The name is constructed from the name_prefix (truncated to ensure total
81
+ length compliance) and the unit_of_work_digest. The total length is
82
+ limited to MAX_JOB_NAME_LENGTH (63 characters) to comply with Kubernetes
83
+ naming constraints.
84
+
85
+ Returns:
86
+ A unique job name in the format: {name_prefix}-{digest}
87
+ """
88
+ prefix = self.name_prefix()[
89
+ : MAX_JOB_NAME_LENGTH - UNIT_OF_WORK_DIGEST_LENGTH - 1
90
+ ]
91
+ return f"{prefix}-{self.unit_of_work_digest(UNIT_OF_WORK_DIGEST_LENGTH)}"
76
92
 
77
93
  @abstractmethod
78
94
  def name_prefix(self) -> str: