qontract-reconcile 0.10.2.dev310__py3-none-any.whl → 0.10.2.dev439__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.
Potentially problematic release.
This version of qontract-reconcile might be problematic. Click here for more details.
- {qontract_reconcile-0.10.2.dev310.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/METADATA +13 -12
- {qontract_reconcile-0.10.2.dev310.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/RECORD +396 -391
- reconcile/acs_rbac.py +2 -2
- reconcile/aus/advanced_upgrade_service.py +18 -12
- reconcile/aus/base.py +134 -32
- reconcile/aus/cluster_version_data.py +15 -5
- reconcile/aus/models.py +3 -1
- reconcile/aus/ocm_addons_upgrade_scheduler_org.py +1 -0
- reconcile/aus/ocm_upgrade_scheduler.py +8 -1
- reconcile/aus/ocm_upgrade_scheduler_org.py +20 -5
- reconcile/aus/version_gates/sts_version_gate_handler.py +54 -1
- reconcile/automated_actions/config/integration.py +16 -4
- reconcile/aws_account_manager/integration.py +8 -8
- reconcile/aws_account_manager/reconciler.py +3 -3
- reconcile/aws_ami_cleanup/integration.py +8 -12
- reconcile/aws_ami_share.py +69 -62
- reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
- reconcile/aws_ecr_image_pull_secrets.py +5 -5
- reconcile/aws_iam_keys.py +1 -0
- reconcile/aws_saml_idp/integration.py +12 -4
- reconcile/aws_saml_roles/integration.py +32 -25
- reconcile/aws_version_sync/integration.py +125 -84
- reconcile/change_owners/bundle.py +3 -3
- reconcile/change_owners/change_log_tracking.py +3 -2
- reconcile/change_owners/change_owners.py +1 -1
- reconcile/change_owners/diff.py +2 -4
- reconcile/checkpoint.py +12 -4
- reconcile/cli.py +111 -18
- reconcile/cluster_deployment_mapper.py +2 -3
- reconcile/dashdotdb_dora.py +5 -12
- reconcile/dashdotdb_slo.py +1 -1
- reconcile/database_access_manager.py +125 -121
- reconcile/deadmanssnitch.py +1 -5
- reconcile/dynatrace_token_provider/integration.py +1 -1
- reconcile/endpoints_discovery/integration.py +4 -1
- reconcile/endpoints_discovery/merge_request.py +1 -1
- reconcile/endpoints_discovery/merge_request_manager.py +9 -11
- reconcile/external_resources/factories.py +5 -12
- reconcile/external_resources/integration.py +1 -1
- reconcile/external_resources/manager.py +8 -5
- reconcile/external_resources/meta.py +0 -1
- reconcile/external_resources/metrics.py +1 -1
- reconcile/external_resources/model.py +20 -20
- reconcile/external_resources/reconciler.py +7 -4
- reconcile/external_resources/secrets_sync.py +10 -14
- reconcile/external_resources/state.py +26 -16
- reconcile/fleet_labeler/integration.py +1 -1
- reconcile/gabi_authorized_users.py +8 -5
- reconcile/gcp_image_mirror.py +2 -2
- reconcile/github_org.py +1 -1
- reconcile/github_owners.py +4 -0
- reconcile/gitlab_housekeeping.py +13 -15
- reconcile/gitlab_members.py +6 -12
- reconcile/gitlab_mr_sqs_consumer.py +2 -2
- reconcile/gitlab_owners.py +15 -11
- reconcile/gitlab_permissions.py +8 -12
- reconcile/glitchtip_project_alerts/integration.py +3 -1
- reconcile/gql_definitions/acs/acs_instances.py +10 -10
- reconcile/gql_definitions/acs/acs_policies.py +5 -5
- reconcile/gql_definitions/acs/acs_rbac.py +6 -6
- reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +32 -32
- reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +26 -26
- reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +6 -7
- reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
- reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
- reconcile/gql_definitions/automated_actions/instance.py +51 -12
- reconcile/gql_definitions/aws_account_manager/aws_accounts.py +11 -11
- reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +20 -10
- reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +28 -68
- reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +20 -10
- reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +20 -10
- reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
- reconcile/gql_definitions/aws_version_sync/clusters.py +10 -10
- reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
- reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
- reconcile/gql_definitions/change_owners/queries/self_service_roles.py +9 -9
- reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +18 -18
- reconcile/gql_definitions/common/alerting_services_settings.py +9 -9
- reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
- reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
- reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_roles.py +120 -0
- reconcile/gql_definitions/common/app_interface_state_settings.py +10 -10
- reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
- reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
- reconcile/gql_definitions/common/apps.py +5 -5
- reconcile/gql_definitions/common/aws_vpc_requests.py +22 -9
- reconcile/gql_definitions/common/aws_vpcs.py +11 -11
- reconcile/gql_definitions/common/clusters.py +37 -35
- reconcile/gql_definitions/common/clusters_minimal.py +14 -14
- reconcile/gql_definitions/common/clusters_with_dms.py +6 -6
- reconcile/gql_definitions/common/clusters_with_peering.py +29 -30
- reconcile/gql_definitions/common/github_orgs.py +10 -10
- reconcile/gql_definitions/common/jira_settings.py +10 -10
- reconcile/gql_definitions/common/jiralert_settings.py +5 -5
- reconcile/gql_definitions/common/ldap_settings.py +5 -5
- reconcile/gql_definitions/common/namespaces.py +42 -44
- reconcile/gql_definitions/common/namespaces_minimal.py +15 -13
- reconcile/gql_definitions/common/ocm_env_telemeter.py +12 -12
- reconcile/gql_definitions/common/ocm_environments.py +19 -19
- reconcile/gql_definitions/common/pagerduty_instances.py +9 -9
- reconcile/gql_definitions/common/pgp_reencryption_settings.py +6 -6
- reconcile/gql_definitions/common/pipeline_providers.py +29 -29
- reconcile/gql_definitions/common/quay_instances.py +5 -5
- reconcile/gql_definitions/common/quay_orgs.py +5 -5
- reconcile/gql_definitions/common/reserved_networks.py +5 -5
- reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
- reconcile/gql_definitions/common/saas_files.py +44 -44
- reconcile/gql_definitions/common/saas_target_namespaces.py +10 -10
- reconcile/gql_definitions/common/saasherder_settings.py +5 -5
- reconcile/gql_definitions/common/slack_workspaces.py +5 -5
- reconcile/gql_definitions/common/smtp_client_settings.py +19 -19
- reconcile/gql_definitions/common/state_aws_account.py +7 -8
- reconcile/gql_definitions/common/users.py +5 -5
- reconcile/gql_definitions/common/users_with_paths.py +5 -5
- reconcile/gql_definitions/cost_report/app_names.py +5 -5
- reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
- reconcile/gql_definitions/cost_report/settings.py +9 -9
- reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +43 -43
- reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +10 -10
- reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
- reconcile/gql_definitions/email_sender/apps.py +5 -5
- reconcile/gql_definitions/email_sender/emails.py +8 -8
- reconcile/gql_definitions/email_sender/users.py +6 -6
- reconcile/gql_definitions/endpoints_discovery/apps.py +10 -10
- reconcile/gql_definitions/external_resources/aws_accounts.py +9 -9
- reconcile/gql_definitions/external_resources/external_resources_modules.py +23 -23
- reconcile/gql_definitions/external_resources/external_resources_namespaces.py +494 -410
- reconcile/gql_definitions/external_resources/external_resources_settings.py +28 -26
- reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
- reconcile/gql_definitions/fleet_labeler/fleet_labels.py +40 -40
- reconcile/gql_definitions/fragments/aus_organization.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
- reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
- reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
- reconcile/gql_definitions/fragments/{aws_vpc_request_subnet.py → aws_organization.py} +12 -8
- reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
- reconcile/gql_definitions/fragments/aws_vpc_request.py +12 -5
- reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
- reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
- reconcile/gql_definitions/fragments/disable.py +5 -5
- reconcile/gql_definitions/fragments/email_service.py +5 -5
- reconcile/gql_definitions/fragments/email_user.py +5 -5
- reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
- reconcile/gql_definitions/fragments/membership_source.py +5 -5
- reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
- reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
- reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
- reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
- reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
- reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
- reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
- reconcile/gql_definitions/fragments/resource_values.py +5 -5
- reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
- reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
- reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
- reconcile/gql_definitions/fragments/terraform_state.py +5 -5
- reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
- reconcile/gql_definitions/fragments/user.py +5 -5
- reconcile/gql_definitions/fragments/vault_secret.py +5 -5
- reconcile/gql_definitions/gcp/gcp_docker_repos.py +9 -9
- reconcile/gql_definitions/gcp/gcp_projects.py +9 -9
- reconcile/gql_definitions/gitlab_members/gitlab_instances.py +9 -9
- reconcile/gql_definitions/gitlab_members/permissions.py +9 -9
- reconcile/gql_definitions/glitchtip/glitchtip_instance.py +9 -9
- reconcile/gql_definitions/glitchtip/glitchtip_project.py +11 -11
- reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +9 -9
- reconcile/gql_definitions/integrations/integrations.py +48 -51
- reconcile/gql_definitions/introspection.json +3510 -1865
- reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +11 -11
- reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +10 -10
- reconcile/gql_definitions/jira/jira_servers.py +5 -5
- reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +14 -10
- reconcile/gql_definitions/jumphosts/jumphosts.py +13 -13
- reconcile/gql_definitions/ldap_groups/roles.py +5 -5
- reconcile/gql_definitions/ldap_groups/settings.py +9 -9
- reconcile/gql_definitions/maintenance/maintenances.py +5 -5
- reconcile/gql_definitions/membershipsources/roles.py +5 -5
- reconcile/gql_definitions/ocm_labels/clusters.py +18 -19
- reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
- reconcile/gql_definitions/openshift_cluster_bots/clusters.py +22 -22
- reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
- reconcile/gql_definitions/openshift_groups/managed_roles.py +6 -6
- reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +10 -10
- reconcile/gql_definitions/quay_membership/quay_membership.py +6 -6
- reconcile/gql_definitions/rhcs/certs.py +33 -87
- reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +43 -0
- reconcile/gql_definitions/rhidp/organizations.py +18 -18
- reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
- reconcile/gql_definitions/service_dependencies/service_dependencies.py +8 -8
- reconcile/gql_definitions/sharding/aws_accounts.py +10 -10
- reconcile/gql_definitions/sharding/ocm_organization.py +8 -8
- reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
- reconcile/gql_definitions/skupper_network/skupper_networks.py +10 -10
- reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
- reconcile/gql_definitions/slack_usergroups/permissions.py +9 -9
- reconcile/gql_definitions/slack_usergroups/users.py +5 -5
- reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
- reconcile/gql_definitions/status_board/status_board.py +6 -7
- reconcile/gql_definitions/statuspage/statuspages.py +9 -9
- reconcile/gql_definitions/templating/template_collection.py +5 -5
- reconcile/gql_definitions/templating/templates.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +6 -6
- reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +11 -11
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +11 -11
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +20 -25
- reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +6 -6
- reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +12 -12
- reconcile/gql_definitions/terraform_init/aws_accounts.py +23 -9
- reconcile/gql_definitions/terraform_repo/terraform_repo.py +9 -9
- reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
- reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +450 -402
- reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +23 -17
- reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +9 -9
- reconcile/gql_definitions/vault_instances/vault_instances.py +61 -61
- reconcile/gql_definitions/vault_policies/vault_policies.py +11 -11
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +8 -8
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
- reconcile/integrations_manager.py +3 -3
- reconcile/jenkins_job_builder.py +1 -1
- reconcile/jenkins_worker_fleets.py +80 -11
- reconcile/jira_permissions_validator.py +237 -122
- reconcile/ldap_groups/integration.py +1 -1
- reconcile/ocm/types.py +35 -56
- reconcile/ocm_aws_infrastructure_access.py +1 -1
- reconcile/ocm_clusters.py +4 -4
- reconcile/ocm_labels/integration.py +3 -2
- reconcile/ocm_machine_pools.py +33 -27
- reconcile/openshift_base.py +122 -10
- reconcile/openshift_cluster_bots.py +5 -5
- reconcile/openshift_groups.py +5 -0
- reconcile/openshift_limitranges.py +1 -1
- reconcile/openshift_namespace_labels.py +1 -1
- reconcile/openshift_namespaces.py +97 -101
- reconcile/openshift_resources_base.py +10 -5
- reconcile/openshift_rhcs_certs.py +77 -40
- reconcile/openshift_rolebindings.py +230 -130
- reconcile/openshift_saas_deploy.py +6 -7
- reconcile/openshift_saas_deploy_change_tester.py +9 -7
- reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
- reconcile/openshift_serviceaccount_tokens.py +8 -7
- reconcile/openshift_tekton_resources.py +1 -1
- reconcile/openshift_upgrade_watcher.py +4 -4
- reconcile/openshift_users.py +5 -3
- reconcile/oum/labelset.py +5 -3
- reconcile/oum/models.py +1 -4
- reconcile/oum/providers.py +1 -1
- reconcile/prometheus_rules_tester/integration.py +4 -4
- reconcile/quay_mirror.py +1 -1
- reconcile/queries.py +131 -0
- reconcile/requests_sender.py +8 -3
- reconcile/resource_scraper.py +1 -5
- reconcile/rhidp/common.py +3 -5
- reconcile/rhidp/sso_client/base.py +19 -10
- reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
- reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
- reconcile/sendgrid_teammates.py +20 -9
- reconcile/skupper_network/integration.py +2 -2
- reconcile/slack_usergroups.py +35 -14
- reconcile/sql_query.py +1 -0
- reconcile/status.py +2 -2
- reconcile/status_board.py +6 -6
- reconcile/statuspage/atlassian.py +7 -7
- reconcile/statuspage/integrations/maintenances.py +4 -3
- reconcile/statuspage/page.py +4 -9
- reconcile/statuspage/status.py +5 -8
- reconcile/templates/rosa-classic-cluster-creation.sh.j2 +5 -1
- reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +4 -1
- reconcile/templating/lib/merge_request_manager.py +2 -2
- reconcile/templating/lib/rendering.py +3 -3
- reconcile/templating/renderer.py +12 -13
- reconcile/terraform_aws_route53.py +18 -8
- reconcile/terraform_cloudflare_dns.py +3 -3
- reconcile/terraform_cloudflare_resources.py +12 -13
- reconcile/terraform_cloudflare_users.py +3 -2
- reconcile/terraform_init/integration.py +187 -23
- reconcile/terraform_repo.py +16 -12
- reconcile/terraform_resources.py +18 -10
- reconcile/terraform_tgw_attachments.py +28 -20
- reconcile/terraform_users.py +27 -22
- reconcile/terraform_vpc_peerings.py +15 -3
- reconcile/terraform_vpc_resources/integration.py +23 -8
- reconcile/typed_queries/app_interface_roles.py +10 -0
- reconcile/typed_queries/aws_account_tags.py +41 -0
- reconcile/typed_queries/cost_report/app_names.py +1 -1
- reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
- reconcile/typed_queries/saas_files.py +13 -13
- reconcile/typed_queries/status_board.py +2 -2
- reconcile/unleash_feature_toggles/integration.py +4 -2
- reconcile/utils/acs/base.py +6 -3
- reconcile/utils/acs/policies.py +2 -2
- reconcile/utils/aggregated_list.py +4 -3
- reconcile/utils/aws_api.py +51 -20
- reconcile/utils/aws_api_typed/api.py +38 -9
- reconcile/utils/aws_api_typed/cloudformation.py +149 -0
- reconcile/utils/aws_api_typed/logs.py +73 -0
- reconcile/utils/aws_api_typed/organization.py +4 -2
- reconcile/utils/binary.py +7 -12
- reconcile/utils/datetime_util.py +67 -0
- reconcile/utils/deadmanssnitch_api.py +1 -1
- reconcile/utils/differ.py +2 -3
- reconcile/utils/early_exit_cache.py +11 -12
- reconcile/utils/expiration.py +7 -3
- reconcile/utils/external_resource_spec.py +24 -1
- reconcile/utils/filtering.py +1 -1
- reconcile/utils/gitlab_api.py +7 -5
- reconcile/utils/glitchtip/client.py +6 -2
- reconcile/utils/glitchtip/models.py +25 -28
- reconcile/utils/gpg.py +5 -3
- reconcile/utils/gql.py +4 -7
- reconcile/utils/helm.py +2 -1
- reconcile/utils/helpers.py +1 -1
- reconcile/utils/imap_client.py +1 -1
- reconcile/utils/instrumented_wrappers.py +1 -1
- reconcile/utils/internal_groups/client.py +2 -2
- reconcile/utils/internal_groups/models.py +8 -17
- reconcile/utils/jenkins_api.py +24 -1
- reconcile/utils/jinja2/utils.py +6 -8
- reconcile/utils/jira_client.py +82 -63
- reconcile/utils/jjb_client.py +78 -46
- reconcile/utils/jobcontroller/controller.py +2 -2
- reconcile/utils/jobcontroller/models.py +17 -1
- reconcile/utils/json.py +74 -0
- reconcile/utils/ldap_client.py +4 -3
- reconcile/utils/lean_terraform_client.py +3 -1
- reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
- reconcile/utils/membershipsources/models.py +16 -23
- reconcile/utils/membershipsources/resolver.py +4 -2
- reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
- reconcile/utils/merge_request_manager/parser.py +6 -6
- reconcile/utils/metrics.py +5 -5
- reconcile/utils/models.py +304 -82
- reconcile/utils/mr/__init__.py +3 -1
- reconcile/utils/mr/app_interface_reporter.py +6 -3
- reconcile/utils/mr/aws_access.py +1 -1
- reconcile/utils/mr/base.py +7 -13
- reconcile/utils/mr/clusters_updates.py +4 -2
- reconcile/utils/mr/notificator.py +3 -3
- reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py +4 -1
- reconcile/utils/mr/promote_qontract.py +28 -12
- reconcile/utils/mr/update_access_report_base.py +3 -4
- reconcile/utils/mr/user_maintenance.py +7 -6
- reconcile/utils/oc.py +445 -336
- reconcile/utils/oc_filters.py +3 -3
- reconcile/utils/ocm/addons.py +0 -1
- reconcile/utils/ocm/base.py +18 -21
- reconcile/utils/ocm/cluster_groups.py +1 -1
- reconcile/utils/ocm/identity_providers.py +2 -2
- reconcile/utils/ocm/labels.py +1 -1
- reconcile/utils/ocm/ocm.py +81 -71
- reconcile/utils/ocm/products.py +9 -3
- reconcile/utils/ocm/search_filters.py +3 -6
- reconcile/utils/ocm/service_log.py +4 -6
- reconcile/utils/ocm/sre_capability_labels.py +20 -13
- reconcile/utils/ocm_base_client.py +4 -4
- reconcile/utils/openshift_resource.py +83 -52
- reconcile/utils/openssl.py +2 -2
- reconcile/utils/output.py +3 -2
- reconcile/utils/pagerduty_api.py +10 -7
- reconcile/utils/promotion_state.py +6 -11
- reconcile/utils/raw_github_api.py +11 -8
- reconcile/utils/repo_owners.py +21 -29
- reconcile/utils/rhcsv2_certs.py +138 -35
- reconcile/utils/rosa/session.py +16 -0
- reconcile/utils/runtime/integration.py +2 -3
- reconcile/utils/runtime/meta.py +2 -1
- reconcile/utils/runtime/runner.py +2 -2
- reconcile/utils/saasherder/interfaces.py +13 -20
- reconcile/utils/saasherder/models.py +25 -21
- reconcile/utils/saasherder/saasherder.py +60 -32
- reconcile/utils/secret_reader.py +6 -6
- reconcile/utils/sharding.py +1 -1
- reconcile/utils/slack_api.py +26 -4
- reconcile/utils/sloth.py +224 -0
- reconcile/utils/sqs_gateway.py +16 -11
- reconcile/utils/state.py +2 -1
- reconcile/utils/structs.py +1 -1
- reconcile/utils/terraform_client.py +29 -26
- reconcile/utils/terrascript_aws_client.py +200 -116
- reconcile/utils/three_way_diff_strategy.py +1 -1
- reconcile/utils/unleash/server.py +2 -8
- reconcile/utils/vault.py +44 -41
- reconcile/utils/vcs.py +8 -8
- reconcile/vault_replication.py +119 -58
- tools/app_interface_reporter.py +4 -4
- tools/cli_commands/cost_report/cost_management_api.py +3 -3
- tools/cli_commands/cost_report/view.py +7 -6
- tools/cli_commands/erv2.py +1 -1
- tools/cli_commands/gpg_encrypt.py +4 -1
- tools/cli_commands/systems_and_tools.py +5 -1
- tools/qontract_cli.py +36 -21
- tools/template_validation.py +3 -1
- reconcile/gql_definitions/ocm_oidc_idp/__init__.py +0 -0
- reconcile/gql_definitions/ocm_subscription_labels/__init__.py +0 -0
- reconcile/jenkins/__init__.py +0 -0
- reconcile/jenkins/types.py +0 -77
- {qontract_reconcile-0.10.2.dev310.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev310.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/entry_points.txt +0 -0
|
@@ -24,7 +24,7 @@ from reconcile.typed_queries.jiralert_settings import get_jiralert_settings
|
|
|
24
24
|
from reconcile.utils import gql, metrics
|
|
25
25
|
from reconcile.utils.defer import defer
|
|
26
26
|
from reconcile.utils.disabled_integrations import integration_is_enabled
|
|
27
|
-
from reconcile.utils.jira_client import JiraClient, JiraWatcherSettings
|
|
27
|
+
from reconcile.utils.jira_client import IssueType, JiraClient, JiraWatcherSettings
|
|
28
28
|
from reconcile.utils.runtime.integration import DesiredStateShardConfig
|
|
29
29
|
from reconcile.utils.secret_reader import SecretReaderBase, create_secret_reader
|
|
30
30
|
from reconcile.utils.semver_helper import make_semver
|
|
@@ -33,8 +33,6 @@ from reconcile.utils.state import State, init_state
|
|
|
33
33
|
QONTRACT_INTEGRATION = "jira-permissions-validator"
|
|
34
34
|
QONTRACT_INTEGRATION_VERSION = make_semver(1, 2, 0)
|
|
35
35
|
|
|
36
|
-
NameToIdMap = dict[str, str]
|
|
37
|
-
|
|
38
36
|
|
|
39
37
|
class BaseMetric(BaseModel):
|
|
40
38
|
"""Base class for metrics"""
|
|
@@ -64,9 +62,6 @@ class ValidationError(IntFlag):
|
|
|
64
62
|
PROJECT_ARCHIVED = auto()
|
|
65
63
|
|
|
66
64
|
|
|
67
|
-
CustomIssueFields = dict[str, dict[str, str]]
|
|
68
|
-
|
|
69
|
-
|
|
70
65
|
class RunnerParams(TypedDict):
|
|
71
66
|
boards: list[JiraBoardV1]
|
|
72
67
|
board_check_interval_sec: int
|
|
@@ -77,6 +72,179 @@ class CacheSource(TypedDict):
|
|
|
77
72
|
boards: list
|
|
78
73
|
|
|
79
74
|
|
|
75
|
+
NameToIdMap = dict[str, str]
|
|
76
|
+
CustomIssueFields = dict[str, dict[str, str]]
|
|
77
|
+
NO_ERRORS = ValidationError(0)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _validate_project_archived(jira: JiraClient, board: JiraBoardV1) -> ValidationError:
|
|
81
|
+
"""Validate that the project is not archived."""
|
|
82
|
+
if jira.is_archived:
|
|
83
|
+
logging.error(f"[{board.name}] project is archived")
|
|
84
|
+
return ValidationError.PROJECT_ARCHIVED
|
|
85
|
+
return NO_ERRORS
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _validate_permissions(jira: JiraClient, board: JiraBoardV1) -> ValidationError:
|
|
89
|
+
"""Validate that the bot has necessary permissions to create and transition issues."""
|
|
90
|
+
error = NO_ERRORS
|
|
91
|
+
|
|
92
|
+
if not jira.can_create_issues():
|
|
93
|
+
logging.error(f"[{board.name}] can not create issues in project")
|
|
94
|
+
error |= ValidationError.CANT_CREATE_ISSUE
|
|
95
|
+
|
|
96
|
+
if not jira.can_transition_issues():
|
|
97
|
+
logging.error(
|
|
98
|
+
f"[{board.name}] AppSRE Jira Bot user does not have the permission to change the issue status."
|
|
99
|
+
)
|
|
100
|
+
error |= ValidationError.CANT_TRANSITION_ISSUES
|
|
101
|
+
|
|
102
|
+
return error
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _validate_components(jira: JiraClient, board: JiraBoardV1) -> ValidationError:
|
|
106
|
+
"""Validate that all escalation policy components exist in the project."""
|
|
107
|
+
error = NO_ERRORS
|
|
108
|
+
components = jira.components()
|
|
109
|
+
|
|
110
|
+
for escalation_policy in board.escalation_policies or []:
|
|
111
|
+
for jira_component in escalation_policy.channels.jira_components or []:
|
|
112
|
+
if jira_component not in components:
|
|
113
|
+
logging.error(
|
|
114
|
+
f"[{board.name}] escalation policy '{escalation_policy.name}' references a non existing Jira component "
|
|
115
|
+
f"'{jira_component}'. Valid components: {components}"
|
|
116
|
+
)
|
|
117
|
+
error |= ValidationError.INVALID_COMPONENT
|
|
118
|
+
|
|
119
|
+
return error
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def _validate_issue_type(
|
|
123
|
+
jira: JiraClient, board: JiraBoardV1, default_issue_type: str
|
|
124
|
+
) -> tuple[ValidationError, IssueType | None]:
|
|
125
|
+
"""Validate that the issue type exists and has statuses."""
|
|
126
|
+
error = NO_ERRORS
|
|
127
|
+
issue_type = board.issue_type or default_issue_type
|
|
128
|
+
project_issue_type = jira.get_issue_type(issue_type)
|
|
129
|
+
|
|
130
|
+
if not project_issue_type:
|
|
131
|
+
project_issue_types_str = ", ".join(t.name for t in jira.project_issue_types())
|
|
132
|
+
logging.error(
|
|
133
|
+
f"[{board.name}] '{issue_type}' is not a valid issue type in project. Valid issue types: {project_issue_types_str}"
|
|
134
|
+
)
|
|
135
|
+
error |= ValidationError.INVALID_ISSUE_TYPE
|
|
136
|
+
return error, None
|
|
137
|
+
|
|
138
|
+
if not project_issue_type.statuses:
|
|
139
|
+
logging.error(
|
|
140
|
+
f"[{board.name}] '{issue_type}' doesn't have any status. Choose a different issue type."
|
|
141
|
+
)
|
|
142
|
+
error |= ValidationError.INVALID_ISSUE_TYPE
|
|
143
|
+
|
|
144
|
+
return error, project_issue_type
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _validate_issue_states(
|
|
148
|
+
board: JiraBoardV1, project_issue_type: IssueType, default_reopen_state: str
|
|
149
|
+
) -> ValidationError:
|
|
150
|
+
"""Validate that reopen and resolve states are valid for the issue type."""
|
|
151
|
+
error = NO_ERRORS
|
|
152
|
+
|
|
153
|
+
reopen_state = board.issue_reopen_state or default_reopen_state
|
|
154
|
+
if reopen_state.lower() not in [t.lower() for t in project_issue_type.statuses]:
|
|
155
|
+
logging.error(
|
|
156
|
+
f"[{board.name}] '{reopen_state}' is not a valid state in project. Valid states: {project_issue_type.statuses}"
|
|
157
|
+
)
|
|
158
|
+
error |= ValidationError.INVALID_ISSUE_STATE
|
|
159
|
+
|
|
160
|
+
if board.issue_resolve_state and board.issue_resolve_state.lower() not in [
|
|
161
|
+
t.lower() for t in project_issue_type.statuses
|
|
162
|
+
]:
|
|
163
|
+
logging.error(
|
|
164
|
+
f"[{board.name}] '{board.issue_resolve_state}' is not a valid state in project. Valid states: {project_issue_type.statuses}"
|
|
165
|
+
)
|
|
166
|
+
error |= ValidationError.INVALID_ISSUE_STATE
|
|
167
|
+
|
|
168
|
+
return error
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def _validate_issue_fields(
|
|
172
|
+
jira: JiraClient, board: JiraBoardV1, project_issue_type: IssueType
|
|
173
|
+
) -> tuple[ValidationError, CustomIssueFields]:
|
|
174
|
+
"""Validate that custom fields exist and have valid values."""
|
|
175
|
+
error = NO_ERRORS
|
|
176
|
+
custom_fields: CustomIssueFields = {}
|
|
177
|
+
|
|
178
|
+
for field in board.issue_fields or []:
|
|
179
|
+
project_issue_field = jira.project_issue_field(
|
|
180
|
+
issue_type_id=project_issue_type.id, field=field.name
|
|
181
|
+
)
|
|
182
|
+
if not project_issue_field:
|
|
183
|
+
logging.error(
|
|
184
|
+
f"[{board.name}] '{field.name}' is not a valid field for '{project_issue_type.name}' in this project."
|
|
185
|
+
)
|
|
186
|
+
error |= ValidationError.INVALID_ISSUE_FIELD
|
|
187
|
+
continue
|
|
188
|
+
|
|
189
|
+
for option in project_issue_field.options:
|
|
190
|
+
if option == field.value:
|
|
191
|
+
custom_fields[project_issue_field.id] = option.model_dump()
|
|
192
|
+
break
|
|
193
|
+
else:
|
|
194
|
+
logging.error(
|
|
195
|
+
f"[{board.name}] '{field.name}' has an invalid value '{field.value}'. Valid values: {project_issue_field.options}"
|
|
196
|
+
)
|
|
197
|
+
error |= ValidationError.INVALID_ISSUE_FIELD
|
|
198
|
+
|
|
199
|
+
return error, custom_fields
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _validate_security_level(
|
|
203
|
+
board: JiraBoardV1, public_projects: Iterable[str]
|
|
204
|
+
) -> ValidationError:
|
|
205
|
+
"""Validate that public projects have a security level defined."""
|
|
206
|
+
if board.name in public_projects and "Security Level" not in [
|
|
207
|
+
f.name for f in board.issue_fields or []
|
|
208
|
+
]:
|
|
209
|
+
logging.error(
|
|
210
|
+
f"[{board.name}] is a public project, but the security level is not defined. Please add 'Security Level' field to the issueFields!"
|
|
211
|
+
)
|
|
212
|
+
return ValidationError.PUBLIC_PROJECT_NO_SECURITY_LEVEL
|
|
213
|
+
return NO_ERRORS
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _validate_priorities(
|
|
217
|
+
jira: JiraClient, board: JiraBoardV1, jira_server_priorities: NameToIdMap
|
|
218
|
+
) -> ValidationError:
|
|
219
|
+
"""Validate that all priority mappings are valid for the project."""
|
|
220
|
+
error = NO_ERRORS
|
|
221
|
+
project_priorities = jira.project_priority_scheme()
|
|
222
|
+
project_priorities_names = [
|
|
223
|
+
p_name
|
|
224
|
+
for project_p_id in project_priorities
|
|
225
|
+
for p_name, p_id in jira_server_priorities.items()
|
|
226
|
+
if p_id == project_p_id
|
|
227
|
+
]
|
|
228
|
+
|
|
229
|
+
for priority in board.severity_priority_mappings.mappings:
|
|
230
|
+
if priority.priority not in jira_server_priorities:
|
|
231
|
+
logging.error(
|
|
232
|
+
f"[{board.name}] {priority.priority} is not a valid Jira priority. Valid priorities: {project_priorities_names}"
|
|
233
|
+
)
|
|
234
|
+
error |= ValidationError.INVALID_PRIORITY
|
|
235
|
+
continue
|
|
236
|
+
if (
|
|
237
|
+
project_priorities
|
|
238
|
+
and jira_server_priorities[priority.priority] not in project_priorities
|
|
239
|
+
):
|
|
240
|
+
logging.error(
|
|
241
|
+
f"[{board.name}] {priority.priority} is not a valid priority in project. Valid priorities: {project_priorities_names}"
|
|
242
|
+
)
|
|
243
|
+
error |= ValidationError.INVALID_PRIORITY
|
|
244
|
+
|
|
245
|
+
return error
|
|
246
|
+
|
|
247
|
+
|
|
80
248
|
def board_is_valid(
|
|
81
249
|
jira: JiraClient,
|
|
82
250
|
board: JiraBoardV1,
|
|
@@ -85,123 +253,52 @@ def board_is_valid(
|
|
|
85
253
|
jira_server_priorities: NameToIdMap,
|
|
86
254
|
public_projects: Iterable[str],
|
|
87
255
|
) -> tuple[ValidationError, CustomIssueFields]:
|
|
88
|
-
|
|
256
|
+
"""Validate all Jira board settings.
|
|
257
|
+
|
|
258
|
+
This method orchestrates multiple validation checks for a Jira board configuration.
|
|
259
|
+
Each validation is performed by a dedicated helper function that focuses on a specific
|
|
260
|
+
aspect of the board configuration.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
A tuple of (ValidationError flags, custom_fields dict)
|
|
264
|
+
"""
|
|
265
|
+
error = NO_ERRORS
|
|
89
266
|
custom_fields: CustomIssueFields = {}
|
|
90
267
|
|
|
91
268
|
try:
|
|
92
|
-
if
|
|
93
|
-
|
|
94
|
-
|
|
269
|
+
# Check if project is archived (early exit)
|
|
270
|
+
archived_error = _validate_project_archived(jira, board)
|
|
271
|
+
if archived_error:
|
|
272
|
+
return archived_error, custom_fields
|
|
95
273
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
error |= ValidationError.CANT_CREATE_ISSUE
|
|
274
|
+
# Validate permissions
|
|
275
|
+
error |= _validate_permissions(jira, board)
|
|
99
276
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
f"[{board.name}] AppSRE Jira Bot user does not have the permission to change the issue status."
|
|
103
|
-
)
|
|
104
|
-
error |= ValidationError.CANT_TRANSITION_ISSUES
|
|
105
|
-
|
|
106
|
-
components = jira.components()
|
|
107
|
-
for escalation_policy in board.escalation_policies or []:
|
|
108
|
-
for jira_component in escalation_policy.channels.jira_components or []:
|
|
109
|
-
if jira_component not in components:
|
|
110
|
-
logging.error(
|
|
111
|
-
f"[{board.name}] escalation policy '{escalation_policy.name}' references a non existing Jira component "
|
|
112
|
-
f"'{jira_component}'. Valid components: {components}"
|
|
113
|
-
)
|
|
114
|
-
error |= ValidationError.INVALID_COMPONENT
|
|
277
|
+
# Validate components
|
|
278
|
+
error |= _validate_components(jira, board)
|
|
115
279
|
|
|
116
|
-
|
|
117
|
-
project_issue_type =
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
)
|
|
122
|
-
logging.error(
|
|
123
|
-
f"[{board.name}] '{issue_type}' is not a valid issue type in project. Valid issue types: {project_issue_types_str}"
|
|
124
|
-
)
|
|
125
|
-
error |= ValidationError.INVALID_ISSUE_TYPE
|
|
280
|
+
# Validate issue type
|
|
281
|
+
issue_type_error, project_issue_type = _validate_issue_type(
|
|
282
|
+
jira, board, default_issue_type
|
|
283
|
+
)
|
|
284
|
+
error |= issue_type_error
|
|
126
285
|
|
|
286
|
+
# If issue type is valid, perform additional validations
|
|
127
287
|
if project_issue_type:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
reopen_state = board.issue_reopen_state or default_reopen_state
|
|
136
|
-
if reopen_state.lower() not in [
|
|
137
|
-
t.lower() for t in project_issue_type.statuses
|
|
138
|
-
]:
|
|
139
|
-
logging.error(
|
|
140
|
-
f"[{board.name}] '{reopen_state}' is not a valid state in project. Valid states: {project_issue_type.statuses}"
|
|
141
|
-
)
|
|
142
|
-
error |= ValidationError.INVALID_ISSUE_STATE
|
|
143
|
-
|
|
144
|
-
if board.issue_resolve_state and board.issue_resolve_state.lower() not in [
|
|
145
|
-
t.lower() for t in project_issue_type.statuses
|
|
146
|
-
]:
|
|
147
|
-
logging.error(
|
|
148
|
-
f"[{board.name}] '{board.issue_resolve_state}' is not a valid state in project. Valid states: {project_issue_type.statuses}"
|
|
149
|
-
)
|
|
150
|
-
error |= ValidationError.INVALID_ISSUE_STATE
|
|
288
|
+
error |= _validate_issue_states(
|
|
289
|
+
board, project_issue_type, default_reopen_state
|
|
290
|
+
)
|
|
291
|
+
fields_error, custom_fields = _validate_issue_fields(
|
|
292
|
+
jira, board, project_issue_type
|
|
293
|
+
)
|
|
294
|
+
error |= fields_error
|
|
151
295
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
issue_type_id=project_issue_type.id, field=field.name
|
|
155
|
-
)
|
|
156
|
-
if not project_issue_field:
|
|
157
|
-
logging.error(
|
|
158
|
-
f"[{board.name}] '{field.name}' is not a valid field for '{project_issue_type.name}' in this project."
|
|
159
|
-
)
|
|
296
|
+
# Validate security level for public projects
|
|
297
|
+
error |= _validate_security_level(board, public_projects)
|
|
160
298
|
|
|
161
|
-
|
|
162
|
-
|
|
299
|
+
# Validate priorities
|
|
300
|
+
error |= _validate_priorities(jira, board, jira_server_priorities)
|
|
163
301
|
|
|
164
|
-
for option in project_issue_field.options:
|
|
165
|
-
if option == field.value:
|
|
166
|
-
# cache the field id and option value/name for later use, e.g. in jiralert templates
|
|
167
|
-
custom_fields[project_issue_field.id] = option.dict()
|
|
168
|
-
break
|
|
169
|
-
else:
|
|
170
|
-
# field.value is not in the allowed options
|
|
171
|
-
logging.error(
|
|
172
|
-
f"[{board.name}] '{field.name}' has an invalid value '{field.value}'. Valid values: {project_issue_field.options}"
|
|
173
|
-
)
|
|
174
|
-
error |= ValidationError.INVALID_ISSUE_FIELD
|
|
175
|
-
continue
|
|
176
|
-
|
|
177
|
-
if board.name in public_projects and "Security Level" not in [
|
|
178
|
-
f.name for f in board.issue_fields or []
|
|
179
|
-
]:
|
|
180
|
-
logging.error(
|
|
181
|
-
f"[{board.name}] is a public project, but the security level is not defined. Please add 'Security Level' field to the issueFields!"
|
|
182
|
-
)
|
|
183
|
-
error |= ValidationError.PUBLIC_PROJECT_NO_SECURITY_LEVEL
|
|
184
|
-
|
|
185
|
-
project_priorities = jira.project_priority_scheme()
|
|
186
|
-
# get the priority names from the project priorities ids
|
|
187
|
-
project_priorities_names = [
|
|
188
|
-
p_name
|
|
189
|
-
for project_p_id in project_priorities
|
|
190
|
-
for p_name, p_id in jira_server_priorities.items()
|
|
191
|
-
if p_id == project_p_id
|
|
192
|
-
]
|
|
193
|
-
for priority in board.severity_priority_mappings.mappings:
|
|
194
|
-
if priority.priority not in jira_server_priorities:
|
|
195
|
-
logging.error(
|
|
196
|
-
f"[{board.name}] {priority.priority} is not a valid Jira priority. Valid priorities: {project_priorities_names}"
|
|
197
|
-
)
|
|
198
|
-
error |= ValidationError.INVALID_PRIORITY
|
|
199
|
-
continue
|
|
200
|
-
if jira_server_priorities[priority.priority] not in project_priorities:
|
|
201
|
-
logging.error(
|
|
202
|
-
f"[{board.name}] {priority.priority} is not a valid priority in project. Valid priorities: {project_priorities_names}"
|
|
203
|
-
)
|
|
204
|
-
error |= ValidationError.INVALID_PRIORITY
|
|
205
302
|
except JIRAError as e:
|
|
206
303
|
if e.status_code == 401:
|
|
207
304
|
# sporadic 401 errors, retrying
|
|
@@ -228,25 +325,41 @@ def validate_boards(
|
|
|
228
325
|
dry_run: bool,
|
|
229
326
|
state: State,
|
|
230
327
|
jira_client_class: type[JiraClient] = JiraClient,
|
|
328
|
+
use_cache: bool = False,
|
|
231
329
|
) -> bool:
|
|
330
|
+
"""Validate all Jira boards.
|
|
331
|
+
|
|
332
|
+
The method iterates over all Jira boards and checks if the configuration is valid. If no errors
|
|
333
|
+
are found, it will skip the next check for the board until the next run time is reached.
|
|
334
|
+
The next run time is calculated based on the board's check interval and some randomness to avoid
|
|
335
|
+
all boards checking at the same time.
|
|
336
|
+
|
|
337
|
+
Additionally, for any Jira board with a permission error, a Prometheus metric will be set to trigger an alert.
|
|
338
|
+
|
|
339
|
+
Returns True if there were any errors. See ValidationError and log messages for details.
|
|
340
|
+
"""
|
|
232
341
|
error = False
|
|
233
342
|
jira_clients: dict[str, JiraClient] = {}
|
|
234
343
|
for board in jira_boards:
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
if
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
344
|
+
if use_cache:
|
|
345
|
+
next_run_time = state.get(board.name, 0)
|
|
346
|
+
if time.time() <= next_run_time:
|
|
347
|
+
if not dry_run:
|
|
348
|
+
# always skip for non-dry-run mode
|
|
349
|
+
continue
|
|
350
|
+
# dry-run mode
|
|
351
|
+
elif len(jira_boards) > 1:
|
|
352
|
+
logging.info(f"[{board.name}] Use cache results. Skipping ...")
|
|
353
|
+
continue
|
|
244
354
|
|
|
245
355
|
logging.debug(f"[{board.name}] checking ...")
|
|
246
356
|
if board.server.server_url not in jira_clients:
|
|
247
357
|
jira_clients[board.server.server_url] = jira_client_class.create(
|
|
248
358
|
project_name=board.name,
|
|
249
359
|
token=secret_reader.read_secret(board.server.token),
|
|
360
|
+
email=secret_reader.read_secret(board.server.email)
|
|
361
|
+
if board.server.email
|
|
362
|
+
else None,
|
|
250
363
|
server_url=board.server.server_url,
|
|
251
364
|
jira_watcher_settings=jira_client_settings,
|
|
252
365
|
)
|
|
@@ -318,7 +431,7 @@ def get_jira_boards(
|
|
|
318
431
|
|
|
319
432
|
|
|
320
433
|
def export_boards(boards: list[JiraBoardV1]) -> list[dict]:
|
|
321
|
-
return [board.
|
|
434
|
+
return [board.model_dump() for board in boards]
|
|
322
435
|
|
|
323
436
|
|
|
324
437
|
@defer
|
|
@@ -326,6 +439,7 @@ def run(
|
|
|
326
439
|
dry_run: bool,
|
|
327
440
|
jira_board_name: list[str] | None = None,
|
|
328
441
|
board_check_interval_sec: int = 3600,
|
|
442
|
+
use_cache: bool = False,
|
|
329
443
|
defer: Callable | None = None,
|
|
330
444
|
) -> None:
|
|
331
445
|
gql_api = gql.get_api()
|
|
@@ -349,6 +463,7 @@ def run(
|
|
|
349
463
|
board_check_interval_sec=board_check_interval_sec,
|
|
350
464
|
dry_run=dry_run,
|
|
351
465
|
state=state,
|
|
466
|
+
use_cache=use_cache,
|
|
352
467
|
)
|
|
353
468
|
|
|
354
469
|
if error:
|
|
@@ -58,7 +58,7 @@ class LdapGroupsIntegration(QontractReconcileIntegration[LdapGroupsIntegrationPa
|
|
|
58
58
|
"""Return the desired state for early exit."""
|
|
59
59
|
if not query_func:
|
|
60
60
|
query_func = gql.get_api().query
|
|
61
|
-
return {"roles": [c.
|
|
61
|
+
return {"roles": [c.model_dump() for c in self.get_roles(query_func)]}
|
|
62
62
|
|
|
63
63
|
@defer
|
|
64
64
|
def run(self, dry_run: bool, defer: Callable | None = None) -> None:
|
reconcile/ocm/types.py
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from pydantic import
|
|
4
|
-
BaseModel,
|
|
5
|
-
Extra,
|
|
6
|
-
Field,
|
|
7
|
-
)
|
|
3
|
+
from pydantic import BaseModel, Field, field_validator
|
|
8
4
|
|
|
9
5
|
|
|
10
6
|
class OCMClusterAutoscale(BaseModel):
|
|
@@ -13,99 +9,82 @@ class OCMClusterAutoscale(BaseModel):
|
|
|
13
9
|
|
|
14
10
|
|
|
15
11
|
class OCMClusterNetwork(BaseModel):
|
|
16
|
-
type: str | None
|
|
12
|
+
type: str | None = None
|
|
17
13
|
vpc: str
|
|
18
14
|
service: str
|
|
19
15
|
pod: str
|
|
20
16
|
|
|
21
17
|
|
|
22
|
-
class OCMClusterSpec(BaseModel):
|
|
23
|
-
autoscale: OCMClusterAutoscale | None
|
|
18
|
+
class OCMClusterSpec(BaseModel, extra="forbid"):
|
|
19
|
+
autoscale: OCMClusterAutoscale | None = None
|
|
24
20
|
channel: str
|
|
25
|
-
disable_user_workload_monitoring: bool | None
|
|
26
|
-
external_id: str | None
|
|
27
|
-
id: str | None
|
|
28
|
-
instance_type: str | None
|
|
29
|
-
multi_az: bool | None
|
|
30
|
-
nodes: int | None
|
|
21
|
+
disable_user_workload_monitoring: bool | None = None
|
|
22
|
+
external_id: str | None = None
|
|
23
|
+
id: str | None = None
|
|
24
|
+
instance_type: str | None = None
|
|
25
|
+
multi_az: bool | None = None
|
|
26
|
+
nodes: int | None = None
|
|
31
27
|
private: bool
|
|
32
28
|
product: str
|
|
33
29
|
provider: str
|
|
34
|
-
provision_shard_id: str | None
|
|
30
|
+
provision_shard_id: str | None = None
|
|
35
31
|
region: str
|
|
36
|
-
initial_version: str | None
|
|
32
|
+
initial_version: str | None = None
|
|
37
33
|
version: str
|
|
38
|
-
hypershift: bool | None
|
|
34
|
+
hypershift: bool | None = None
|
|
35
|
+
fips: bool = False
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
@field_validator("fips", mode="before")
|
|
38
|
+
@classmethod
|
|
39
|
+
def set_fips_default(cls, v: bool | None) -> bool:
|
|
40
|
+
return v or False
|
|
42
41
|
|
|
43
42
|
|
|
44
|
-
class OSDClusterSpec(OCMClusterSpec):
|
|
43
|
+
class OSDClusterSpec(OCMClusterSpec, extra="forbid"):
|
|
45
44
|
load_balancers: int
|
|
46
45
|
storage: int
|
|
47
46
|
|
|
48
|
-
class Config:
|
|
49
|
-
extra = Extra.forbid
|
|
50
47
|
|
|
51
|
-
|
|
52
|
-
class ROSAOcmAwsStsAttrs(BaseModel):
|
|
48
|
+
class ROSAOcmAwsStsAttrs(BaseModel, extra="forbid"):
|
|
53
49
|
installer_role_arn: str
|
|
54
50
|
support_role_arn: str
|
|
55
|
-
controlplane_role_arn: str | None
|
|
51
|
+
controlplane_role_arn: str | None = None
|
|
56
52
|
worker_role_arn: str
|
|
57
53
|
|
|
58
|
-
class Config:
|
|
59
|
-
extra = Extra.forbid
|
|
60
|
-
|
|
61
54
|
|
|
62
|
-
class ROSAOcmAwsAttrs(BaseModel):
|
|
55
|
+
class ROSAOcmAwsAttrs(BaseModel, extra="forbid"):
|
|
63
56
|
creator_role_arn: str
|
|
64
|
-
sts: ROSAOcmAwsStsAttrs | None
|
|
65
|
-
|
|
66
|
-
class Config:
|
|
67
|
-
extra = Extra.forbid
|
|
57
|
+
sts: ROSAOcmAwsStsAttrs | None = None
|
|
68
58
|
|
|
69
59
|
|
|
70
|
-
class ROSAClusterAWSAccount(BaseModel):
|
|
60
|
+
class ROSAClusterAWSAccount(BaseModel, extra="forbid"):
|
|
71
61
|
uid: str
|
|
72
|
-
rosa: ROSAOcmAwsAttrs | None
|
|
73
|
-
billing_account_id: str | None
|
|
62
|
+
rosa: ROSAOcmAwsAttrs | None = None
|
|
63
|
+
billing_account_id: str | None = None
|
|
74
64
|
|
|
75
|
-
class Config:
|
|
76
|
-
extra = Extra.forbid
|
|
77
65
|
|
|
78
|
-
|
|
79
|
-
class ROSAClusterSpec(OCMClusterSpec):
|
|
66
|
+
class ROSAClusterSpec(OCMClusterSpec, extra="forbid"):
|
|
80
67
|
account: ROSAClusterAWSAccount
|
|
81
|
-
subnet_ids: list[str] | None
|
|
82
|
-
availability_zones: list[str] | None
|
|
83
|
-
oidc_endpoint_url: str | None
|
|
84
|
-
|
|
85
|
-
class Config:
|
|
86
|
-
extra = Extra.forbid
|
|
68
|
+
subnet_ids: list[str] | None = None
|
|
69
|
+
availability_zones: list[str] | None = None
|
|
70
|
+
oidc_endpoint_url: str | None = None
|
|
87
71
|
|
|
88
72
|
|
|
89
73
|
class ClusterMachinePool(BaseModel):
|
|
90
74
|
id: str
|
|
91
75
|
instance_type: str
|
|
92
|
-
replicas: int | None
|
|
93
|
-
autoscale: OCMClusterAutoscale | None
|
|
76
|
+
replicas: int | None = None
|
|
77
|
+
autoscale: OCMClusterAutoscale | None = None
|
|
94
78
|
|
|
95
79
|
|
|
96
|
-
class OCMSpec(BaseModel):
|
|
97
|
-
path: str | None
|
|
80
|
+
class OCMSpec(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
81
|
+
path: str | None = None
|
|
98
82
|
spec: OSDClusterSpec | ROSAClusterSpec | OCMClusterSpec
|
|
99
83
|
machine_pools: list[ClusterMachinePool] = Field(
|
|
100
84
|
default_factory=list, alias="machinePools"
|
|
101
85
|
)
|
|
102
86
|
network: OCMClusterNetwork
|
|
103
|
-
domain: str | None
|
|
87
|
+
domain: str | None = None
|
|
104
88
|
server_url: str = Field("", alias="serverUrl")
|
|
105
89
|
console_url: str = Field("", alias="consoleUrl")
|
|
106
90
|
elb_fqdn: str = Field("", alias="elbFQDN")
|
|
107
|
-
|
|
108
|
-
class Config:
|
|
109
|
-
smart_union = True
|
|
110
|
-
# This is need to populate by either console_url or consoleUrl, for instance
|
|
111
|
-
allow_population_by_field_name = True
|
|
@@ -103,7 +103,7 @@ def fetch_desired_state(clusters: Iterable[Mapping[str, Any]]) -> list[dict[str,
|
|
|
103
103
|
namespaces = get_namespaces()
|
|
104
104
|
for namespace_info in namespaces:
|
|
105
105
|
specs = get_external_resource_specs(
|
|
106
|
-
namespace_info.
|
|
106
|
+
namespace_info.model_dump(by_alias=True), provision_provider=PROVIDER_AWS
|
|
107
107
|
)
|
|
108
108
|
for spec in specs:
|
|
109
109
|
if spec.provider != "aws-iam-service-account":
|
reconcile/ocm_clusters.py
CHANGED
|
@@ -248,13 +248,13 @@ def get_cluster_ocm_update_spec(
|
|
|
248
248
|
if not desired_spec.network.type:
|
|
249
249
|
desired_spec.network.type = "OVNKubernetes"
|
|
250
250
|
|
|
251
|
-
cspec = current_spec.spec.
|
|
252
|
-
cspec[ocmmod.SPEC_ATTR_NETWORK] = current_spec.network.
|
|
251
|
+
cspec = current_spec.spec.model_dump()
|
|
252
|
+
cspec[ocmmod.SPEC_ATTR_NETWORK] = current_spec.network.model_dump(
|
|
253
253
|
exclude={IGNORE_NETWORK_TYPE_ATTR}
|
|
254
254
|
)
|
|
255
255
|
|
|
256
|
-
dspec = desired_spec.spec.
|
|
257
|
-
dspec[ocmmod.SPEC_ATTR_NETWORK] = desired_spec.network.
|
|
256
|
+
dspec = desired_spec.spec.model_dump()
|
|
257
|
+
dspec[ocmmod.SPEC_ATTR_NETWORK] = desired_spec.network.model_dump(
|
|
258
258
|
exclude={IGNORE_NETWORK_TYPE_ATTR}
|
|
259
259
|
)
|
|
260
260
|
|
|
@@ -4,7 +4,7 @@ import logging
|
|
|
4
4
|
from typing import TYPE_CHECKING, Any
|
|
5
5
|
|
|
6
6
|
from deepdiff import DeepHash
|
|
7
|
-
from pydantic import
|
|
7
|
+
from pydantic import field_validator
|
|
8
8
|
|
|
9
9
|
from reconcile.aus.aus_label_source import (
|
|
10
10
|
init_aus_cluster_label_source,
|
|
@@ -62,7 +62,8 @@ class OcmLabelsIntegrationParams(PydanticRunParams):
|
|
|
62
62
|
managed_label_prefixes: list[str] = []
|
|
63
63
|
ignored_label_prefixes: list[str] = []
|
|
64
64
|
|
|
65
|
-
@
|
|
65
|
+
@field_validator("managed_label_prefixes", "ignored_label_prefixes")
|
|
66
|
+
@classmethod
|
|
66
67
|
def must_end_with_dot(cls, v: list[str]) -> list[str]:
|
|
67
68
|
return [prefix + "." if not prefix.endswith(".") else prefix for prefix in v]
|
|
68
69
|
|