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
reconcile/utils/expiration.py
CHANGED
|
@@ -6,6 +6,8 @@ from typing import (
|
|
|
6
6
|
cast,
|
|
7
7
|
)
|
|
8
8
|
|
|
9
|
+
from reconcile.utils.datetime_util import ensure_utc, utc_now
|
|
10
|
+
|
|
9
11
|
DATE_FORMAT = "%Y-%m-%d"
|
|
10
12
|
|
|
11
13
|
|
|
@@ -17,12 +19,14 @@ DictsOrRoles = TypeVar("DictsOrRoles", bound=Iterable[FilterableRole] | Iterable
|
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
def date_expired(date: str) -> bool:
|
|
20
|
-
exp_date = datetime.datetime.strptime(date, DATE_FORMAT).date()
|
|
21
|
-
current_date =
|
|
22
|
+
exp_date = ensure_utc(datetime.datetime.strptime(date, DATE_FORMAT)).date() # noqa: DTZ007
|
|
23
|
+
current_date = utc_now().date()
|
|
22
24
|
return current_date >= exp_date
|
|
23
25
|
|
|
24
26
|
|
|
25
|
-
def filter
|
|
27
|
+
def filter[DictsOrRoles: Iterable[FilterableRole] | Iterable[dict]](
|
|
28
|
+
roles: DictsOrRoles | None,
|
|
29
|
+
) -> DictsOrRoles:
|
|
26
30
|
"""Filters roles and returns the ones which are not yet expired."""
|
|
27
31
|
filtered = []
|
|
28
32
|
for r in roles or []:
|
|
@@ -157,6 +157,8 @@ class ExternalResourceSpec:
|
|
|
157
157
|
tags["cost-center"] = cost_center
|
|
158
158
|
if service_phase := self.namespace["environment"].get("servicePhase"):
|
|
159
159
|
tags["service-phase"] = service_phase
|
|
160
|
+
if cost_center := self.namespace["environment"].get("costCenter"):
|
|
161
|
+
tags["cost-center"] = cost_center
|
|
160
162
|
|
|
161
163
|
resource_tags_str = self.resource.get("tags")
|
|
162
164
|
if resource_tags_str:
|
reconcile/utils/filtering.py
CHANGED
reconcile/utils/gitlab_api.py
CHANGED
|
@@ -263,13 +263,13 @@ class GitLabApi:
|
|
|
263
263
|
# we can determine if a pending MR exists based on the title
|
|
264
264
|
return any(mr.title == title for mr in mrs)
|
|
265
265
|
|
|
266
|
-
@retry()
|
|
266
|
+
@retry(no_retry_exceptions=(RuntimeError,))
|
|
267
267
|
def get_project_maintainers(
|
|
268
268
|
self, repo_url: str | None = None, query: dict | None = None
|
|
269
|
-
) -> list[str]
|
|
269
|
+
) -> list[str]:
|
|
270
270
|
project = self.project if repo_url is None else self.get_project(repo_url)
|
|
271
271
|
if project is None:
|
|
272
|
-
|
|
272
|
+
raise RuntimeError("project not found")
|
|
273
273
|
members = project.members_all.list(iterator=True, query_parameters=query or {})
|
|
274
274
|
return [m.username for m in members if m.access_level >= 40]
|
|
275
275
|
|
|
@@ -444,6 +444,8 @@ class GitLabApi:
|
|
|
444
444
|
def get_merge_request_comments(
|
|
445
445
|
merge_request: ProjectMergeRequest,
|
|
446
446
|
include_description: bool = False,
|
|
447
|
+
include_approvals: bool = False,
|
|
448
|
+
approval_body: str = "",
|
|
447
449
|
) -> list[Comment]:
|
|
448
450
|
comments = []
|
|
449
451
|
if include_description:
|
|
@@ -455,6 +457,16 @@ class GitLabApi:
|
|
|
455
457
|
created_at=merge_request.created_at,
|
|
456
458
|
)
|
|
457
459
|
)
|
|
460
|
+
if include_approvals:
|
|
461
|
+
comments.extend(
|
|
462
|
+
Comment(
|
|
463
|
+
id=approval["user"]["id"],
|
|
464
|
+
username=approval["user"]["username"],
|
|
465
|
+
body=approval_body,
|
|
466
|
+
created_at=approval["approved_at"],
|
|
467
|
+
)
|
|
468
|
+
for approval in merge_request.approvals.get().approved_by
|
|
469
|
+
)
|
|
458
470
|
comments.extend(
|
|
459
471
|
Comment(
|
|
460
472
|
id=note.id,
|
|
@@ -826,14 +838,16 @@ class GitLabApi:
|
|
|
826
838
|
)
|
|
827
839
|
|
|
828
840
|
def get_commit_sha(self, ref: str, repo_url: str) -> str:
|
|
829
|
-
project
|
|
841
|
+
if not (project := self.get_project(repo_url)):
|
|
842
|
+
raise ValueError(f"Project not found for repo_url: {repo_url}")
|
|
830
843
|
commits = project.commits.list(ref_name=ref, per_page=1, page=1)
|
|
831
844
|
return commits[0].id
|
|
832
845
|
|
|
833
846
|
def repository_compare(
|
|
834
847
|
self, repo_url: str, ref_from: str, ref_to: str
|
|
835
848
|
) -> list[dict[str, Any]]:
|
|
836
|
-
project
|
|
849
|
+
if not (project := self.get_project(repo_url)):
|
|
850
|
+
raise ValueError(f"Project not found for repo_url: {repo_url}")
|
|
837
851
|
response: Any = project.repository_compare(ref_from, ref_to)
|
|
838
852
|
return response.get("commits", [])
|
|
839
853
|
|
|
@@ -164,7 +164,9 @@ class GlitchtipClient(ApiBase):
|
|
|
164
164
|
return ProjectAlert(
|
|
165
165
|
**self._post(
|
|
166
166
|
f"/api/0/projects/{organization_slug}/{project_slug}/alerts/",
|
|
167
|
-
data=alert.
|
|
167
|
+
data=alert.model_dump(
|
|
168
|
+
mode="json", by_alias=True, exclude_unset=True, exclude_none=True
|
|
169
|
+
),
|
|
168
170
|
)
|
|
169
171
|
)
|
|
170
172
|
|
|
@@ -183,7 +185,9 @@ class GlitchtipClient(ApiBase):
|
|
|
183
185
|
return ProjectAlert(
|
|
184
186
|
**self._put(
|
|
185
187
|
f"/api/0/projects/{organization_slug}/{project_slug}/alerts/{alert.pk}/",
|
|
186
|
-
data=alert.
|
|
188
|
+
data=alert.model_dump(
|
|
189
|
+
mode="json", by_alias=True, exclude_unset=True, exclude_none=True
|
|
190
|
+
),
|
|
187
191
|
)
|
|
188
192
|
)
|
|
189
193
|
|
|
@@ -3,13 +3,13 @@ from __future__ import annotations
|
|
|
3
3
|
import re
|
|
4
4
|
from datetime import datetime
|
|
5
5
|
from enum import Enum
|
|
6
|
-
from typing import TYPE_CHECKING, Any
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Self
|
|
7
7
|
|
|
8
8
|
from pydantic import (
|
|
9
9
|
BaseModel,
|
|
10
10
|
Field,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
field_validator,
|
|
12
|
+
model_validator,
|
|
13
13
|
)
|
|
14
14
|
|
|
15
15
|
if TYPE_CHECKING:
|
|
@@ -49,7 +49,8 @@ class Team(BaseModel):
|
|
|
49
49
|
slug: str = ""
|
|
50
50
|
users: list[User] = []
|
|
51
51
|
|
|
52
|
-
@
|
|
52
|
+
@model_validator(mode="before")
|
|
53
|
+
@classmethod
|
|
53
54
|
def name_xor_slug_must_be_set(
|
|
54
55
|
cls, values: MutableMapping[str, Any]
|
|
55
56
|
) -> MutableMapping[str, Any]:
|
|
@@ -58,11 +59,11 @@ class Team(BaseModel):
|
|
|
58
59
|
), "name xor slug must be set!"
|
|
59
60
|
return values
|
|
60
61
|
|
|
61
|
-
@
|
|
62
|
-
def slugify(
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
return
|
|
62
|
+
@model_validator(mode="after")
|
|
63
|
+
def slugify(self) -> Self:
|
|
64
|
+
self.slug = self.slug or slugify(self.name)
|
|
65
|
+
self.name = slugify(self.name) or self.slug
|
|
66
|
+
return self
|
|
66
67
|
|
|
67
68
|
def __lt__(self, other: Team) -> bool:
|
|
68
69
|
return self.slug < other.slug
|
|
@@ -86,16 +87,15 @@ class RecipientType(Enum):
|
|
|
86
87
|
WEBHOOK = "webhook"
|
|
87
88
|
|
|
88
89
|
|
|
89
|
-
class ProjectAlertRecipient(
|
|
90
|
+
class ProjectAlertRecipient(
|
|
91
|
+
BaseModel, validate_by_name=True, validate_by_alias=True, use_enum_values=True
|
|
92
|
+
):
|
|
90
93
|
pk: int | None = Field(None, alias="id")
|
|
91
94
|
recipient_type: RecipientType = Field(..., alias="recipientType")
|
|
92
95
|
url: str = ""
|
|
93
96
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
use_enum_values = True
|
|
97
|
-
|
|
98
|
-
@validator("recipient_type")
|
|
97
|
+
@field_validator("recipient_type")
|
|
98
|
+
@classmethod
|
|
99
99
|
def recipient_type_enforce_enum_type(cls, v: str | RecipientType) -> RecipientType:
|
|
100
100
|
if isinstance(v, RecipientType):
|
|
101
101
|
return v
|
|
@@ -113,17 +113,15 @@ class ProjectAlertRecipient(BaseModel):
|
|
|
113
113
|
return hash((self.recipient_type, self.url))
|
|
114
114
|
|
|
115
115
|
|
|
116
|
-
class ProjectAlert(BaseModel):
|
|
116
|
+
class ProjectAlert(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
117
117
|
pk: int | None = Field(None, alias="id")
|
|
118
118
|
name: str
|
|
119
119
|
timespan_minutes: int = Field(..., alias="timespanMinutes")
|
|
120
120
|
quantity: int
|
|
121
121
|
recipients: list[ProjectAlertRecipient] = Field([], alias="alertRecipients")
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
@root_validator
|
|
123
|
+
@model_validator(mode="before")
|
|
124
|
+
@classmethod
|
|
127
125
|
def empty_name(cls, values: MutableMapping[str, Any]) -> MutableMapping[str, Any]:
|
|
128
126
|
# name is an empty string if the alert was created manually because it can't be set via UI
|
|
129
127
|
# use the pk instead.
|
|
@@ -141,20 +139,18 @@ class ProjectAlert(BaseModel):
|
|
|
141
139
|
)
|
|
142
140
|
|
|
143
141
|
|
|
144
|
-
class Project(BaseModel):
|
|
142
|
+
class Project(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
145
143
|
pk: int | None = Field(None, alias="id")
|
|
146
144
|
name: str
|
|
147
145
|
slug: str = ""
|
|
148
|
-
platform: str | None
|
|
146
|
+
platform: str | None = None
|
|
149
147
|
teams: list[Team] = []
|
|
150
148
|
alerts: list[ProjectAlert] = []
|
|
151
149
|
event_throttle_rate: int = Field(0, alias="eventThrottleRate")
|
|
152
150
|
organization: Organization | None = None
|
|
153
151
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
@root_validator
|
|
152
|
+
@model_validator(mode="before")
|
|
153
|
+
@classmethod
|
|
158
154
|
def slugify(cls, values: MutableMapping[str, Any]) -> MutableMapping[str, Any]:
|
|
159
155
|
values["slug"] = values.get("slug") or slugify(values["name"])
|
|
160
156
|
return values
|
|
@@ -195,7 +191,8 @@ class Organization(BaseModel):
|
|
|
195
191
|
teams: list[Team] = []
|
|
196
192
|
users: list[User] = []
|
|
197
193
|
|
|
198
|
-
@
|
|
194
|
+
@model_validator(mode="before")
|
|
195
|
+
@classmethod
|
|
199
196
|
def slugify(cls, values: MutableMapping[str, Any]) -> MutableMapping[str, Any]:
|
|
200
197
|
values["slug"] = values.get("slug") or slugify(values["name"])
|
|
201
198
|
return values
|
|
@@ -212,4 +209,4 @@ class Organization(BaseModel):
|
|
|
212
209
|
return hash(self.name)
|
|
213
210
|
|
|
214
211
|
|
|
215
|
-
Project.
|
|
212
|
+
Project.model_rebuild()
|
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]
|
|
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
|
-
#
|
|
176
|
-
|
|
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/helpers.py
CHANGED
|
@@ -40,7 +40,7 @@ class InstrumentedImage(Image):
|
|
|
40
40
|
|
|
41
41
|
|
|
42
42
|
class InstrumentedSkopeo(Skopeo):
|
|
43
|
-
def copy(self, *args: Any, **kwargs: Any) ->
|
|
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.
|
|
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.
|
|
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(
|
|
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
|
-
}
|
reconcile/utils/jinja2/utils.py
CHANGED
|
@@ -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
|
|
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.
|
|
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: (
|
|
349
|
-
|
|
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))
|