qontract-reconcile 0.10.2.dev361__py3-none-any.whl → 0.10.2.dev474__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/METADATA +14 -13
- {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/RECORD +371 -364
- {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/WHEEL +1 -1
- reconcile/acs_rbac.py +2 -2
- reconcile/aus/advanced_upgrade_service.py +18 -12
- reconcile/aus/aus_sts_gate_handler.py +59 -0
- reconcile/aus/base.py +137 -34
- 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_gate_approver.py +1 -16
- reconcile/aus/version_gates/sts_version_gate_handler.py +5 -72
- reconcile/automated_actions/config/integration.py +16 -4
- reconcile/aws_account_manager/integration.py +21 -9
- reconcile/aws_account_manager/reconciler.py +3 -3
- reconcile/aws_account_manager/utils.py +1 -1
- 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 +1 -1
- reconcile/aws_iam_keys.py +1 -0
- reconcile/aws_saml_idp/integration.py +12 -4
- reconcile/aws_saml_roles/integration.py +30 -23
- reconcile/aws_version_sync/integration.py +6 -12
- reconcile/change_owners/README.md +1 -1
- reconcile/change_owners/bundle.py +3 -3
- reconcile/change_owners/change_log_tracking.py +3 -2
- reconcile/change_owners/change_owners.py +108 -42
- reconcile/change_owners/decision.py +1 -1
- reconcile/change_owners/diff.py +0 -2
- reconcile/checkpoint.py +11 -3
- reconcile/cli.py +94 -11
- reconcile/dashdotdb_dora.py +5 -12
- reconcile/dashdotdb_slo.py +1 -1
- reconcile/database_access_manager.py +123 -117
- 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 +8 -8
- reconcile/external_resources/factories.py +4 -6
- reconcile/external_resources/integration.py +1 -1
- reconcile/external_resources/manager.py +8 -6
- reconcile/external_resources/meta.py +0 -1
- reconcile/external_resources/metrics.py +1 -1
- reconcile/external_resources/model.py +19 -15
- reconcile/external_resources/reconciler.py +7 -4
- reconcile/external_resources/secrets_sync.py +6 -10
- reconcile/external_resources/state.py +26 -16
- reconcile/fleet_labeler/integration.py +1 -1
- reconcile/gabi_authorized_users.py +5 -2
- 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_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 +5 -5
- reconcile/gql_definitions/acs/acs_policies.py +5 -5
- reconcile/gql_definitions/acs/acs_rbac.py +5 -5
- reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +5 -5
- reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +5 -5
- reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +5 -5
- 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 +46 -7
- reconcile/gql_definitions/aws_account_manager/aws_accounts.py +14 -5
- reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +15 -5
- reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +27 -66
- reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +15 -5
- reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +15 -5
- reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
- reconcile/gql_definitions/aws_version_sync/clusters.py +5 -5
- 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 +5 -5
- reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +5 -5
- reconcile/gql_definitions/common/alerting_services_settings.py +5 -5
- 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 +5 -5
- reconcile/gql_definitions/common/app_interface_state_settings.py +5 -5
- 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 +18 -5
- reconcile/gql_definitions/common/aws_vpcs.py +5 -5
- reconcile/gql_definitions/common/clusters.py +7 -5
- reconcile/gql_definitions/common/clusters_minimal.py +5 -5
- reconcile/gql_definitions/common/clusters_with_dms.py +5 -5
- reconcile/gql_definitions/common/clusters_with_peering.py +5 -5
- reconcile/gql_definitions/common/github_orgs.py +5 -5
- reconcile/gql_definitions/common/jira_settings.py +5 -5
- reconcile/gql_definitions/common/jiralert_settings.py +5 -5
- reconcile/gql_definitions/common/ldap_settings.py +5 -5
- reconcile/gql_definitions/common/namespaces.py +5 -5
- reconcile/gql_definitions/common/namespaces_minimal.py +7 -5
- reconcile/gql_definitions/common/ocm_env_telemeter.py +5 -5
- reconcile/gql_definitions/common/ocm_environments.py +5 -5
- reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
- reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -5
- reconcile/gql_definitions/common/pipeline_providers.py +5 -5
- 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 +5 -5
- reconcile/gql_definitions/common/saas_target_namespaces.py +5 -5
- 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 +5 -5
- reconcile/gql_definitions/common/state_aws_account.py +5 -5
- 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 +5 -5
- reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +5 -5
- reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +5 -5
- 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 +5 -5
- reconcile/gql_definitions/email_sender/users.py +5 -5
- reconcile/gql_definitions/endpoints_discovery/apps.py +5 -5
- reconcile/gql_definitions/external_resources/aws_accounts.py +5 -5
- reconcile/gql_definitions/external_resources/external_resources_modules.py +5 -5
- reconcile/gql_definitions/external_resources/external_resources_namespaces.py +38 -7
- reconcile/gql_definitions/external_resources/external_resources_settings.py +5 -5
- reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
- reconcile/gql_definitions/fleet_labeler/fleet_labels.py +5 -5
- 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_organization.py +33 -0
- 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 +5 -5
- reconcile/gql_definitions/gcp/gcp_projects.py +5 -5
- reconcile/gql_definitions/gitlab_members/gitlab_instances.py +5 -5
- reconcile/gql_definitions/gitlab_members/permissions.py +5 -5
- reconcile/gql_definitions/glitchtip/glitchtip_instance.py +5 -5
- reconcile/gql_definitions/glitchtip/glitchtip_project.py +5 -5
- reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +5 -5
- reconcile/gql_definitions/integrations/integrations.py +5 -5
- reconcile/gql_definitions/introspection.json +775 -136
- reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +5 -5
- reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +5 -5
- reconcile/gql_definitions/jira/jira_servers.py +5 -5
- reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +9 -5
- reconcile/gql_definitions/jumphosts/jumphosts.py +5 -5
- reconcile/gql_definitions/ldap_groups/roles.py +5 -5
- reconcile/gql_definitions/ldap_groups/settings.py +5 -5
- reconcile/gql_definitions/maintenance/maintenances.py +5 -5
- reconcile/gql_definitions/membershipsources/roles.py +5 -5
- reconcile/gql_definitions/ocm_labels/clusters.py +5 -5
- reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
- reconcile/gql_definitions/openshift_cluster_bots/clusters.py +5 -5
- reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
- reconcile/gql_definitions/openshift_groups/managed_roles.py +5 -5
- reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +5 -5
- reconcile/gql_definitions/quay_membership/quay_membership.py +5 -5
- reconcile/gql_definitions/rhcs/certs.py +25 -79
- reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +43 -0
- reconcile/gql_definitions/rhidp/organizations.py +5 -5
- reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
- reconcile/gql_definitions/service_dependencies/service_dependencies.py +5 -5
- reconcile/gql_definitions/sharding/aws_accounts.py +5 -5
- reconcile/gql_definitions/sharding/ocm_organization.py +5 -5
- reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
- reconcile/gql_definitions/skupper_network/skupper_networks.py +5 -5
- reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
- reconcile/gql_definitions/slack_usergroups/permissions.py +5 -5
- 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 +5 -5
- reconcile/gql_definitions/statuspage/statuspages.py +5 -5
- 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 +5 -5
- reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +5 -5
- reconcile/gql_definitions/terraform_init/aws_accounts.py +19 -5
- reconcile/gql_definitions/terraform_repo/terraform_repo.py +5 -5
- reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
- reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +37 -7
- reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +15 -5
- reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +5 -5
- reconcile/gql_definitions/vault_instances/vault_instances.py +5 -5
- reconcile/gql_definitions/vault_policies/vault_policies.py +5 -5
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +8 -5
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +6 -5
- reconcile/integrations_manager.py +3 -3
- reconcile/jenkins_worker_fleets.py +10 -8
- reconcile/jira_permissions_validator.py +237 -122
- reconcile/ldap_groups/integration.py +1 -1
- reconcile/ocm/types.py +35 -57
- 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 +113 -4
- reconcile/openshift_cluster_bots.py +1 -1
- reconcile/openshift_namespace_labels.py +1 -1
- reconcile/openshift_namespaces.py +96 -101
- reconcile/openshift_resources_base.py +6 -2
- reconcile/openshift_rhcs_certs.py +74 -37
- reconcile/openshift_rolebindings.py +7 -11
- reconcile/openshift_saas_deploy.py +4 -5
- reconcile/openshift_saas_deploy_change_tester.py +9 -7
- reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
- reconcile/openshift_serviceaccount_tokens.py +2 -2
- reconcile/openshift_upgrade_watcher.py +4 -4
- reconcile/oum/labelset.py +5 -3
- reconcile/oum/models.py +1 -4
- reconcile/prometheus_rules_tester/integration.py +3 -3
- reconcile/quay_base.py +25 -6
- reconcile/quay_membership.py +55 -29
- reconcile/quay_mirror.py +1 -1
- reconcile/quay_mirror_org.py +6 -4
- reconcile/quay_permissions.py +81 -75
- reconcile/quay_repos.py +35 -37
- reconcile/queries.py +132 -1
- reconcile/rhidp/common.py +3 -5
- reconcile/rhidp/sso_client/base.py +16 -5
- reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
- reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
- reconcile/skupper_network/integration.py +2 -2
- reconcile/slack_usergroups.py +35 -14
- reconcile/sql_query.py +1 -0
- 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 +1 -1
- reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +1 -1
- reconcile/templating/lib/rendering.py +3 -3
- reconcile/templating/renderer.py +2 -2
- reconcile/templating/validator.py +4 -4
- reconcile/terraform_aws_route53.py +7 -1
- reconcile/terraform_cloudflare_dns.py +3 -3
- reconcile/terraform_cloudflare_resources.py +5 -5
- reconcile/terraform_cloudflare_users.py +3 -2
- reconcile/terraform_init/integration.py +187 -23
- reconcile/terraform_repo.py +16 -12
- reconcile/terraform_resources.py +6 -6
- reconcile/terraform_tgw_attachments.py +27 -19
- reconcile/terraform_users.py +7 -0
- reconcile/terraform_vpc_peerings.py +14 -3
- reconcile/terraform_vpc_resources/integration.py +20 -8
- reconcile/terraform_vpc_resources/merge_request.py +12 -2
- reconcile/terraform_vpc_resources/merge_request_manager.py +43 -19
- 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 +20 -15
- 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/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/environ.py +5 -0
- reconcile/utils/expiration.py +7 -3
- reconcile/utils/external_resource_spec.py +2 -0
- reconcile/utils/filtering.py +1 -1
- reconcile/utils/gitlab_api.py +19 -5
- reconcile/utils/glitchtip/client.py +6 -2
- reconcile/utils/glitchtip/models.py +25 -28
- reconcile/utils/gql.py +4 -7
- reconcile/utils/helpers.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/jinja2/utils.py +6 -101
- reconcile/utils/jira_client.py +82 -63
- reconcile/utils/jjb_client.py +26 -13
- reconcile/utils/jobcontroller/controller.py +2 -2
- reconcile/utils/jobcontroller/models.py +17 -1
- reconcile/utils/json.py +43 -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/app_interface_reporter.py +2 -2
- reconcile/utils/mr/notificator.py +3 -3
- reconcile/utils/mr/update_access_report_base.py +3 -4
- reconcile/utils/mr/user_maintenance.py +3 -2
- reconcile/utils/oc.py +252 -201
- reconcile/utils/oc_filters.py +3 -3
- reconcile/utils/ocm/addons.py +0 -1
- reconcile/utils/ocm/base.py +17 -20
- 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/products.py +8 -8
- 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/openshift_resource.py +8 -3
- reconcile/utils/pagerduty_api.py +10 -7
- reconcile/utils/promotion_state.py +6 -11
- reconcile/utils/quay_api.py +74 -87
- reconcile/utils/raw_github_api.py +1 -1
- reconcile/utils/rhcsv2_certs.py +86 -23
- reconcile/utils/rosa/session.py +16 -0
- reconcile/utils/runtime/integration.py +2 -3
- reconcile/utils/runtime/runner.py +2 -2
- reconcile/utils/saasherder/interfaces.py +13 -20
- reconcile/utils/saasherder/models.py +23 -20
- reconcile/utils/saasherder/saasherder.py +50 -27
- reconcile/utils/slack_api.py +2 -2
- reconcile/utils/sloth.py +171 -2
- reconcile/utils/structs.py +1 -1
- reconcile/utils/terraform_client.py +5 -4
- reconcile/utils/terrascript_aws_client.py +274 -124
- reconcile/utils/unleash/server.py +2 -8
- reconcile/utils/vault.py +5 -12
- reconcile/utils/vcs.py +8 -8
- reconcile/vault_replication.py +107 -42
- reconcile/vpc_peerings_validator.py +13 -0
- 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/qontract_cli.py +28 -17
- tools/template_validation.py +3 -1
- {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/entry_points.txt +0 -0
|
@@ -7,10 +7,7 @@ from enum import Enum
|
|
|
7
7
|
from typing import Any, NotRequired, TypedDict
|
|
8
8
|
|
|
9
9
|
from github import Github
|
|
10
|
-
from pydantic import
|
|
11
|
-
BaseModel,
|
|
12
|
-
Field,
|
|
13
|
-
)
|
|
10
|
+
from pydantic import BaseModel, Field, model_validator
|
|
14
11
|
|
|
15
12
|
from reconcile.gql_definitions.fragments.saas_slo_document import (
|
|
16
13
|
SLODocument,
|
|
@@ -207,7 +204,12 @@ TriggerSpecUnion = (
|
|
|
207
204
|
)
|
|
208
205
|
|
|
209
206
|
|
|
210
|
-
class Namespace(
|
|
207
|
+
class Namespace(
|
|
208
|
+
BaseModel,
|
|
209
|
+
validate_by_name=True,
|
|
210
|
+
validate_by_alias=True,
|
|
211
|
+
arbitrary_types_allowed=True,
|
|
212
|
+
):
|
|
211
213
|
name: str
|
|
212
214
|
environment: SaasEnvironment
|
|
213
215
|
app: SaasApp
|
|
@@ -217,29 +219,19 @@ class Namespace(BaseModel):
|
|
|
217
219
|
..., alias="managedResourceNames"
|
|
218
220
|
)
|
|
219
221
|
|
|
220
|
-
class Config:
|
|
221
|
-
arbitrary_types_allowed = True
|
|
222
|
-
allow_population_by_field_name = True
|
|
223
|
-
|
|
224
222
|
|
|
225
|
-
class PromotionChannelData(BaseModel):
|
|
223
|
+
class PromotionChannelData(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
226
224
|
q_type: str = Field(..., alias="type")
|
|
227
225
|
|
|
228
|
-
class Config:
|
|
229
|
-
allow_population_by_field_name = True
|
|
230
226
|
|
|
231
|
-
|
|
232
|
-
class ParentSaasPromotion(BaseModel):
|
|
227
|
+
class ParentSaasPromotion(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
233
228
|
q_type: str = Field(..., alias="type")
|
|
234
|
-
parent_saas: str | None
|
|
235
|
-
target_config_hash: str | None
|
|
236
|
-
|
|
237
|
-
class Config:
|
|
238
|
-
allow_population_by_field_name = True
|
|
229
|
+
parent_saas: str | None = None
|
|
230
|
+
target_config_hash: str | None = None
|
|
239
231
|
|
|
240
232
|
|
|
241
233
|
class PromotionData(BaseModel):
|
|
242
|
-
channel: str | None
|
|
234
|
+
channel: str | None = None
|
|
243
235
|
data: list[ParentSaasPromotion | PromotionChannelData] | None = None
|
|
244
236
|
|
|
245
237
|
|
|
@@ -264,6 +256,17 @@ class Promotion(BaseModel):
|
|
|
264
256
|
saas_file_paths: list[str] | None = None
|
|
265
257
|
target_paths: list[str] | None = None
|
|
266
258
|
|
|
259
|
+
@model_validator(mode="before")
|
|
260
|
+
@classmethod
|
|
261
|
+
def handle_gql_classes(cls, data: Any) -> Any:
|
|
262
|
+
if data.get("promotion_data"):
|
|
263
|
+
data["promotion_data"] = [
|
|
264
|
+
# convert a GQL class to a dict
|
|
265
|
+
item.model_dump() if hasattr(item, "model_dump") else item
|
|
266
|
+
for item in data["promotion_data"]
|
|
267
|
+
]
|
|
268
|
+
return data
|
|
269
|
+
|
|
267
270
|
|
|
268
271
|
@dataclass
|
|
269
272
|
class ImageAuth:
|
|
@@ -16,7 +16,7 @@ from collections.abc import (
|
|
|
16
16
|
Sequence,
|
|
17
17
|
)
|
|
18
18
|
from contextlib import suppress
|
|
19
|
-
from datetime import
|
|
19
|
+
from datetime import datetime, timedelta
|
|
20
20
|
from types import TracebackType
|
|
21
21
|
from typing import Any
|
|
22
22
|
|
|
@@ -37,6 +37,7 @@ from sretoolbox.utils import (
|
|
|
37
37
|
from reconcile.github_org import get_default_config
|
|
38
38
|
from reconcile.status import RunningState
|
|
39
39
|
from reconcile.utils import helm
|
|
40
|
+
from reconcile.utils.datetime_util import utc_now
|
|
40
41
|
from reconcile.utils.github_api import GithubRepositoryApi
|
|
41
42
|
from reconcile.utils.gitlab_api import GitLabApi
|
|
42
43
|
from reconcile.utils.jenkins_api import JenkinsApi, JobBuildState
|
|
@@ -91,8 +92,7 @@ from reconcile.utils.state import State
|
|
|
91
92
|
from reconcile.utils.vcs import VCS
|
|
92
93
|
|
|
93
94
|
TARGET_CONFIG_HASH = "target_config_hash"
|
|
94
|
-
|
|
95
|
-
|
|
95
|
+
TEMPLATE_API_VERSION = "template.openshift.io/v1"
|
|
96
96
|
UNIQUE_SAAS_FILE_ENV_COMBO_LEN = 56
|
|
97
97
|
REQUEST_TIMEOUT = 60
|
|
98
98
|
|
|
@@ -764,7 +764,8 @@ class SaasHerder:
|
|
|
764
764
|
case "gitlab":
|
|
765
765
|
if not self.gitlab:
|
|
766
766
|
raise Exception("gitlab is not initialized")
|
|
767
|
-
project
|
|
767
|
+
if not (project := self.gitlab.get_project(url)):
|
|
768
|
+
raise Exception(f"Could not find gitlab project for {url}")
|
|
768
769
|
content = self.gitlab.get_raw_file(
|
|
769
770
|
project=project,
|
|
770
771
|
path=path,
|
|
@@ -800,7 +801,8 @@ class SaasHerder:
|
|
|
800
801
|
case "gitlab":
|
|
801
802
|
if not self.gitlab:
|
|
802
803
|
raise Exception("gitlab is not initialized")
|
|
803
|
-
project
|
|
804
|
+
if not (project := self.gitlab.get_project(url)):
|
|
805
|
+
raise Exception(f"Could not find gitlab project for {url}")
|
|
804
806
|
dir_contents = self.gitlab.get_directory_contents(
|
|
805
807
|
project,
|
|
806
808
|
ref=commit_sha,
|
|
@@ -825,7 +827,8 @@ class SaasHerder:
|
|
|
825
827
|
case "gitlab":
|
|
826
828
|
if not self.gitlab:
|
|
827
829
|
raise Exception("gitlab is not initialized")
|
|
828
|
-
project
|
|
830
|
+
if not (project := self.gitlab.get_project(url)):
|
|
831
|
+
raise Exception(f"Could not find gitlab project for {url}")
|
|
829
832
|
commits = project.commits.list(ref_name=ref, per_page=1, page=1)
|
|
830
833
|
return commits[0].id
|
|
831
834
|
case _:
|
|
@@ -870,10 +873,23 @@ class SaasHerder:
|
|
|
870
873
|
"""
|
|
871
874
|
if parameter_name in consolidated_parameters:
|
|
872
875
|
return False
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
876
|
+
return any(
|
|
877
|
+
template_parameter["name"] == parameter_name
|
|
878
|
+
for template_parameter in template.get("parameters") or []
|
|
879
|
+
)
|
|
880
|
+
|
|
881
|
+
@staticmethod
|
|
882
|
+
def _pre_process_template(template: dict[str, Any]) -> dict[str, Any]:
|
|
883
|
+
"""
|
|
884
|
+
The only supported apiVersion for OpenShift Template is "template.openshift.io/v1".
|
|
885
|
+
There are examples of templates using "v1", it can't pass validation on 4.19+ oc versions.
|
|
886
|
+
|
|
887
|
+
Args:
|
|
888
|
+
template (dict): The OpenShift template dictionary.
|
|
889
|
+
Returns:
|
|
890
|
+
dict: The OpenShift template dictionary with the correct apiVersion.
|
|
891
|
+
"""
|
|
892
|
+
return template | {"apiVersion": TEMPLATE_API_VERSION}
|
|
877
893
|
|
|
878
894
|
def _process_template(
|
|
879
895
|
self, spec: TargetSpec
|
|
@@ -963,7 +979,8 @@ class SaasHerder:
|
|
|
963
979
|
oc = OCLocal("cluster", None, None, local=True)
|
|
964
980
|
try:
|
|
965
981
|
resources: Iterable[Mapping[str, Any]] = oc.process(
|
|
966
|
-
template,
|
|
982
|
+
template=self._pre_process_template(template),
|
|
983
|
+
parameters=consolidated_parameters,
|
|
967
984
|
)
|
|
968
985
|
except StatusCodeError as e:
|
|
969
986
|
logging.error(f"{error_prefix} error processing template: {e!s}")
|
|
@@ -1178,13 +1195,13 @@ class SaasHerder:
|
|
|
1178
1195
|
images_list = threaded.run(
|
|
1179
1196
|
self._collect_images, resources, self.available_thread_pool_size
|
|
1180
1197
|
)
|
|
1181
|
-
|
|
1182
|
-
self.images.update(
|
|
1183
|
-
if not
|
|
1198
|
+
images_set = set(itertools.chain.from_iterable(images_list))
|
|
1199
|
+
self.images.update(images_set)
|
|
1200
|
+
if not images_set:
|
|
1184
1201
|
return False # no errors
|
|
1185
1202
|
images = threaded.run(
|
|
1186
1203
|
self._get_image,
|
|
1187
|
-
|
|
1204
|
+
images_set,
|
|
1188
1205
|
self.available_thread_pool_size,
|
|
1189
1206
|
image_patterns=spec.image_patterns,
|
|
1190
1207
|
image_auth=spec.image_auth,
|
|
@@ -1249,7 +1266,9 @@ class SaasHerder:
|
|
|
1249
1266
|
self.saas_files,
|
|
1250
1267
|
self.thread_pool_size,
|
|
1251
1268
|
)
|
|
1252
|
-
desired_state_specs = list(
|
|
1269
|
+
desired_state_specs: list[TargetSpec] = list(
|
|
1270
|
+
itertools.chain.from_iterable(results)
|
|
1271
|
+
)
|
|
1253
1272
|
promotions = threaded.run(
|
|
1254
1273
|
self.populate_desired_state_saas_file,
|
|
1255
1274
|
desired_state_specs,
|
|
@@ -1897,21 +1916,23 @@ class SaasHerder:
|
|
|
1897
1916
|
name=target.name,
|
|
1898
1917
|
ref=target.ref,
|
|
1899
1918
|
promotion=(
|
|
1900
|
-
target.promotion.
|
|
1919
|
+
target.promotion.model_dump(by_alias=True) if target.promotion else None
|
|
1901
1920
|
),
|
|
1902
1921
|
secretParameters=(
|
|
1903
|
-
[p.
|
|
1922
|
+
[p.model_dump(by_alias=True) for p in target.secret_parameters]
|
|
1904
1923
|
if target.secret_parameters
|
|
1905
1924
|
else None
|
|
1906
1925
|
),
|
|
1907
1926
|
slos=(
|
|
1908
|
-
[slo.
|
|
1927
|
+
[slo.model_dump(by_alias=True) for slo in target.slos]
|
|
1909
1928
|
if target.slos
|
|
1910
1929
|
else None
|
|
1911
1930
|
),
|
|
1912
|
-
upstream=(
|
|
1931
|
+
upstream=(
|
|
1932
|
+
target.upstream.model_dump(by_alias=True) if target.upstream else None
|
|
1933
|
+
),
|
|
1913
1934
|
images=(
|
|
1914
|
-
[i.
|
|
1935
|
+
[i.model_dump(by_alias=True) for i in target.images]
|
|
1915
1936
|
if target.images
|
|
1916
1937
|
else None
|
|
1917
1938
|
),
|
|
@@ -1947,16 +1968,16 @@ class SaasHerder:
|
|
|
1947
1968
|
)
|
|
1948
1969
|
if saas_file.managed_resource_names:
|
|
1949
1970
|
state_content["saas_file_managed_resource_names"] = [
|
|
1950
|
-
m.
|
|
1971
|
+
m.model_dump() for m in saas_file.managed_resource_names
|
|
1951
1972
|
]
|
|
1952
1973
|
# include secret parameters from resource template and saas file
|
|
1953
1974
|
if resource_template.secret_parameters:
|
|
1954
1975
|
state_content["rt_secretparameters"] = [
|
|
1955
|
-
p.
|
|
1976
|
+
p.model_dump() for p in resource_template.secret_parameters
|
|
1956
1977
|
]
|
|
1957
1978
|
if saas_file.secret_parameters:
|
|
1958
1979
|
state_content["saas_file_secretparameters"] = [
|
|
1959
|
-
p.
|
|
1980
|
+
p.model_dump() for p in saas_file.secret_parameters
|
|
1960
1981
|
]
|
|
1961
1982
|
return state_content
|
|
1962
1983
|
|
|
@@ -2025,7 +2046,7 @@ class SaasHerder:
|
|
|
2025
2046
|
if promotion.commit_sha in self.hotfix_versions.get(promotion.url, set()):
|
|
2026
2047
|
return True
|
|
2027
2048
|
|
|
2028
|
-
now =
|
|
2049
|
+
now = utc_now()
|
|
2029
2050
|
passed_soak_days = timedelta(days=0)
|
|
2030
2051
|
|
|
2031
2052
|
for channel in promotion.subscribe:
|
|
@@ -2127,7 +2148,7 @@ class SaasHerder:
|
|
|
2127
2148
|
if not (self.state and self._promotion_state):
|
|
2128
2149
|
raise Exception("state is not initialized")
|
|
2129
2150
|
|
|
2130
|
-
now =
|
|
2151
|
+
now = utc_now()
|
|
2131
2152
|
for promotion in self.promotions:
|
|
2132
2153
|
if promotion is None:
|
|
2133
2154
|
continue
|
|
@@ -2239,7 +2260,9 @@ class SaasHerder:
|
|
|
2239
2260
|
for rt in saas_file.resource_templates:
|
|
2240
2261
|
for target in rt.targets:
|
|
2241
2262
|
template_vars = {
|
|
2242
|
-
"resource": {
|
|
2263
|
+
"resource": {
|
|
2264
|
+
"namespace": target.namespace.model_dump(by_alias=True)
|
|
2265
|
+
}
|
|
2243
2266
|
}
|
|
2244
2267
|
if target.parameters:
|
|
2245
2268
|
for param in target.parameters:
|
reconcile/utils/slack_api.py
CHANGED
|
@@ -71,14 +71,14 @@ class HasClientGlobalConfig(Protocol):
|
|
|
71
71
|
max_retries: int | None
|
|
72
72
|
timeout: int | None
|
|
73
73
|
|
|
74
|
-
def
|
|
74
|
+
def model_dump(self) -> dict[str, int | None]: ...
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
class HasClientMethodConfig(Protocol):
|
|
78
78
|
name: str
|
|
79
79
|
args: Any
|
|
80
80
|
|
|
81
|
-
def
|
|
81
|
+
def model_dump(self) -> dict[str, str]: ...
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
class HasClientConfig(Protocol):
|
reconcile/utils/sloth.py
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import tempfile
|
|
1
3
|
from io import StringIO
|
|
2
|
-
from typing import NotRequired, TypedDict
|
|
4
|
+
from typing import Any, NotRequired, TypedDict
|
|
5
|
+
|
|
6
|
+
import yaml
|
|
3
7
|
|
|
4
8
|
from reconcile.utils.ruamel import create_ruamel_instance
|
|
5
9
|
|
|
@@ -21,6 +25,44 @@ class PrometheusRuleSpec(TypedDict):
|
|
|
21
25
|
groups: list[PrometheusRuleGroup]
|
|
22
26
|
|
|
23
27
|
|
|
28
|
+
class SLOParametersDict(TypedDict):
|
|
29
|
+
window: str
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class SLO(TypedDict):
|
|
33
|
+
name: str
|
|
34
|
+
SLIType: str
|
|
35
|
+
SLISpecification: str
|
|
36
|
+
SLOTarget: float
|
|
37
|
+
SLOTargetUnit: str
|
|
38
|
+
SLOParameters: SLOParametersDict
|
|
39
|
+
SLODetails: str
|
|
40
|
+
dashboard: str
|
|
41
|
+
expr: str
|
|
42
|
+
SLIErrorQuery: NotRequired[str]
|
|
43
|
+
SLITotalQuery: NotRequired[str]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class App(TypedDict):
|
|
47
|
+
name: str
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class SLODocument(TypedDict):
|
|
51
|
+
name: str
|
|
52
|
+
app: App
|
|
53
|
+
slos: NotRequired[list[SLO]]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class SlothGenerateError(Exception):
|
|
57
|
+
def __init__(self, msg: Any):
|
|
58
|
+
super().__init__("sloth generate failed: " + str(msg))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class SlothInputError(Exception):
|
|
62
|
+
def __init__(self, msg: Any):
|
|
63
|
+
super().__init__("sloth input validation failed: " + str(msg))
|
|
64
|
+
|
|
65
|
+
|
|
24
66
|
def process_sloth_output(output_file_path: str) -> str:
|
|
25
67
|
ruamel_instance = create_ruamel_instance()
|
|
26
68
|
with open(output_file_path, encoding="utf-8") as f:
|
|
@@ -49,7 +91,134 @@ def process_sloth_output(output_file_path: str) -> str:
|
|
|
49
91
|
rule["annotations"] = annotations
|
|
50
92
|
else:
|
|
51
93
|
rule.pop("annotations", None)
|
|
52
|
-
|
|
53
94
|
with StringIO() as s:
|
|
54
95
|
ruamel_instance.dump(data, s)
|
|
55
96
|
return s.getvalue()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def run_sloth(spec: dict[str, Any]) -> str:
|
|
100
|
+
with (
|
|
101
|
+
tempfile.NamedTemporaryFile(
|
|
102
|
+
encoding="utf-8", mode="w", suffix=".yml"
|
|
103
|
+
) as input_file,
|
|
104
|
+
tempfile.NamedTemporaryFile(
|
|
105
|
+
encoding="utf-8", mode="w", suffix=".yml"
|
|
106
|
+
) as output_file,
|
|
107
|
+
):
|
|
108
|
+
yaml.dump(spec, input_file, allow_unicode=True)
|
|
109
|
+
cmd = ["sloth", "generate", "-i", input_file.name, "-o", output_file.name]
|
|
110
|
+
try:
|
|
111
|
+
subprocess.run(cmd, capture_output=True, check=True, text=True)
|
|
112
|
+
except subprocess.CalledProcessError as e:
|
|
113
|
+
error_msg = f"{e}"
|
|
114
|
+
if e.stdout:
|
|
115
|
+
error_msg += f"\nstdout: {e.stdout}"
|
|
116
|
+
if e.stderr:
|
|
117
|
+
error_msg += f"\nstderr: {e.stderr}"
|
|
118
|
+
raise SlothGenerateError(error_msg) from e
|
|
119
|
+
return process_sloth_output(output_file.name)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def get_slo_target(slo: SLO) -> float:
|
|
123
|
+
"""
|
|
124
|
+
Ensure SLO target unit aligns with format expected by sloth for 'Objective' attribute
|
|
125
|
+
https://pkg.go.dev/github.com/slok/sloth/pkg/prometheus/api/v1#section-readme
|
|
126
|
+
"""
|
|
127
|
+
val = float(slo["SLOTarget"])
|
|
128
|
+
return val * (100.0 if slo.get("SLOTargetUnit") == "percent_0_1" else 1.0)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def generate_sloth_rules(
|
|
132
|
+
slo_document: SLODocument,
|
|
133
|
+
version: str = "prometheus/v1",
|
|
134
|
+
) -> str:
|
|
135
|
+
"""Generate Prometheus rules for an slo_document_v1 using sloth
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
slo_document query:
|
|
139
|
+
{
|
|
140
|
+
slo_docs: slo_document_v1(filter: {name: "foo"}) {
|
|
141
|
+
name
|
|
142
|
+
app {
|
|
143
|
+
name
|
|
144
|
+
}
|
|
145
|
+
slos {
|
|
146
|
+
name
|
|
147
|
+
SLIType
|
|
148
|
+
SLOTargetUnit
|
|
149
|
+
SLOParameters {
|
|
150
|
+
window
|
|
151
|
+
}
|
|
152
|
+
expr
|
|
153
|
+
SLOTarget
|
|
154
|
+
SLIErrorQuery
|
|
155
|
+
SLITotalQuery
|
|
156
|
+
SLODetails
|
|
157
|
+
dashboard
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
version: Spec version (default: "prometheus/v1")
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Generated Prometheus rules as YAML string
|
|
165
|
+
"""
|
|
166
|
+
if not slo_document.get("slos"):
|
|
167
|
+
raise SlothInputError("SLO document has no SLOs defined")
|
|
168
|
+
|
|
169
|
+
service = slo_document["app"]["name"]
|
|
170
|
+
# only process SLOs that have both error and total queries defined
|
|
171
|
+
slo_input = [
|
|
172
|
+
{
|
|
173
|
+
"name": slo["name"],
|
|
174
|
+
"objective": get_slo_target(slo),
|
|
175
|
+
"description": f"{slo['name']} SLO for {service}",
|
|
176
|
+
"sli": {
|
|
177
|
+
"events": {
|
|
178
|
+
"error_query": slo["SLIErrorQuery"].replace(
|
|
179
|
+
"{{window}}", "{{.window}}"
|
|
180
|
+
),
|
|
181
|
+
"total_query": slo["SLITotalQuery"].replace(
|
|
182
|
+
"{{window}}", "{{.window}}"
|
|
183
|
+
),
|
|
184
|
+
}
|
|
185
|
+
},
|
|
186
|
+
"alerting": {
|
|
187
|
+
"name": f"{service.title()}{slo['name'].title()}",
|
|
188
|
+
"annotations": {
|
|
189
|
+
"summary": f"High error rate on {service} {slo['name']}",
|
|
190
|
+
"message": f"High error rate on {service} {slo['name']}",
|
|
191
|
+
"runbook": slo["SLODetails"],
|
|
192
|
+
"dashboard": slo["dashboard"],
|
|
193
|
+
},
|
|
194
|
+
"page_alert": {
|
|
195
|
+
"labels": {
|
|
196
|
+
"severity": "critical",
|
|
197
|
+
"service": service,
|
|
198
|
+
"slo": slo["name"],
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"ticket_alert": {
|
|
202
|
+
"labels": {
|
|
203
|
+
"severity": "high",
|
|
204
|
+
"service": service,
|
|
205
|
+
"slo": slo["name"],
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
}
|
|
210
|
+
for slo in slo_document["slos"]
|
|
211
|
+
if slo.get("SLIErrorQuery") and slo.get("SLITotalQuery")
|
|
212
|
+
]
|
|
213
|
+
|
|
214
|
+
if not slo_input:
|
|
215
|
+
raise SlothInputError(
|
|
216
|
+
"No SLOs found with both SLIErrorQuery and SLITotalQuery defined"
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
spec = {
|
|
220
|
+
"version": version,
|
|
221
|
+
"service": service,
|
|
222
|
+
"slos": slo_input,
|
|
223
|
+
}
|
|
224
|
+
return run_sloth(spec)
|
reconcile/utils/structs.py
CHANGED
|
@@ -36,6 +36,7 @@ from reconcile.typed_queries.app_interface_custom_messages import (
|
|
|
36
36
|
)
|
|
37
37
|
from reconcile.utils.aws_api import AWSApi
|
|
38
38
|
from reconcile.utils.aws_helper import get_region_from_availability_zone
|
|
39
|
+
from reconcile.utils.datetime_util import ensure_utc, utc_now
|
|
39
40
|
from reconcile.utils.external_resource_spec import (
|
|
40
41
|
ExternalResourceSpec,
|
|
41
42
|
ExternalResourceSpecInventory,
|
|
@@ -222,7 +223,7 @@ class TerraformClient:
|
|
|
222
223
|
if disable_deletions_detected:
|
|
223
224
|
raise RuntimeError("Terraform plan has disabled deletions detected")
|
|
224
225
|
|
|
225
|
-
@retry(no_retry_exceptions=RdsUpgradeValidationError)
|
|
226
|
+
@retry(no_retry_exceptions=(RdsUpgradeValidationError,))
|
|
226
227
|
def terraform_plan(
|
|
227
228
|
self, spec: TerraformSpec, enable_deletion: bool
|
|
228
229
|
) -> tuple[bool, list[AccountUser], bool]:
|
|
@@ -419,11 +420,11 @@ class TerraformClient:
|
|
|
419
420
|
deletion_approvals = account.get("deletionApprovals")
|
|
420
421
|
if not deletion_approvals:
|
|
421
422
|
return False
|
|
422
|
-
now =
|
|
423
|
+
now = utc_now()
|
|
423
424
|
for da in deletion_approvals:
|
|
424
425
|
try:
|
|
425
|
-
expiration =
|
|
426
|
-
da["expiration"], DATE_FORMAT
|
|
426
|
+
expiration = ensure_utc(
|
|
427
|
+
datetime.strptime(da["expiration"], DATE_FORMAT) # noqa: DTZ007
|
|
427
428
|
) + timedelta(days=1)
|
|
428
429
|
except ValueError:
|
|
429
430
|
raise DeletionApprovalExpirationValueError(
|