qontract-reconcile 0.10.0__py3-none-any.whl → 0.10.1.dev1203__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.1.dev1203.dist-info/METADATA +500 -0
- qontract_reconcile-0.10.1.dev1203.dist-info/RECORD +771 -0
- {qontract_reconcile-0.10.0.dist-info → qontract_reconcile-0.10.1.dev1203.dist-info}/WHEEL +1 -2
- {qontract_reconcile-0.10.0.dist-info → qontract_reconcile-0.10.1.dev1203.dist-info}/entry_points.txt +4 -2
- reconcile/acs_notifiers.py +126 -0
- reconcile/acs_policies.py +243 -0
- reconcile/acs_rbac.py +596 -0
- reconcile/aus/advanced_upgrade_service.py +621 -8
- reconcile/aus/aus_label_source.py +115 -0
- reconcile/aus/base.py +1053 -353
- reconcile/{utils → aus}/cluster_version_data.py +27 -12
- reconcile/aus/healthchecks.py +77 -0
- reconcile/aus/metrics.py +158 -0
- reconcile/aus/models.py +245 -5
- reconcile/aus/node_pool_spec.py +35 -0
- reconcile/aus/ocm_addons_upgrade_scheduler_org.py +225 -110
- reconcile/aus/ocm_upgrade_scheduler.py +76 -71
- reconcile/aus/ocm_upgrade_scheduler_org.py +81 -23
- reconcile/aus/version_gate_approver.py +204 -0
- reconcile/aus/version_gates/__init__.py +12 -0
- reconcile/aus/version_gates/handler.py +33 -0
- reconcile/aus/version_gates/ingress_gate_handler.py +32 -0
- reconcile/aus/version_gates/ocp_gate_handler.py +26 -0
- reconcile/aus/version_gates/sts_version_gate_handler.py +100 -0
- reconcile/aws_account_manager/README.md +5 -0
- reconcile/aws_account_manager/integration.py +373 -0
- reconcile/aws_account_manager/merge_request_manager.py +114 -0
- reconcile/aws_account_manager/metrics.py +39 -0
- reconcile/aws_account_manager/reconciler.py +403 -0
- reconcile/aws_account_manager/utils.py +41 -0
- reconcile/aws_ami_cleanup/integration.py +273 -0
- reconcile/aws_ami_share.py +18 -14
- reconcile/aws_cloudwatch_log_retention/integration.py +253 -0
- reconcile/aws_iam_keys.py +1 -1
- reconcile/aws_iam_password_reset.py +56 -20
- reconcile/aws_saml_idp/integration.py +204 -0
- reconcile/aws_saml_roles/integration.py +322 -0
- reconcile/aws_support_cases_sos.py +2 -2
- reconcile/aws_version_sync/integration.py +430 -0
- reconcile/aws_version_sync/merge_request_manager/merge_request.py +156 -0
- reconcile/aws_version_sync/merge_request_manager/merge_request_manager.py +160 -0
- reconcile/aws_version_sync/utils.py +64 -0
- reconcile/blackbox_exporter_endpoint_monitoring.py +2 -5
- reconcile/change_owners/README.md +34 -0
- reconcile/change_owners/approver.py +7 -9
- reconcile/change_owners/bundle.py +134 -9
- reconcile/change_owners/change_log_tracking.py +236 -0
- reconcile/change_owners/change_owners.py +204 -194
- reconcile/change_owners/change_types.py +183 -265
- reconcile/change_owners/changes.py +488 -0
- reconcile/change_owners/decision.py +120 -41
- reconcile/change_owners/diff.py +63 -92
- reconcile/change_owners/implicit_ownership.py +19 -16
- reconcile/change_owners/self_service_roles.py +158 -35
- reconcile/change_owners/tester.py +20 -18
- reconcile/checkpoint.py +4 -6
- reconcile/cli.py +1523 -242
- reconcile/closedbox_endpoint_monitoring_base.py +10 -17
- reconcile/cluster_auth_rhidp/integration.py +257 -0
- reconcile/cluster_deployment_mapper.py +2 -5
- reconcile/cna/assets/asset.py +4 -7
- reconcile/cna/assets/null.py +2 -5
- reconcile/cna/integration.py +2 -3
- reconcile/cna/state.py +6 -9
- reconcile/dashdotdb_base.py +31 -10
- reconcile/dashdotdb_cso.py +3 -6
- reconcile/dashdotdb_dora.py +530 -0
- reconcile/dashdotdb_dvo.py +10 -13
- reconcile/dashdotdb_slo.py +75 -19
- reconcile/database_access_manager.py +753 -0
- reconcile/deadmanssnitch.py +207 -0
- reconcile/dynatrace_token_provider/dependencies.py +69 -0
- reconcile/dynatrace_token_provider/integration.py +656 -0
- reconcile/dynatrace_token_provider/metrics.py +62 -0
- reconcile/dynatrace_token_provider/model.py +14 -0
- reconcile/dynatrace_token_provider/ocm.py +140 -0
- reconcile/dynatrace_token_provider/validate.py +48 -0
- reconcile/endpoints_discovery/integration.py +348 -0
- reconcile/endpoints_discovery/merge_request.py +96 -0
- reconcile/endpoints_discovery/merge_request_manager.py +178 -0
- reconcile/external_resources/aws.py +204 -0
- reconcile/external_resources/factories.py +163 -0
- reconcile/external_resources/integration.py +194 -0
- reconcile/external_resources/integration_secrets_sync.py +47 -0
- reconcile/external_resources/manager.py +405 -0
- reconcile/external_resources/meta.py +17 -0
- reconcile/external_resources/metrics.py +95 -0
- reconcile/external_resources/model.py +350 -0
- reconcile/external_resources/reconciler.py +265 -0
- reconcile/external_resources/secrets_sync.py +465 -0
- reconcile/external_resources/state.py +258 -0
- reconcile/gabi_authorized_users.py +19 -11
- reconcile/gcr_mirror.py +43 -34
- reconcile/github_org.py +4 -6
- reconcile/github_owners.py +1 -1
- reconcile/github_repo_invites.py +2 -5
- reconcile/gitlab_fork_compliance.py +14 -13
- reconcile/gitlab_housekeeping.py +185 -91
- reconcile/gitlab_labeler.py +15 -14
- reconcile/gitlab_members.py +126 -120
- reconcile/gitlab_owners.py +53 -66
- reconcile/gitlab_permissions.py +167 -6
- reconcile/glitchtip/README.md +150 -0
- reconcile/glitchtip/integration.py +99 -51
- reconcile/glitchtip/reconciler.py +99 -70
- reconcile/glitchtip_project_alerts/__init__.py +0 -0
- reconcile/glitchtip_project_alerts/integration.py +333 -0
- reconcile/glitchtip_project_dsn/integration.py +43 -43
- reconcile/gql_definitions/acs/__init__.py +0 -0
- reconcile/gql_definitions/acs/acs_instances.py +83 -0
- reconcile/gql_definitions/acs/acs_policies.py +239 -0
- reconcile/gql_definitions/acs/acs_rbac.py +111 -0
- reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +46 -8
- reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +38 -8
- reconcile/gql_definitions/app_interface_metrics_exporter/__init__.py +0 -0
- reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +61 -0
- reconcile/gql_definitions/aws_account_manager/__init__.py +0 -0
- reconcile/gql_definitions/aws_account_manager/aws_accounts.py +177 -0
- reconcile/gql_definitions/aws_ami_cleanup/__init__.py +0 -0
- reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +161 -0
- reconcile/gql_definitions/aws_saml_idp/__init__.py +0 -0
- reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +117 -0
- reconcile/gql_definitions/aws_saml_roles/__init__.py +0 -0
- reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +117 -0
- reconcile/gql_definitions/aws_saml_roles/roles.py +97 -0
- reconcile/gql_definitions/aws_version_sync/__init__.py +0 -0
- reconcile/gql_definitions/aws_version_sync/clusters.py +83 -0
- reconcile/gql_definitions/aws_version_sync/namespaces.py +143 -0
- reconcile/gql_definitions/change_owners/queries/change_types.py +16 -29
- reconcile/gql_definitions/change_owners/queries/self_service_roles.py +45 -11
- reconcile/gql_definitions/cluster_auth_rhidp/__init__.py +0 -0
- reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +128 -0
- reconcile/gql_definitions/cna/queries/cna_provisioners.py +6 -8
- reconcile/gql_definitions/cna/queries/cna_resources.py +3 -5
- reconcile/gql_definitions/common/alerting_services_settings.py +2 -2
- reconcile/gql_definitions/common/app_code_component_repos.py +9 -5
- reconcile/gql_definitions/{glitchtip/glitchtip_settings.py → common/app_interface_custom_messages.py} +14 -16
- reconcile/gql_definitions/common/app_interface_dms_settings.py +86 -0
- reconcile/gql_definitions/common/app_interface_repo_settings.py +2 -2
- reconcile/gql_definitions/common/app_interface_state_settings.py +3 -5
- reconcile/gql_definitions/common/app_interface_vault_settings.py +3 -5
- reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +120 -0
- reconcile/gql_definitions/common/apps.py +72 -0
- reconcile/gql_definitions/common/aws_vpc_requests.py +109 -0
- reconcile/gql_definitions/common/aws_vpcs.py +84 -0
- reconcile/gql_definitions/common/clusters.py +120 -254
- reconcile/gql_definitions/common/clusters_minimal.py +11 -35
- reconcile/gql_definitions/common/clusters_with_dms.py +72 -0
- reconcile/gql_definitions/common/clusters_with_peering.py +70 -98
- reconcile/gql_definitions/common/github_orgs.py +2 -2
- reconcile/gql_definitions/common/jira_settings.py +68 -0
- reconcile/gql_definitions/common/jiralert_settings.py +68 -0
- reconcile/gql_definitions/common/namespaces.py +74 -32
- reconcile/gql_definitions/common/namespaces_minimal.py +4 -10
- reconcile/gql_definitions/common/ocm_env_telemeter.py +95 -0
- reconcile/gql_definitions/common/ocm_environments.py +4 -2
- reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
- reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -11
- reconcile/gql_definitions/common/pipeline_providers.py +45 -90
- reconcile/gql_definitions/common/quay_instances.py +64 -0
- reconcile/gql_definitions/common/quay_orgs.py +68 -0
- reconcile/gql_definitions/common/reserved_networks.py +94 -0
- reconcile/gql_definitions/common/saas_files.py +133 -95
- reconcile/gql_definitions/common/saas_target_namespaces.py +41 -26
- reconcile/gql_definitions/common/saasherder_settings.py +2 -2
- reconcile/gql_definitions/common/slack_workspaces.py +62 -0
- reconcile/gql_definitions/common/smtp_client_settings.py +2 -2
- reconcile/gql_definitions/common/state_aws_account.py +77 -0
- reconcile/gql_definitions/common/users.py +3 -2
- reconcile/gql_definitions/cost_report/__init__.py +0 -0
- reconcile/gql_definitions/cost_report/app_names.py +68 -0
- reconcile/gql_definitions/cost_report/cost_namespaces.py +86 -0
- reconcile/gql_definitions/cost_report/settings.py +77 -0
- reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +42 -12
- reconcile/gql_definitions/dynatrace_token_provider/__init__.py +0 -0
- reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +79 -0
- reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +84 -0
- reconcile/gql_definitions/endpoints_discovery/__init__.py +0 -0
- reconcile/gql_definitions/endpoints_discovery/namespaces.py +127 -0
- reconcile/gql_definitions/external_resources/__init__.py +0 -0
- reconcile/gql_definitions/external_resources/aws_accounts.py +73 -0
- reconcile/gql_definitions/external_resources/external_resources_modules.py +78 -0
- reconcile/gql_definitions/external_resources/external_resources_namespaces.py +1111 -0
- reconcile/gql_definitions/external_resources/external_resources_settings.py +98 -0
- reconcile/gql_definitions/fragments/aus_organization.py +34 -39
- reconcile/gql_definitions/fragments/aws_account_common.py +62 -0
- reconcile/gql_definitions/fragments/aws_account_managed.py +57 -0
- reconcile/gql_definitions/fragments/aws_account_sso.py +35 -0
- reconcile/gql_definitions/fragments/aws_infra_management_account.py +2 -2
- reconcile/gql_definitions/fragments/aws_vpc.py +47 -0
- reconcile/gql_definitions/fragments/aws_vpc_request.py +65 -0
- reconcile/gql_definitions/fragments/aws_vpc_request_subnet.py +29 -0
- reconcile/gql_definitions/fragments/deplopy_resources.py +7 -7
- reconcile/gql_definitions/fragments/disable.py +28 -0
- reconcile/gql_definitions/fragments/jumphost_common_fields.py +2 -2
- reconcile/gql_definitions/fragments/membership_source.py +47 -0
- reconcile/gql_definitions/fragments/minimal_ocm_organization.py +29 -0
- reconcile/gql_definitions/fragments/oc_connection_cluster.py +4 -9
- reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
- reconcile/gql_definitions/fragments/pipeline_provider_retention.py +30 -0
- reconcile/gql_definitions/fragments/prometheus_instance.py +48 -0
- reconcile/gql_definitions/fragments/resource_limits_requirements.py +29 -0
- reconcile/gql_definitions/fragments/{resource_requirements.py → resource_requests_requirements.py} +3 -3
- reconcile/gql_definitions/fragments/resource_values.py +2 -2
- reconcile/gql_definitions/fragments/saas_target_namespace.py +55 -12
- reconcile/gql_definitions/fragments/serviceaccount_token.py +38 -0
- reconcile/gql_definitions/fragments/terraform_state.py +36 -0
- reconcile/gql_definitions/fragments/upgrade_policy.py +5 -3
- reconcile/gql_definitions/fragments/user.py +3 -2
- reconcile/gql_definitions/fragments/vault_secret.py +2 -2
- reconcile/gql_definitions/gitlab_members/gitlab_instances.py +6 -2
- reconcile/gql_definitions/gitlab_members/permissions.py +3 -5
- reconcile/gql_definitions/glitchtip/glitchtip_instance.py +16 -2
- reconcile/gql_definitions/glitchtip/glitchtip_project.py +22 -23
- reconcile/gql_definitions/glitchtip_project_alerts/__init__.py +0 -0
- reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +173 -0
- reconcile/gql_definitions/integrations/integrations.py +62 -45
- reconcile/gql_definitions/introspection.json +51176 -0
- reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +13 -5
- reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +79 -0
- reconcile/gql_definitions/jira/__init__.py +0 -0
- reconcile/gql_definitions/jira/jira_servers.py +80 -0
- reconcile/gql_definitions/jira_permissions_validator/__init__.py +0 -0
- reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +131 -0
- reconcile/gql_definitions/jumphosts/jumphosts.py +3 -5
- reconcile/gql_definitions/ldap_groups/__init__.py +0 -0
- reconcile/gql_definitions/ldap_groups/roles.py +111 -0
- reconcile/gql_definitions/ldap_groups/settings.py +79 -0
- reconcile/gql_definitions/maintenance/__init__.py +0 -0
- reconcile/gql_definitions/maintenance/maintenances.py +101 -0
- reconcile/gql_definitions/membershipsources/__init__.py +0 -0
- reconcile/gql_definitions/membershipsources/roles.py +112 -0
- reconcile/gql_definitions/ocm_labels/__init__.py +0 -0
- reconcile/gql_definitions/ocm_labels/clusters.py +112 -0
- reconcile/gql_definitions/ocm_labels/organizations.py +78 -0
- reconcile/gql_definitions/ocm_subscription_labels/__init__.py +0 -0
- reconcile/gql_definitions/openshift_cluster_bots/__init__.py +0 -0
- reconcile/gql_definitions/openshift_cluster_bots/clusters.py +126 -0
- reconcile/gql_definitions/openshift_groups/managed_groups.py +2 -2
- reconcile/gql_definitions/openshift_groups/managed_roles.py +3 -2
- reconcile/gql_definitions/openshift_serviceaccount_tokens/__init__.py +0 -0
- reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +132 -0
- reconcile/gql_definitions/quay_membership/quay_membership.py +3 -5
- reconcile/gql_definitions/rhidp/__init__.py +0 -0
- reconcile/gql_definitions/rhidp/organizations.py +96 -0
- reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +2 -2
- reconcile/gql_definitions/service_dependencies/service_dependencies.py +9 -31
- reconcile/gql_definitions/sharding/aws_accounts.py +2 -2
- reconcile/gql_definitions/sharding/ocm_organization.py +63 -0
- reconcile/gql_definitions/skupper_network/site_controller_template.py +2 -2
- reconcile/gql_definitions/skupper_network/skupper_networks.py +12 -38
- reconcile/gql_definitions/slack_usergroups/clusters.py +2 -2
- reconcile/gql_definitions/slack_usergroups/permissions.py +8 -15
- reconcile/gql_definitions/slack_usergroups/users.py +3 -2
- reconcile/gql_definitions/slo_documents/__init__.py +0 -0
- reconcile/gql_definitions/slo_documents/slo_documents.py +142 -0
- reconcile/gql_definitions/status_board/__init__.py +0 -0
- reconcile/gql_definitions/status_board/status_board.py +163 -0
- reconcile/gql_definitions/statuspage/statuspages.py +56 -7
- reconcile/gql_definitions/templating/__init__.py +0 -0
- reconcile/gql_definitions/templating/template_collection.py +130 -0
- reconcile/gql_definitions/templating/templates.py +108 -0
- reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +4 -8
- reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +8 -8
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +6 -8
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +45 -56
- reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +4 -8
- reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +4 -8
- reconcile/gql_definitions/terraform_init/__init__.py +0 -0
- reconcile/gql_definitions/terraform_init/aws_accounts.py +93 -0
- reconcile/gql_definitions/terraform_repo/__init__.py +0 -0
- reconcile/gql_definitions/terraform_repo/terraform_repo.py +141 -0
- reconcile/gql_definitions/terraform_resources/database_access_manager.py +158 -0
- reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +153 -162
- reconcile/gql_definitions/terraform_tgw_attachments/__init__.py +0 -0
- reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +119 -0
- reconcile/gql_definitions/unleash_feature_toggles/__init__.py +0 -0
- reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +113 -0
- reconcile/gql_definitions/vault_instances/vault_instances.py +17 -50
- reconcile/gql_definitions/vault_policies/vault_policies.py +2 -2
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +49 -12
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +7 -2
- reconcile/integrations_manager.py +25 -13
- reconcile/jenkins/types.py +5 -1
- reconcile/jenkins_base.py +36 -0
- reconcile/jenkins_job_builder.py +10 -48
- reconcile/jenkins_job_builds_cleaner.py +40 -25
- reconcile/jenkins_job_cleaner.py +1 -3
- reconcile/jenkins_roles.py +22 -26
- reconcile/jenkins_webhooks.py +9 -6
- reconcile/jenkins_worker_fleets.py +11 -6
- reconcile/jira_permissions_validator.py +340 -0
- reconcile/jira_watcher.py +3 -5
- reconcile/ldap_groups/__init__.py +0 -0
- reconcile/ldap_groups/integration.py +279 -0
- reconcile/ldap_users.py +3 -0
- reconcile/ocm/types.py +39 -59
- reconcile/ocm_additional_routers.py +0 -1
- reconcile/ocm_addons_upgrade_tests_trigger.py +10 -15
- reconcile/ocm_aws_infrastructure_access.py +30 -32
- reconcile/ocm_clusters.py +217 -130
- reconcile/ocm_external_configuration_labels.py +15 -0
- reconcile/ocm_github_idp.py +1 -1
- reconcile/ocm_groups.py +25 -5
- reconcile/ocm_internal_notifications/__init__.py +0 -0
- reconcile/ocm_internal_notifications/integration.py +119 -0
- reconcile/ocm_labels/__init__.py +0 -0
- reconcile/ocm_labels/integration.py +409 -0
- reconcile/ocm_machine_pools.py +517 -108
- reconcile/ocm_upgrade_scheduler_org_updater.py +15 -11
- reconcile/openshift_base.py +609 -207
- reconcile/openshift_cluster_bots.py +344 -0
- reconcile/openshift_clusterrolebindings.py +15 -15
- reconcile/openshift_groups.py +42 -45
- reconcile/openshift_limitranges.py +1 -0
- reconcile/openshift_namespace_labels.py +22 -28
- reconcile/openshift_namespaces.py +22 -22
- reconcile/openshift_network_policies.py +4 -8
- reconcile/openshift_prometheus_rules.py +43 -0
- reconcile/openshift_resourcequotas.py +2 -16
- reconcile/openshift_resources.py +12 -10
- reconcile/openshift_resources_base.py +304 -328
- reconcile/openshift_rolebindings.py +18 -20
- reconcile/openshift_saas_deploy.py +105 -21
- reconcile/openshift_saas_deploy_change_tester.py +30 -35
- reconcile/openshift_saas_deploy_trigger_base.py +39 -36
- reconcile/openshift_saas_deploy_trigger_cleaner.py +41 -27
- reconcile/openshift_saas_deploy_trigger_configs.py +1 -2
- reconcile/openshift_saas_deploy_trigger_images.py +1 -2
- reconcile/openshift_saas_deploy_trigger_moving_commits.py +1 -2
- reconcile/openshift_saas_deploy_trigger_upstream_jobs.py +1 -2
- reconcile/openshift_serviceaccount_tokens.py +138 -74
- reconcile/openshift_tekton_resources.py +89 -24
- reconcile/openshift_upgrade_watcher.py +110 -62
- reconcile/openshift_users.py +16 -15
- reconcile/openshift_vault_secrets.py +11 -6
- reconcile/oum/__init__.py +0 -0
- reconcile/oum/base.py +387 -0
- reconcile/oum/labelset.py +55 -0
- reconcile/oum/metrics.py +71 -0
- reconcile/oum/models.py +69 -0
- reconcile/oum/providers.py +59 -0
- reconcile/oum/standalone.py +196 -0
- reconcile/prometheus_rules_tester/integration.py +31 -23
- reconcile/quay_base.py +4 -1
- reconcile/quay_membership.py +1 -2
- reconcile/quay_mirror.py +111 -61
- reconcile/quay_mirror_org.py +34 -21
- reconcile/quay_permissions.py +7 -3
- reconcile/quay_repos.py +24 -32
- reconcile/queries.py +263 -198
- reconcile/query_validator.py +3 -5
- reconcile/resource_scraper.py +3 -4
- reconcile/{template_tester.py → resource_template_tester.py} +3 -3
- reconcile/rhidp/__init__.py +0 -0
- reconcile/rhidp/common.py +214 -0
- reconcile/rhidp/metrics.py +20 -0
- reconcile/rhidp/ocm_oidc_idp/__init__.py +0 -0
- reconcile/rhidp/ocm_oidc_idp/base.py +221 -0
- reconcile/rhidp/ocm_oidc_idp/integration.py +56 -0
- reconcile/rhidp/ocm_oidc_idp/metrics.py +22 -0
- reconcile/rhidp/sso_client/__init__.py +0 -0
- reconcile/rhidp/sso_client/base.py +266 -0
- reconcile/rhidp/sso_client/integration.py +60 -0
- reconcile/rhidp/sso_client/metrics.py +39 -0
- reconcile/run_integration.py +293 -0
- reconcile/saas_auto_promotions_manager/integration.py +69 -24
- reconcile/saas_auto_promotions_manager/merge_request_manager/batcher.py +208 -0
- reconcile/saas_auto_promotions_manager/merge_request_manager/desired_state.py +28 -0
- reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request.py +3 -4
- reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request_manager_v2.py +172 -0
- reconcile/saas_auto_promotions_manager/merge_request_manager/metrics.py +42 -0
- reconcile/saas_auto_promotions_manager/merge_request_manager/mr_parser.py +226 -0
- reconcile/saas_auto_promotions_manager/merge_request_manager/open_merge_requests.py +23 -0
- reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +108 -32
- reconcile/saas_auto_promotions_manager/meta.py +4 -0
- reconcile/saas_auto_promotions_manager/publisher.py +32 -4
- reconcile/saas_auto_promotions_manager/s3_exporter.py +77 -0
- reconcile/saas_auto_promotions_manager/subscriber.py +110 -23
- reconcile/saas_auto_promotions_manager/utils/saas_files_inventory.py +48 -41
- reconcile/saas_file_validator.py +16 -6
- reconcile/sendgrid_teammates.py +27 -12
- reconcile/service_dependencies.py +0 -3
- reconcile/signalfx_endpoint_monitoring.py +2 -5
- reconcile/skupper_network/integration.py +10 -11
- reconcile/skupper_network/models.py +3 -5
- reconcile/skupper_network/reconciler.py +28 -35
- reconcile/skupper_network/site_controller.py +8 -8
- reconcile/slack_base.py +4 -7
- reconcile/slack_usergroups.py +249 -171
- reconcile/sql_query.py +324 -171
- reconcile/status.py +0 -1
- reconcile/status_board.py +275 -0
- reconcile/statuspage/__init__.py +0 -5
- reconcile/statuspage/atlassian.py +219 -80
- reconcile/statuspage/integration.py +9 -97
- reconcile/statuspage/integrations/__init__.py +0 -0
- reconcile/statuspage/integrations/components.py +77 -0
- reconcile/statuspage/integrations/maintenances.py +111 -0
- reconcile/statuspage/page.py +107 -72
- reconcile/statuspage/state.py +6 -11
- reconcile/statuspage/status.py +8 -12
- reconcile/templates/rosa-classic-cluster-creation.sh.j2 +60 -0
- reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +61 -0
- reconcile/templating/__init__.py +0 -0
- reconcile/templating/lib/__init__.py +0 -0
- reconcile/templating/lib/merge_request_manager.py +180 -0
- reconcile/templating/lib/model.py +20 -0
- reconcile/templating/lib/rendering.py +191 -0
- reconcile/templating/renderer.py +410 -0
- reconcile/templating/validator.py +153 -0
- reconcile/terraform_aws_route53.py +13 -10
- reconcile/terraform_cloudflare_dns.py +92 -122
- reconcile/terraform_cloudflare_resources.py +15 -13
- reconcile/terraform_cloudflare_users.py +27 -27
- reconcile/terraform_init/__init__.py +0 -0
- reconcile/terraform_init/integration.py +165 -0
- reconcile/terraform_init/merge_request.py +57 -0
- reconcile/terraform_init/merge_request_manager.py +102 -0
- reconcile/terraform_repo.py +403 -0
- reconcile/terraform_resources.py +266 -168
- reconcile/terraform_tgw_attachments.py +417 -167
- reconcile/terraform_users.py +40 -17
- reconcile/terraform_vpc_peerings.py +310 -142
- reconcile/terraform_vpc_resources/__init__.py +0 -0
- reconcile/terraform_vpc_resources/integration.py +220 -0
- reconcile/terraform_vpc_resources/merge_request.py +57 -0
- reconcile/terraform_vpc_resources/merge_request_manager.py +107 -0
- reconcile/typed_queries/alerting_services_settings.py +1 -2
- reconcile/typed_queries/app_interface_custom_messages.py +24 -0
- reconcile/typed_queries/app_interface_deadmanssnitch_settings.py +17 -0
- reconcile/typed_queries/app_interface_metrics_exporter/__init__.py +0 -0
- reconcile/typed_queries/app_interface_metrics_exporter/onboarding_status.py +13 -0
- reconcile/typed_queries/app_interface_repo_url.py +1 -2
- reconcile/typed_queries/app_interface_state_settings.py +1 -3
- reconcile/typed_queries/app_interface_vault_settings.py +1 -2
- reconcile/typed_queries/app_quay_repos_escalation_policies.py +14 -0
- reconcile/typed_queries/apps.py +11 -0
- reconcile/typed_queries/aws_vpc_requests.py +9 -0
- reconcile/typed_queries/aws_vpcs.py +12 -0
- reconcile/typed_queries/cloudflare.py +10 -0
- reconcile/typed_queries/clusters.py +7 -5
- reconcile/typed_queries/clusters_minimal.py +6 -5
- reconcile/typed_queries/clusters_with_dms.py +16 -0
- reconcile/typed_queries/cost_report/__init__.py +0 -0
- reconcile/typed_queries/cost_report/app_names.py +22 -0
- reconcile/typed_queries/cost_report/cost_namespaces.py +43 -0
- reconcile/typed_queries/cost_report/settings.py +15 -0
- reconcile/typed_queries/dynatrace.py +10 -0
- reconcile/typed_queries/dynatrace_environments.py +14 -0
- reconcile/typed_queries/dynatrace_token_provider_token_specs.py +14 -0
- reconcile/typed_queries/external_resources.py +46 -0
- reconcile/typed_queries/get_state_aws_account.py +20 -0
- reconcile/typed_queries/glitchtip.py +10 -0
- reconcile/typed_queries/jenkins.py +25 -0
- reconcile/typed_queries/jira.py +7 -0
- reconcile/typed_queries/jira_settings.py +16 -0
- reconcile/typed_queries/jiralert_settings.py +22 -0
- reconcile/typed_queries/ocm.py +8 -0
- reconcile/typed_queries/pagerduty_instances.py +2 -7
- reconcile/typed_queries/quay.py +23 -0
- reconcile/typed_queries/repos.py +20 -8
- reconcile/typed_queries/reserved_networks.py +12 -0
- reconcile/typed_queries/saas_files.py +221 -167
- reconcile/typed_queries/slack.py +7 -0
- reconcile/typed_queries/slo_documents.py +12 -0
- reconcile/typed_queries/status_board.py +58 -0
- reconcile/typed_queries/tekton_pipeline_providers.py +1 -2
- reconcile/typed_queries/terraform_namespaces.py +1 -2
- reconcile/typed_queries/terraform_tgw_attachments/__init__.py +0 -0
- reconcile/typed_queries/terraform_tgw_attachments/aws_accounts.py +16 -0
- reconcile/typed_queries/unleash.py +10 -0
- reconcile/typed_queries/users.py +11 -0
- reconcile/typed_queries/vault.py +10 -0
- reconcile/unleash_feature_toggles/__init__.py +0 -0
- reconcile/unleash_feature_toggles/integration.py +287 -0
- reconcile/utils/acs/__init__.py +0 -0
- reconcile/utils/acs/base.py +81 -0
- reconcile/utils/acs/notifiers.py +143 -0
- reconcile/utils/acs/policies.py +163 -0
- reconcile/utils/acs/rbac.py +277 -0
- reconcile/utils/aggregated_list.py +11 -9
- reconcile/utils/amtool.py +6 -4
- reconcile/utils/aws_api.py +279 -66
- reconcile/utils/aws_api_typed/__init__.py +0 -0
- reconcile/utils/aws_api_typed/account.py +23 -0
- reconcile/utils/aws_api_typed/api.py +273 -0
- reconcile/utils/aws_api_typed/dynamodb.py +16 -0
- reconcile/utils/aws_api_typed/iam.py +67 -0
- reconcile/utils/aws_api_typed/organization.py +152 -0
- reconcile/utils/aws_api_typed/s3.py +26 -0
- reconcile/utils/aws_api_typed/service_quotas.py +79 -0
- reconcile/utils/aws_api_typed/sts.py +36 -0
- reconcile/utils/aws_api_typed/support.py +79 -0
- reconcile/utils/aws_helper.py +42 -3
- reconcile/utils/batches.py +11 -0
- reconcile/utils/binary.py +7 -9
- reconcile/utils/cloud_resource_best_practice/__init__.py +0 -0
- reconcile/utils/cloud_resource_best_practice/aws_rds.py +66 -0
- reconcile/utils/clusterhealth/__init__.py +0 -0
- reconcile/utils/clusterhealth/providerbase.py +39 -0
- reconcile/utils/clusterhealth/telemeter.py +39 -0
- reconcile/utils/config.py +3 -4
- reconcile/utils/deadmanssnitch_api.py +86 -0
- reconcile/utils/differ.py +205 -0
- reconcile/utils/disabled_integrations.py +4 -6
- reconcile/utils/dynatrace/__init__.py +0 -0
- reconcile/utils/dynatrace/client.py +93 -0
- reconcile/utils/early_exit_cache.py +289 -0
- reconcile/utils/elasticsearch_exceptions.py +5 -0
- reconcile/utils/environ.py +2 -2
- reconcile/utils/exceptions.py +4 -0
- reconcile/utils/expiration.py +4 -8
- reconcile/utils/extended_early_exit.py +210 -0
- reconcile/utils/external_resource_spec.py +34 -12
- reconcile/utils/external_resources.py +48 -20
- reconcile/utils/filtering.py +16 -0
- reconcile/utils/git.py +49 -16
- reconcile/utils/github_api.py +10 -9
- reconcile/utils/gitlab_api.py +333 -190
- reconcile/utils/glitchtip/client.py +97 -100
- reconcile/utils/glitchtip/models.py +89 -11
- reconcile/utils/gql.py +157 -58
- reconcile/utils/grouping.py +17 -0
- reconcile/utils/helm.py +89 -18
- reconcile/utils/helpers.py +51 -0
- reconcile/utils/imap_client.py +5 -6
- reconcile/utils/internal_groups/__init__.py +0 -0
- reconcile/utils/internal_groups/client.py +160 -0
- reconcile/utils/internal_groups/models.py +71 -0
- reconcile/utils/jenkins_api.py +10 -34
- reconcile/utils/jinja2/__init__.py +0 -0
- reconcile/utils/{jinja2_ext.py → jinja2/extensions.py} +6 -4
- reconcile/utils/jinja2/filters.py +142 -0
- reconcile/utils/jinja2/utils.py +278 -0
- reconcile/utils/jira_client.py +165 -8
- reconcile/utils/jjb_client.py +47 -35
- reconcile/utils/jobcontroller/__init__.py +0 -0
- reconcile/utils/jobcontroller/controller.py +413 -0
- reconcile/utils/jobcontroller/models.py +195 -0
- reconcile/utils/jsonpath.py +4 -5
- reconcile/utils/jump_host.py +13 -12
- reconcile/utils/keycloak.py +106 -0
- reconcile/utils/ldap_client.py +35 -6
- reconcile/utils/lean_terraform_client.py +115 -6
- reconcile/utils/membershipsources/__init__.py +0 -0
- reconcile/utils/membershipsources/app_interface_resolver.py +60 -0
- reconcile/utils/membershipsources/models.py +91 -0
- reconcile/utils/membershipsources/resolver.py +110 -0
- reconcile/utils/merge_request_manager/__init__.py +0 -0
- reconcile/utils/merge_request_manager/merge_request_manager.py +99 -0
- reconcile/utils/merge_request_manager/parser.py +67 -0
- reconcile/utils/metrics.py +511 -1
- reconcile/utils/models.py +123 -0
- reconcile/utils/mr/README.md +198 -0
- reconcile/utils/mr/__init__.py +14 -10
- reconcile/utils/mr/app_interface_reporter.py +2 -2
- reconcile/utils/mr/aws_access.py +4 -4
- reconcile/utils/mr/base.py +51 -31
- reconcile/utils/mr/clusters_updates.py +10 -7
- reconcile/utils/mr/glitchtip_access_reporter.py +2 -4
- reconcile/utils/mr/labels.py +14 -1
- reconcile/utils/mr/notificator.py +1 -3
- reconcile/utils/mr/ocm_update_recommended_version.py +1 -2
- reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py +7 -3
- reconcile/utils/mr/promote_qontract.py +203 -0
- reconcile/utils/mr/user_maintenance.py +24 -4
- reconcile/utils/oauth2_backend_application_session.py +132 -0
- reconcile/utils/oc.py +194 -170
- reconcile/utils/oc_connection_parameters.py +40 -51
- reconcile/utils/oc_filters.py +11 -13
- reconcile/utils/oc_map.py +14 -35
- reconcile/utils/ocm/__init__.py +30 -1
- reconcile/utils/ocm/addons.py +228 -0
- reconcile/utils/ocm/base.py +618 -5
- reconcile/utils/ocm/cluster_groups.py +5 -56
- reconcile/utils/ocm/clusters.py +111 -99
- reconcile/utils/ocm/identity_providers.py +66 -0
- reconcile/utils/ocm/label_sources.py +75 -0
- reconcile/utils/ocm/labels.py +139 -54
- reconcile/utils/ocm/manifests.py +39 -0
- reconcile/utils/ocm/ocm.py +182 -928
- reconcile/utils/ocm/products.py +758 -0
- reconcile/utils/ocm/search_filters.py +20 -28
- reconcile/utils/ocm/service_log.py +32 -79
- reconcile/utils/ocm/sre_capability_labels.py +51 -0
- reconcile/utils/ocm/status_board.py +66 -0
- reconcile/utils/ocm/subscriptions.py +49 -59
- reconcile/utils/ocm/syncsets.py +39 -0
- reconcile/utils/ocm/upgrades.py +181 -0
- reconcile/utils/ocm_base_client.py +71 -36
- reconcile/utils/openshift_resource.py +113 -67
- reconcile/utils/output.py +18 -11
- reconcile/utils/pagerduty_api.py +16 -10
- reconcile/utils/parse_dhms_duration.py +13 -1
- reconcile/utils/prometheus.py +123 -0
- reconcile/utils/promotion_state.py +56 -19
- reconcile/utils/promtool.py +5 -8
- reconcile/utils/quay_api.py +13 -25
- reconcile/utils/raw_github_api.py +3 -5
- reconcile/utils/repo_owners.py +2 -8
- reconcile/utils/rest_api_base.py +126 -0
- reconcile/utils/rosa/__init__.py +0 -0
- reconcile/utils/rosa/rosa_cli.py +310 -0
- reconcile/utils/rosa/session.py +201 -0
- reconcile/utils/ruamel.py +16 -0
- reconcile/utils/runtime/__init__.py +0 -1
- reconcile/utils/runtime/desired_state_diff.py +9 -20
- reconcile/utils/runtime/environment.py +33 -8
- reconcile/utils/runtime/integration.py +28 -12
- reconcile/utils/runtime/meta.py +1 -3
- reconcile/utils/runtime/runner.py +8 -11
- reconcile/utils/runtime/sharding.py +93 -36
- reconcile/utils/saasherder/__init__.py +1 -1
- reconcile/utils/saasherder/interfaces.py +143 -138
- reconcile/utils/saasherder/models.py +201 -43
- reconcile/utils/saasherder/saasherder.py +508 -378
- reconcile/utils/secret_reader.py +22 -27
- reconcile/utils/semver_helper.py +15 -1
- reconcile/utils/slack_api.py +124 -36
- reconcile/utils/smtp_client.py +1 -2
- reconcile/utils/sqs_gateway.py +10 -6
- reconcile/utils/state.py +276 -127
- reconcile/utils/terraform/config_client.py +6 -7
- reconcile/utils/terraform_client.py +284 -125
- reconcile/utils/terrascript/cloudflare_client.py +38 -17
- reconcile/utils/terrascript/cloudflare_resources.py +67 -18
- reconcile/utils/terrascript/models.py +2 -3
- reconcile/utils/terrascript/resources.py +1 -2
- reconcile/utils/terrascript_aws_client.py +1292 -540
- reconcile/utils/three_way_diff_strategy.py +157 -0
- reconcile/utils/unleash/__init__.py +11 -0
- reconcile/utils/{unleash.py → unleash/client.py} +35 -29
- reconcile/utils/unleash/server.py +145 -0
- reconcile/utils/vault.py +42 -32
- reconcile/utils/vaultsecretref.py +2 -4
- reconcile/utils/vcs.py +250 -0
- reconcile/vault_replication.py +38 -31
- reconcile/vpc_peerings_validator.py +82 -13
- tools/app_interface_metrics_exporter.py +70 -0
- tools/app_interface_reporter.py +44 -157
- tools/cli_commands/container_images_report.py +154 -0
- tools/cli_commands/cost_report/__init__.py +0 -0
- tools/cli_commands/cost_report/aws.py +137 -0
- tools/cli_commands/cost_report/cost_management_api.py +155 -0
- tools/cli_commands/cost_report/model.py +49 -0
- tools/cli_commands/cost_report/openshift.py +166 -0
- tools/cli_commands/cost_report/openshift_cost_optimization.py +187 -0
- tools/cli_commands/cost_report/response.py +124 -0
- tools/cli_commands/cost_report/util.py +72 -0
- tools/cli_commands/cost_report/view.py +524 -0
- tools/cli_commands/erv2.py +620 -0
- tools/cli_commands/gpg_encrypt.py +5 -8
- tools/cli_commands/systems_and_tools.py +489 -0
- tools/glitchtip_access_revalidation.py +1 -1
- tools/qontract_cli.py +2301 -673
- tools/saas_metrics_exporter/__init__.py +0 -0
- tools/saas_metrics_exporter/commit_distance/__init__.py +0 -0
- tools/saas_metrics_exporter/commit_distance/channel.py +63 -0
- tools/saas_metrics_exporter/commit_distance/commit_distance.py +103 -0
- tools/saas_metrics_exporter/commit_distance/metrics.py +19 -0
- tools/saas_metrics_exporter/main.py +99 -0
- tools/saas_promotion_state/__init__.py +0 -0
- tools/saas_promotion_state/saas_promotion_state.py +105 -0
- tools/sd_app_sre_alert_report.py +145 -0
- tools/template_validation.py +107 -0
- e2e_tests/cli.py +0 -83
- e2e_tests/create_namespace.py +0 -43
- e2e_tests/dedicated_admin_rolebindings.py +0 -44
- e2e_tests/dedicated_admin_test_base.py +0 -39
- e2e_tests/default_network_policies.py +0 -47
- e2e_tests/default_project_labels.py +0 -52
- e2e_tests/network_policy_test_base.py +0 -17
- e2e_tests/test_base.py +0 -56
- qontract_reconcile-0.10.0.dist-info/LICENSE +0 -201
- qontract_reconcile-0.10.0.dist-info/METADATA +0 -63
- qontract_reconcile-0.10.0.dist-info/RECORD +0 -586
- qontract_reconcile-0.10.0.dist-info/top_level.txt +0 -4
- reconcile/ecr_mirror.py +0 -152
- reconcile/github_scanner.py +0 -74
- reconcile/gitlab_integrations.py +0 -63
- reconcile/gql_definitions/ocm_oidc_idp/clusters.py +0 -195
- reconcile/gql_definitions/ocp_release_mirror/ocp_release_mirror.py +0 -287
- reconcile/integrations_validator.py +0 -18
- reconcile/jenkins_plugins.py +0 -129
- reconcile/kafka_clusters.py +0 -208
- reconcile/ocm_cluster_admin.py +0 -42
- reconcile/ocm_oidc_idp.py +0 -198
- reconcile/ocp_release_mirror.py +0 -373
- reconcile/prometheus_rules_tester_old.py +0 -436
- reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request_manager.py +0 -279
- reconcile/saas_auto_promotions_manager/utils/vcs.py +0 -141
- reconcile/sentry_config.py +0 -613
- reconcile/sentry_helper.py +0 -69
- reconcile/test/conftest.py +0 -187
- reconcile/test/fixtures.py +0 -24
- reconcile/test/saas_auto_promotions_manager/conftest.py +0 -69
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/conftest.py +0 -110
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/data_keys.py +0 -10
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_housekeeping.py +0 -200
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_merge_request_manager.py +0 -151
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/conftest.py +0 -63
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/data_keys.py +0 -4
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_multiple_namespaces.py +0 -46
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_single_namespace.py +0 -94
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_single_target.py +0 -44
- reconcile/test/saas_auto_promotions_manager/subscriber/conftest.py +0 -74
- reconcile/test/saas_auto_promotions_manager/subscriber/data_keys.py +0 -11
- reconcile/test/saas_auto_promotions_manager/subscriber/test_content_hash.py +0 -155
- reconcile/test/saas_auto_promotions_manager/subscriber/test_diff.py +0 -173
- reconcile/test/saas_auto_promotions_manager/subscriber/test_multiple_channels_config_hash.py +0 -226
- reconcile/test/saas_auto_promotions_manager/subscriber/test_multiple_channels_moving_ref.py +0 -224
- reconcile/test/saas_auto_promotions_manager/subscriber/test_single_channel_with_single_publisher.py +0 -350
- reconcile/test/saas_auto_promotions_manager/test_integration_test.py +0 -129
- reconcile/test/saas_auto_promotions_manager/utils/saas_files_inventory/test_multiple_publishers_for_single_channel.py +0 -70
- reconcile/test/saas_auto_promotions_manager/utils/saas_files_inventory/test_saas_files_use_target_config_hash.py +0 -63
- reconcile/test/saas_auto_promotions_manager/utils/saas_files_inventory/test_saas_files_with_auto_promote.py +0 -74
- reconcile/test/saas_auto_promotions_manager/utils/saas_files_inventory/test_saas_files_without_auto_promote.py +0 -65
- reconcile/test/test_aggregated_list.py +0 -237
- reconcile/test/test_amtool.py +0 -37
- reconcile/test/test_auto_promoter.py +0 -295
- reconcile/test/test_aws_ami_share.py +0 -68
- reconcile/test/test_aws_iam_keys.py +0 -70
- reconcile/test/test_aws_iam_password_reset.py +0 -35
- reconcile/test/test_aws_support_cases_sos.py +0 -23
- reconcile/test/test_checkpoint.py +0 -178
- reconcile/test/test_cli.py +0 -41
- reconcile/test/test_closedbox_endpoint_monitoring.py +0 -207
- reconcile/test/test_gabi_authorized_users.py +0 -72
- reconcile/test/test_github_org.py +0 -154
- reconcile/test/test_github_repo_invites.py +0 -123
- reconcile/test/test_gitlab_housekeeping.py +0 -88
- reconcile/test/test_gitlab_labeler.py +0 -129
- reconcile/test/test_gitlab_members.py +0 -283
- reconcile/test/test_instrumented_wrappers.py +0 -18
- reconcile/test/test_integrations_manager.py +0 -995
- reconcile/test/test_jenkins_worker_fleets.py +0 -55
- reconcile/test/test_jump_host.py +0 -117
- reconcile/test/test_ldap_users.py +0 -123
- reconcile/test/test_make.py +0 -28
- reconcile/test/test_ocm_additional_routers.py +0 -134
- reconcile/test/test_ocm_addons_upgrade_scheduler_org.py +0 -149
- reconcile/test/test_ocm_clusters.py +0 -598
- reconcile/test/test_ocm_clusters_manifest_updates.py +0 -89
- reconcile/test/test_ocm_oidc_idp.py +0 -315
- reconcile/test/test_ocm_update_recommended_version.py +0 -145
- reconcile/test/test_ocm_upgrade_scheduler.py +0 -614
- reconcile/test/test_ocm_upgrade_scheduler_org_updater.py +0 -129
- reconcile/test/test_openshift_base.py +0 -730
- reconcile/test/test_openshift_namespace_labels.py +0 -345
- reconcile/test/test_openshift_namespaces.py +0 -256
- reconcile/test/test_openshift_resource.py +0 -415
- reconcile/test/test_openshift_resources_base.py +0 -440
- reconcile/test/test_openshift_saas_deploy_change_tester.py +0 -310
- reconcile/test/test_openshift_tekton_resources.py +0 -253
- reconcile/test/test_openshift_upgrade_watcher.py +0 -146
- reconcile/test/test_prometheus_rules_tester.py +0 -151
- reconcile/test/test_prometheus_rules_tester_old.py +0 -77
- reconcile/test/test_quay_membership.py +0 -86
- reconcile/test/test_quay_mirror.py +0 -109
- reconcile/test/test_quay_mirror_org.py +0 -70
- reconcile/test/test_quay_repos.py +0 -59
- reconcile/test/test_queries.py +0 -53
- reconcile/test/test_repo_owners.py +0 -47
- reconcile/test/test_requests_sender.py +0 -139
- reconcile/test/test_saasherder.py +0 -1074
- reconcile/test/test_saasherder_allowed_secret_paths.py +0 -127
- reconcile/test/test_secret_reader.py +0 -153
- reconcile/test/test_slack_base.py +0 -185
- reconcile/test/test_slack_usergroups.py +0 -744
- reconcile/test/test_sql_query.py +0 -19
- reconcile/test/test_terraform_cloudflare_dns.py +0 -117
- reconcile/test/test_terraform_cloudflare_resources.py +0 -106
- reconcile/test/test_terraform_cloudflare_users.py +0 -749
- reconcile/test/test_terraform_resources.py +0 -257
- reconcile/test/test_terraform_tgw_attachments.py +0 -631
- reconcile/test/test_terraform_users.py +0 -57
- reconcile/test/test_terraform_vpc_peerings.py +0 -499
- reconcile/test/test_terraform_vpc_peerings_build_desired_state.py +0 -1061
- reconcile/test/test_unleash.py +0 -138
- reconcile/test/test_utils_aws_api.py +0 -240
- reconcile/test/test_utils_aws_helper.py +0 -80
- reconcile/test/test_utils_cluster_version_data.py +0 -177
- reconcile/test/test_utils_data_structures.py +0 -13
- reconcile/test/test_utils_disabled_integrations.py +0 -86
- reconcile/test/test_utils_expiration.py +0 -109
- reconcile/test/test_utils_external_resource_spec.py +0 -383
- reconcile/test/test_utils_external_resources.py +0 -247
- reconcile/test/test_utils_github_api.py +0 -73
- reconcile/test/test_utils_gitlab_api.py +0 -20
- reconcile/test/test_utils_gpg.py +0 -69
- reconcile/test/test_utils_gql.py +0 -81
- reconcile/test/test_utils_helm.py +0 -306
- reconcile/test/test_utils_helpers.py +0 -55
- reconcile/test/test_utils_imap_client.py +0 -65
- reconcile/test/test_utils_jjb_client.py +0 -52
- reconcile/test/test_utils_jsonpath.py +0 -286
- reconcile/test/test_utils_ldap_client.py +0 -51
- reconcile/test/test_utils_mr.py +0 -226
- reconcile/test/test_utils_mr_clusters_updates.py +0 -77
- reconcile/test/test_utils_oc.py +0 -984
- reconcile/test/test_utils_ocm.py +0 -110
- reconcile/test/test_utils_pagerduty_api.py +0 -251
- reconcile/test/test_utils_parse_dhms_duration.py +0 -34
- reconcile/test/test_utils_password_validator.py +0 -155
- reconcile/test/test_utils_quay_api.py +0 -86
- reconcile/test/test_utils_semver_helper.py +0 -19
- reconcile/test/test_utils_sharding.py +0 -56
- reconcile/test/test_utils_slack_api.py +0 -439
- reconcile/test/test_utils_smtp_client.py +0 -73
- reconcile/test/test_utils_state.py +0 -256
- reconcile/test/test_utils_terraform.py +0 -13
- reconcile/test/test_utils_terraform_client.py +0 -585
- reconcile/test/test_utils_terraform_config_client.py +0 -219
- reconcile/test/test_utils_terrascript_aws_client.py +0 -277
- reconcile/test/test_utils_terrascript_cloudflare_client.py +0 -597
- reconcile/test/test_utils_terrascript_cloudflare_resources.py +0 -26
- reconcile/test/test_vault_replication.py +0 -515
- reconcile/test/test_vault_utils.py +0 -47
- reconcile/test/test_version_bump.py +0 -18
- reconcile/test/test_vpc_peerings_validator.py +0 -103
- reconcile/test/test_wrong_region.py +0 -78
- reconcile/typed_queries/glitchtip_settings.py +0 -18
- reconcile/typed_queries/ocp_release_mirror.py +0 -11
- reconcile/unleash_watcher.py +0 -120
- reconcile/utils/git_secrets.py +0 -63
- reconcile/utils/mr/auto_promoter.py +0 -218
- reconcile/utils/sentry_client.py +0 -383
- release/test_version.py +0 -50
- release/version.py +0 -100
- tools/test/test_qontract_cli.py +0 -60
- tools/test/test_sre_checkpoints.py +0 -79
- /e2e_tests/__init__.py → /reconcile/aus/upgrades.py +0 -0
- /reconcile/{gql_definitions/ocp_release_mirror → aws_account_manager}/__init__.py +0 -0
- /reconcile/{test → aws_ami_cleanup}/__init__.py +0 -0
- /reconcile/{test/saas_auto_promotions_manager → aws_cloudwatch_log_retention}/__init__.py +0 -0
- /reconcile/{test/saas_auto_promotions_manager/merge_request_manager → aws_saml_idp}/__init__.py +0 -0
- /reconcile/{test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager → aws_saml_roles}/__init__.py +0 -0
- /reconcile/{test/saas_auto_promotions_manager/merge_request_manager/renderer → aws_version_sync}/__init__.py +0 -0
- /reconcile/{test/saas_auto_promotions_manager/subscriber → aws_version_sync/merge_request_manager}/__init__.py +0 -0
- /reconcile/{test/saas_auto_promotions_manager/utils → cluster_auth_rhidp}/__init__.py +0 -0
- /reconcile/{test/saas_auto_promotions_manager/utils/saas_files_inventory → dynatrace_token_provider}/__init__.py +0 -0
- {release → reconcile/endpoints_discovery}/__init__.py +0 -0
- {tools/test → reconcile/external_resources}/__init__.py +0 -0
@@ -0,0 +1,99 @@
|
|
1
|
+
import logging
|
2
|
+
from abc import abstractmethod
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from typing import Any, Generic, TypeVar
|
5
|
+
|
6
|
+
from gitlab.v4.objects import ProjectMergeRequest
|
7
|
+
from pydantic import BaseModel
|
8
|
+
|
9
|
+
from reconcile.utils.merge_request_manager.parser import (
|
10
|
+
Parser,
|
11
|
+
ParserError,
|
12
|
+
ParserVersionError,
|
13
|
+
)
|
14
|
+
from reconcile.utils.vcs import VCS
|
15
|
+
|
16
|
+
T = TypeVar("T", bound=BaseModel)
|
17
|
+
|
18
|
+
|
19
|
+
@dataclass
|
20
|
+
class OpenMergeRequest(Generic[T]):
|
21
|
+
raw: ProjectMergeRequest
|
22
|
+
mr_info: T
|
23
|
+
|
24
|
+
|
25
|
+
class MergeRequestManagerBase(Generic[T]):
|
26
|
+
""" """
|
27
|
+
|
28
|
+
def __init__(self, vcs: VCS, parser: Parser, mr_label: str):
|
29
|
+
self._vcs = vcs
|
30
|
+
self._parser = parser
|
31
|
+
self._mr_label = mr_label
|
32
|
+
self._open_mrs: list[OpenMergeRequest] = []
|
33
|
+
self._open_mrs_with_problems: list[OpenMergeRequest] = []
|
34
|
+
self._housekeeping_ran = False
|
35
|
+
|
36
|
+
@abstractmethod
|
37
|
+
def create_merge_request(self, data: Any) -> None:
|
38
|
+
pass
|
39
|
+
|
40
|
+
def _merge_request_already_exists(
|
41
|
+
self,
|
42
|
+
expected_data: dict[str, Any],
|
43
|
+
) -> OpenMergeRequest | None:
|
44
|
+
for mr in self._open_mrs:
|
45
|
+
mr_info_dict = mr.mr_info.dict()
|
46
|
+
if all(mr_info_dict.get(k) == expected_data.get(k) for k in expected_data):
|
47
|
+
return mr
|
48
|
+
|
49
|
+
return None
|
50
|
+
|
51
|
+
def _fetch_managed_open_merge_requests(self) -> list[ProjectMergeRequest]:
|
52
|
+
all_open_mrs = self._vcs.get_open_app_interface_merge_requests()
|
53
|
+
return [mr for mr in all_open_mrs if self._mr_label in mr.labels]
|
54
|
+
|
55
|
+
def housekeeping(self) -> None:
|
56
|
+
"""
|
57
|
+
Close bad MRs:
|
58
|
+
- bad description format
|
59
|
+
- wrong version
|
60
|
+
- merge conflict
|
61
|
+
|
62
|
+
--> if we update the template output, we automatically close
|
63
|
+
old open MRs and replace them with new ones.
|
64
|
+
"""
|
65
|
+
for mr in self._fetch_managed_open_merge_requests():
|
66
|
+
attrs = mr.attributes
|
67
|
+
desc = str(attrs.get("description") or "")
|
68
|
+
has_conflicts = attrs.get("has_conflicts", False)
|
69
|
+
if has_conflicts:
|
70
|
+
logging.info(
|
71
|
+
"Merge-conflict detected. Closing %s",
|
72
|
+
mr.attributes.get("web_url", "NO_WEBURL"),
|
73
|
+
)
|
74
|
+
self._vcs.close_app_interface_mr(
|
75
|
+
mr, "Closing this MR because of a merge-conflict."
|
76
|
+
)
|
77
|
+
continue
|
78
|
+
try:
|
79
|
+
mr_info = self._parser.parse(description=desc)
|
80
|
+
except ParserVersionError:
|
81
|
+
logging.info(
|
82
|
+
"Old MR version detected! Closing %s",
|
83
|
+
mr.attributes.get("web_url", "NO_WEBURL"),
|
84
|
+
)
|
85
|
+
self._vcs.close_app_interface_mr(
|
86
|
+
mr, "Closing this MR because it has an outdated integration version"
|
87
|
+
)
|
88
|
+
continue
|
89
|
+
except ParserError:
|
90
|
+
logging.info(
|
91
|
+
"Bad MR description format. Closing %s",
|
92
|
+
mr.attributes.get("web_url", "NO_WEBURL"),
|
93
|
+
)
|
94
|
+
self._vcs.close_app_interface_mr(
|
95
|
+
mr, "Closing this MR because of bad description format."
|
96
|
+
)
|
97
|
+
continue
|
98
|
+
self._open_mrs.append(OpenMergeRequest(raw=mr, mr_info=mr_info))
|
99
|
+
self._housekeeping_ran = True
|
@@ -0,0 +1,67 @@
|
|
1
|
+
import re
|
2
|
+
from typing import Generic, TypeVar
|
3
|
+
|
4
|
+
from pydantic import BaseModel
|
5
|
+
|
6
|
+
from reconcile.utils.models import data_default_none
|
7
|
+
|
8
|
+
|
9
|
+
class ParserError(Exception):
|
10
|
+
"""Raised when some information cannot be found."""
|
11
|
+
|
12
|
+
|
13
|
+
class ParserVersionError(Exception):
|
14
|
+
"""Raised when the version is outdated."""
|
15
|
+
|
16
|
+
|
17
|
+
T = TypeVar("T", bound=BaseModel)
|
18
|
+
|
19
|
+
|
20
|
+
class Parser(Generic[T]):
|
21
|
+
"""This class is only concerned with parsing an MR description rendered by the Renderer."""
|
22
|
+
|
23
|
+
def __init__(
|
24
|
+
self,
|
25
|
+
klass: type[T],
|
26
|
+
compiled_regexes: dict[str, re.Pattern],
|
27
|
+
version_ref: str,
|
28
|
+
expected_version: str,
|
29
|
+
data_separator: str,
|
30
|
+
):
|
31
|
+
self.klass = klass
|
32
|
+
self.compiled_regexes = compiled_regexes
|
33
|
+
self.expected_version = expected_version
|
34
|
+
self.version_ref = version_ref
|
35
|
+
self.data_separator = data_separator
|
36
|
+
|
37
|
+
@staticmethod
|
38
|
+
def _find_by_regex(pattern: re.Pattern, content: str) -> str:
|
39
|
+
if matches := pattern.search(content):
|
40
|
+
groups = matches.groups()
|
41
|
+
if len(groups) == 1:
|
42
|
+
return groups[0]
|
43
|
+
|
44
|
+
raise ParserError(f"Could not find {pattern} in MR description")
|
45
|
+
|
46
|
+
def _find_by_name(self, name: str, content: str) -> str:
|
47
|
+
return self._find_by_regex(self.compiled_regexes[name], content)
|
48
|
+
|
49
|
+
def _data_from_description(self, description: str) -> dict[str, str]:
|
50
|
+
return {
|
51
|
+
k: self._find_by_name(k, description)
|
52
|
+
for k, v in self.compiled_regexes.items()
|
53
|
+
}
|
54
|
+
|
55
|
+
def parse(self, description: str) -> T:
|
56
|
+
"""Parse the description of an MR"""
|
57
|
+
parts = description.split(self.data_separator)
|
58
|
+
if not len(parts) == 2:
|
59
|
+
raise ParserError("Could not find data separator in MR description")
|
60
|
+
|
61
|
+
if self.expected_version != self._find_by_name(self.version_ref, parts[1]):
|
62
|
+
raise ParserVersionError("Version is outdated")
|
63
|
+
return self.klass(
|
64
|
+
**data_default_none(
|
65
|
+
self.klass, self._data_from_description(parts[1]), use_defaults=False
|
66
|
+
)
|
67
|
+
)
|
reconcile/utils/metrics.py
CHANGED
@@ -1,8 +1,36 @@
|
|
1
|
-
|
1
|
+
import copy
|
2
|
+
import re
|
3
|
+
import threading
|
4
|
+
from abc import ABC
|
5
|
+
from collections import defaultdict
|
6
|
+
from collections.abc import (
|
7
|
+
Generator,
|
8
|
+
Hashable,
|
9
|
+
Iterable,
|
10
|
+
Sequence,
|
11
|
+
)
|
12
|
+
from types import TracebackType
|
13
|
+
from typing import (
|
14
|
+
Any,
|
15
|
+
TypeVar,
|
16
|
+
)
|
17
|
+
|
18
|
+
from prometheus_client.core import (
|
19
|
+
REGISTRY,
|
2
20
|
Counter,
|
21
|
+
CounterMetricFamily,
|
3
22
|
Gauge,
|
23
|
+
GaugeMetricFamily,
|
4
24
|
Histogram,
|
25
|
+
Metric,
|
26
|
+
)
|
27
|
+
from prometheus_client.registry import (
|
28
|
+
Collector,
|
29
|
+
CollectorRegistry,
|
5
30
|
)
|
31
|
+
from pydantic import BaseModel
|
32
|
+
|
33
|
+
pushgateway_registry = CollectorRegistry()
|
6
34
|
|
7
35
|
run_time = Gauge(
|
8
36
|
name="qontract_reconcile_last_run_seconds",
|
@@ -10,12 +38,26 @@ run_time = Gauge(
|
|
10
38
|
labelnames=["integration", "shards", "shard_id"],
|
11
39
|
)
|
12
40
|
|
41
|
+
pushgateway_run_time = Gauge(
|
42
|
+
name="qontract_reconcile_last_run_seconds",
|
43
|
+
documentation="Last run duration in seconds",
|
44
|
+
labelnames=["integration", "shards", "shard_id"],
|
45
|
+
registry=pushgateway_registry,
|
46
|
+
)
|
47
|
+
|
13
48
|
run_status = Gauge(
|
14
49
|
name="qontract_reconcile_last_run_status",
|
15
50
|
documentation="Last run status",
|
16
51
|
labelnames=["integration", "shards", "shard_id"],
|
17
52
|
)
|
18
53
|
|
54
|
+
pushgateway_run_status = Gauge(
|
55
|
+
name="qontract_reconcile_last_run_status",
|
56
|
+
documentation="Last run status",
|
57
|
+
labelnames=["integration", "shards", "shard_id"],
|
58
|
+
registry=pushgateway_registry,
|
59
|
+
)
|
60
|
+
|
19
61
|
execution_counter = Counter(
|
20
62
|
name="qontract_reconcile_execution_counter",
|
21
63
|
documentation="Counts started integration executions",
|
@@ -64,3 +106,471 @@ gitlab_request = Counter(
|
|
64
106
|
documentation="Number of calls made to Gitlab API",
|
65
107
|
labelnames=["integration"],
|
66
108
|
)
|
109
|
+
|
110
|
+
ocm_request = Counter(
|
111
|
+
name="qontract_reconcile_ocm_request_total",
|
112
|
+
documentation="Number of calls made to OCM API",
|
113
|
+
labelnames=["verb", "client_id"],
|
114
|
+
)
|
115
|
+
|
116
|
+
slack_request = Counter(
|
117
|
+
name="qontract_reconcile_slack_request_total",
|
118
|
+
documentation="Number of calls made to Slack API",
|
119
|
+
labelnames=["resource", "verb"],
|
120
|
+
)
|
121
|
+
|
122
|
+
|
123
|
+
#
|
124
|
+
# Class based metrics
|
125
|
+
#
|
126
|
+
|
127
|
+
|
128
|
+
class BaseMetric(ABC, BaseModel):
|
129
|
+
@classmethod
|
130
|
+
def name(cls) -> str:
|
131
|
+
"""
|
132
|
+
Returns the prometheus metric name. Defaults to a snake case version of the
|
133
|
+
class name. Removes the suffix `_metric` is present. Subclasses can override this.
|
134
|
+
"""
|
135
|
+
metric_name = re.sub(r"(?<!^)(?=[A-Z])", "_", cls.__name__).lower()
|
136
|
+
if metric_name.endswith("_metric"):
|
137
|
+
metric_name = metric_name[:-7]
|
138
|
+
return metric_name
|
139
|
+
|
140
|
+
|
141
|
+
class GaugeMetric(BaseMetric):
|
142
|
+
"""
|
143
|
+
Base class for gauge metrics.
|
144
|
+
"""
|
145
|
+
|
146
|
+
@classmethod
|
147
|
+
def metric_family(cls) -> GaugeMetricFamily:
|
148
|
+
labels = [f.alias for f in cls.__fields__.values()]
|
149
|
+
return GaugeMetricFamily(cls.name(), cls.__doc__ or "", labels=labels)
|
150
|
+
|
151
|
+
@classmethod
|
152
|
+
def name(cls) -> str:
|
153
|
+
metric_name = super().name()
|
154
|
+
if metric_name.endswith("_gauge"):
|
155
|
+
metric_name = metric_name[:-6]
|
156
|
+
return metric_name
|
157
|
+
|
158
|
+
|
159
|
+
class InfoMetric(GaugeMetric):
|
160
|
+
"""
|
161
|
+
Base class for info metrics.
|
162
|
+
"""
|
163
|
+
|
164
|
+
|
165
|
+
class CounterMetric(BaseMetric):
|
166
|
+
"""
|
167
|
+
Base class for counter metrics
|
168
|
+
"""
|
169
|
+
|
170
|
+
@classmethod
|
171
|
+
def metric_family(cls) -> CounterMetricFamily:
|
172
|
+
labels = [f.alias for f in cls.__fields__.values()]
|
173
|
+
return CounterMetricFamily(cls.name(), cls.__doc__ or "", labels=labels)
|
174
|
+
|
175
|
+
@classmethod
|
176
|
+
def name(cls) -> str:
|
177
|
+
metric_name = super().name()
|
178
|
+
if metric_name.endswith("_counter"):
|
179
|
+
metric_name = metric_name[:-8]
|
180
|
+
return metric_name
|
181
|
+
|
182
|
+
|
183
|
+
class MetricsContainer:
|
184
|
+
"""
|
185
|
+
A container for metrics, supporting transactional behaviour and scoped metrics.
|
186
|
+
"""
|
187
|
+
|
188
|
+
def __init__(
|
189
|
+
self,
|
190
|
+
) -> None:
|
191
|
+
self._gauges: dict[type[GaugeMetric], dict[Sequence[str], float]] = defaultdict(
|
192
|
+
dict
|
193
|
+
)
|
194
|
+
self._counters: dict[type[CounterMetric], dict[Sequence[str], float]] = (
|
195
|
+
defaultdict(dict)
|
196
|
+
)
|
197
|
+
|
198
|
+
self._scopes: dict[Hashable, MetricsContainer] = {}
|
199
|
+
|
200
|
+
def set_gauge(self, metric: GaugeMetric, value: float) -> None:
|
201
|
+
"""
|
202
|
+
Sets the value of the given gauge metric to the given value.
|
203
|
+
"""
|
204
|
+
label_values = tuple(metric.dict(by_alias=True).values())
|
205
|
+
self._gauges[metric.__class__][label_values] = value
|
206
|
+
|
207
|
+
def set_info(self, metric: InfoMetric) -> None:
|
208
|
+
"""
|
209
|
+
Adds an info metric. Info metrics are gauges with a value of 1,
|
210
|
+
so they can be used to join with other metrics by multiplying.
|
211
|
+
"""
|
212
|
+
self.set_gauge(metric, 1.0)
|
213
|
+
|
214
|
+
def inc_counter(self, counter: CounterMetric, by: int = 1) -> None:
|
215
|
+
"""
|
216
|
+
Increases the value of the given counter by the given amount.
|
217
|
+
"""
|
218
|
+
# all label values need to be strings, so lets convert them
|
219
|
+
label_values = tuple(str(v) for v in counter.dict(by_alias=True).values())
|
220
|
+
current_value = self._counters[counter.__class__].get(label_values) or 0
|
221
|
+
self._counters[counter.__class__][label_values] = current_value + by
|
222
|
+
|
223
|
+
def _aggregate_scopes(self) -> "MetricsContainer":
|
224
|
+
containers = [self]
|
225
|
+
for sub in self._scopes.values():
|
226
|
+
containers.append(sub._aggregate_scopes())
|
227
|
+
return join_metric_containers(containers)
|
228
|
+
|
229
|
+
def collect(self) -> Generator[Metric, None, None]:
|
230
|
+
"""
|
231
|
+
Collects all metrics from this container and all its scopes.
|
232
|
+
"""
|
233
|
+
return self._aggregate_scopes()._collect_local()
|
234
|
+
|
235
|
+
T = TypeVar("T", bound=BaseMetric)
|
236
|
+
|
237
|
+
def get_metric_value(self, metric_class: type[T], **kwargs: Any) -> float | None:
|
238
|
+
"""
|
239
|
+
Finds a unique match for the metrics class and labels, and returns its value.
|
240
|
+
If more than one match is found, a ValueError is raised.
|
241
|
+
If no match is found, None is returned.
|
242
|
+
"""
|
243
|
+
found = self.get_metrics(metric_class, **kwargs)
|
244
|
+
if len(found) == 1:
|
245
|
+
return found[0][1]
|
246
|
+
if len(found) > 1:
|
247
|
+
raise ValueError(
|
248
|
+
f"More than one metric found for {metric_class} and labels {kwargs}"
|
249
|
+
)
|
250
|
+
return None
|
251
|
+
|
252
|
+
def get_metrics(
|
253
|
+
self, metric_class: type[T], **kwargs: Any
|
254
|
+
) -> list[tuple[T, float]]:
|
255
|
+
"""
|
256
|
+
Returns all metrics of the given class from this container and all its scopes,
|
257
|
+
that match (or partially match) the given labels.
|
258
|
+
"""
|
259
|
+
mc = self._aggregate_scopes()
|
260
|
+
metrics = {}
|
261
|
+
if issubclass(metric_class, CounterMetric):
|
262
|
+
metrics = mc._counters.get(metric_class, {})
|
263
|
+
elif issubclass(metric_class, GaugeMetric):
|
264
|
+
metrics = mc._gauges.get(metric_class, {})
|
265
|
+
else:
|
266
|
+
raise ValueError(f"Unknown metric class {metric_class}")
|
267
|
+
|
268
|
+
def match_labels_predicate(metric: BaseMetric, **match_labels: Any) -> bool:
|
269
|
+
for key, value in match_labels.items():
|
270
|
+
if getattr(metric, key) != value:
|
271
|
+
return False
|
272
|
+
return True
|
273
|
+
|
274
|
+
unfiltered_results = [
|
275
|
+
(
|
276
|
+
metric_class(**{
|
277
|
+
key: labels[i]
|
278
|
+
for i, key in enumerate(metric_class.__fields__.keys())
|
279
|
+
}),
|
280
|
+
value,
|
281
|
+
)
|
282
|
+
for labels, value in metrics.items()
|
283
|
+
]
|
284
|
+
|
285
|
+
return [
|
286
|
+
(metric, value)
|
287
|
+
for metric, value in unfiltered_results
|
288
|
+
if match_labels_predicate(metric, **kwargs)
|
289
|
+
]
|
290
|
+
|
291
|
+
def _collect_local(self) -> Generator[Metric, None, None]:
|
292
|
+
"""
|
293
|
+
Collects only the metrics present in this container, ignoring
|
294
|
+
any scopes.
|
295
|
+
"""
|
296
|
+
# collect all gauges
|
297
|
+
for gauge_metric_class, values in self._gauges.items():
|
298
|
+
gauge_metric_family = gauge_metric_class.metric_family()
|
299
|
+
for labels, value in values.items():
|
300
|
+
gauge_metric_family.add_metric(
|
301
|
+
self._convert_labels_to_strings(labels), value
|
302
|
+
)
|
303
|
+
yield gauge_metric_family
|
304
|
+
|
305
|
+
# collect all counters
|
306
|
+
for counter_metric_class, values in self._counters.items():
|
307
|
+
counter_metric_family = counter_metric_class.metric_family()
|
308
|
+
for labels, value in values.items():
|
309
|
+
counter_metric_family.add_metric(
|
310
|
+
self._convert_labels_to_strings(labels), value
|
311
|
+
)
|
312
|
+
yield counter_metric_family
|
313
|
+
|
314
|
+
def _convert_labels_to_strings(self, raw_labels: Iterable[Any]) -> list[str]:
|
315
|
+
return [
|
316
|
+
str(label).lower() if isinstance(label, bool) else str(label)
|
317
|
+
for label in raw_labels
|
318
|
+
]
|
319
|
+
|
320
|
+
def clone(self, keep_gauges: bool, keep_counters: bool) -> "MetricsContainer":
|
321
|
+
"""
|
322
|
+
Clones this container.
|
323
|
+
"""
|
324
|
+
cloned_container = MetricsContainer()
|
325
|
+
if keep_gauges:
|
326
|
+
cloned_container._gauges = copy.deepcopy(self._gauges)
|
327
|
+
if keep_counters:
|
328
|
+
cloned_container._counters = copy.deepcopy(self._counters)
|
329
|
+
return cloned_container
|
330
|
+
|
331
|
+
def absorb(
|
332
|
+
self, other: "MetricsContainer", aggregate_counters: bool = True
|
333
|
+
) -> None:
|
334
|
+
"""
|
335
|
+
Absorbs the gauges and counter from the given container into this one.
|
336
|
+
"""
|
337
|
+
# bring all gauges together
|
338
|
+
for gauge_metric_class, values in other._gauges.items():
|
339
|
+
self._gauges[gauge_metric_class].update(values)
|
340
|
+
|
341
|
+
# bring all counters together, add their values up when the labels match
|
342
|
+
for counter_metric_class, values in other._counters.items():
|
343
|
+
if aggregate_counters:
|
344
|
+
for labels, counter_state in values.items():
|
345
|
+
aggregated_counter_state = (
|
346
|
+
self._counters[counter_metric_class].get(labels) or 0
|
347
|
+
)
|
348
|
+
self._counters[counter_metric_class][labels] = (
|
349
|
+
aggregated_counter_state + counter_state
|
350
|
+
)
|
351
|
+
else:
|
352
|
+
self._counters[counter_metric_class].update(values)
|
353
|
+
|
354
|
+
# bring scopes along
|
355
|
+
self._scopes.update(other._scopes)
|
356
|
+
|
357
|
+
|
358
|
+
def join_metric_containers(
|
359
|
+
metric_containers: Iterable["MetricsContainer"], aggregate_counters: bool = True
|
360
|
+
) -> "MetricsContainer":
|
361
|
+
"""
|
362
|
+
Join all given metric containers into a single one.
|
363
|
+
If gauge duplicates are found, the last one wins.
|
364
|
+
If counter duplicates are found, their values are added up.
|
365
|
+
"""
|
366
|
+
aggregated_metrics = MetricsContainer()
|
367
|
+
for mc in metric_containers:
|
368
|
+
aggregated_metrics.absorb(mc, aggregate_counters=aggregate_counters)
|
369
|
+
return aggregated_metrics
|
370
|
+
|
371
|
+
|
372
|
+
class _MetricsContext:
|
373
|
+
"""
|
374
|
+
Context manager for the metrics container. Metrics collected within the
|
375
|
+
context will be aggregated and exposed to the prometheus client when the
|
376
|
+
context exits.
|
377
|
+
|
378
|
+
See `transactional_metrics` to learn more about the `scope`and `aggregate_counters`
|
379
|
+
parameters.
|
380
|
+
"""
|
381
|
+
|
382
|
+
def __init__(
|
383
|
+
self,
|
384
|
+
scope: Hashable | None,
|
385
|
+
parent: MetricsContainer,
|
386
|
+
aggregate_counters: bool,
|
387
|
+
):
|
388
|
+
self.scope = scope
|
389
|
+
self.parent = parent
|
390
|
+
self.aggregate_counters = aggregate_counters
|
391
|
+
|
392
|
+
def __enter__(self) -> MetricsContainer:
|
393
|
+
# if the context manager is used with the scope parameter, it opens a new
|
394
|
+
# scope within the parent container. Otherwise, it opens a new container
|
395
|
+
# that will be absorbed into the parent container after exit
|
396
|
+
self.container = MetricsContainer()
|
397
|
+
if self.scope:
|
398
|
+
previous_scope_container = self.parent._scopes.get(self.scope)
|
399
|
+
if previous_scope_container:
|
400
|
+
self.container = previous_scope_container.clone(
|
401
|
+
keep_gauges=False, keep_counters=self.aggregate_counters
|
402
|
+
)
|
403
|
+
|
404
|
+
_STATE.set_current_container(self.container)
|
405
|
+
return self.container
|
406
|
+
|
407
|
+
def __exit__(
|
408
|
+
self,
|
409
|
+
exc_type: type[BaseException] | None,
|
410
|
+
exc_value: BaseException | None,
|
411
|
+
traceback: TracebackType | None,
|
412
|
+
) -> None:
|
413
|
+
if self.scope:
|
414
|
+
self.parent._scopes[self.scope] = self.container
|
415
|
+
else:
|
416
|
+
self.parent.absorb(self.container)
|
417
|
+
_STATE.set_current_container(self.parent)
|
418
|
+
|
419
|
+
|
420
|
+
def transactional_metrics(
|
421
|
+
scope: Hashable | None = None,
|
422
|
+
parent_container: MetricsContainer | None = None,
|
423
|
+
aggregate_counters: bool = True,
|
424
|
+
) -> _MetricsContext:
|
425
|
+
"""
|
426
|
+
Creates the context manager for the metrics container, providing
|
427
|
+
transactional behaviour. All metrics exposed within the context manager
|
428
|
+
will be exposed only after the context manager ends.
|
429
|
+
|
430
|
+
If a `scope` parameter is given, the metrics will be grouped by that scope
|
431
|
+
and will replace all metrics collected in the same scope in previous runs.
|
432
|
+
This can be used to forget metrics from previous runs and only expose the
|
433
|
+
ones collected in the current run. For counters this is only true if
|
434
|
+
`aggregate_counters` is set to False, which makes mostly sense if a counter
|
435
|
+
value is provided by an external source and we want to expose the latest
|
436
|
+
value only, so being an ever increasing gauge but with prometheus counter
|
437
|
+
semantics. Otherwise counter metrics will be aggregated across transactions.
|
438
|
+
|
439
|
+
If a `parent_container` is provided, the metrics will be collected in that
|
440
|
+
container (and its scopes). If no `parent_container` is provided, the container
|
441
|
+
of another currently running transaction will be used, if any. Otherwise the
|
442
|
+
global container will be used.
|
443
|
+
"""
|
444
|
+
return _MetricsContext(
|
445
|
+
scope=scope,
|
446
|
+
parent=(parent_container or _STATE.get_current_container()),
|
447
|
+
aggregate_counters=aggregate_counters,
|
448
|
+
)
|
449
|
+
|
450
|
+
|
451
|
+
class MetricCollector(Collector):
|
452
|
+
"""
|
453
|
+
Acts as the bridge between the metrics collected in the MetricsContainers
|
454
|
+
and the prometheus client. The `collect` function is called by the
|
455
|
+
prometheus client during a scrape.
|
456
|
+
"""
|
457
|
+
|
458
|
+
def __init__(self, metric_container: MetricsContainer) -> None:
|
459
|
+
self.metric_container = metric_container
|
460
|
+
super().__init__()
|
461
|
+
|
462
|
+
def collect(self) -> Generator[Metric, None, None]:
|
463
|
+
return self.metric_container.collect()
|
464
|
+
|
465
|
+
|
466
|
+
# define the top level metrics container and register it with the prometheus
|
467
|
+
_GLOBAL_METRICS_CONTAINER = MetricsContainer()
|
468
|
+
REGISTRY.register(MetricCollector(_GLOBAL_METRICS_CONTAINER))
|
469
|
+
|
470
|
+
|
471
|
+
class _CurrentContainerState(threading.local):
|
472
|
+
"""
|
473
|
+
Thread-local state for the current metrics container.
|
474
|
+
"""
|
475
|
+
|
476
|
+
def __init__(self) -> None:
|
477
|
+
super().__init__()
|
478
|
+
self.container: MetricsContainer = _GLOBAL_METRICS_CONTAINER
|
479
|
+
|
480
|
+
def get_current_container(self) -> MetricsContainer:
|
481
|
+
return self.container
|
482
|
+
|
483
|
+
def set_current_container(self, container: MetricsContainer) -> None:
|
484
|
+
self.container = container
|
485
|
+
|
486
|
+
|
487
|
+
_STATE = _CurrentContainerState()
|
488
|
+
|
489
|
+
|
490
|
+
def set_gauge(metric: GaugeMetric, value: float) -> None:
|
491
|
+
"""
|
492
|
+
Expose a gauge metric into the current metrics container.
|
493
|
+
Honors running transactions.
|
494
|
+
"""
|
495
|
+
_STATE.get_current_container().set_gauge(metric, value)
|
496
|
+
|
497
|
+
|
498
|
+
def set_info(metric: InfoMetric) -> None:
|
499
|
+
"""
|
500
|
+
Expose an info metric into the current metrics container.
|
501
|
+
Honors running transactions.
|
502
|
+
"""
|
503
|
+
set_gauge(metric, 1.0)
|
504
|
+
|
505
|
+
|
506
|
+
def inc_counter(counter: CounterMetric, by: int = 1) -> None:
|
507
|
+
"""
|
508
|
+
Increases a counter in the current metrics container.
|
509
|
+
Honors running transactions.
|
510
|
+
"""
|
511
|
+
_STATE.get_current_container().inc_counter(counter, by)
|
512
|
+
|
513
|
+
|
514
|
+
#
|
515
|
+
# MetricSet
|
516
|
+
#
|
517
|
+
|
518
|
+
|
519
|
+
ERMS = TypeVar("ERMS", bound="ErrorRateMetricSet")
|
520
|
+
|
521
|
+
|
522
|
+
class ErrorRateMetricSet:
|
523
|
+
"""
|
524
|
+
A context manager that exposes a counter metric and an error counter metric
|
525
|
+
for a code block. The code block within the contextmanager is considered
|
526
|
+
failed if an exception is raised of if the `fail` method is called.
|
527
|
+
"""
|
528
|
+
|
529
|
+
def __init__(
|
530
|
+
self: ERMS, counter: CounterMetric, error_counter: CounterMetric
|
531
|
+
) -> None:
|
532
|
+
self._counter = counter
|
533
|
+
self._error_counter = error_counter
|
534
|
+
self._errors: list[BaseException] = []
|
535
|
+
|
536
|
+
def __enter__(self: ERMS) -> ERMS:
|
537
|
+
inc_counter(self._counter)
|
538
|
+
return self
|
539
|
+
|
540
|
+
def fail(self: ERMS, error: BaseException) -> None:
|
541
|
+
"""
|
542
|
+
Mark the context as failed and record it as an event
|
543
|
+
that increases the error counter.
|
544
|
+
"""
|
545
|
+
self._errors.append(error)
|
546
|
+
|
547
|
+
@property
|
548
|
+
def failed(self: ERMS) -> bool:
|
549
|
+
"""
|
550
|
+
Returns True if the context manager was marked as failed.
|
551
|
+
"""
|
552
|
+
return bool(self._errors)
|
553
|
+
|
554
|
+
@property
|
555
|
+
def errors(self: ERMS) -> list[BaseException]:
|
556
|
+
"""
|
557
|
+
Return the list of errors that caused the context manager to fail.
|
558
|
+
"""
|
559
|
+
return self._errors
|
560
|
+
|
561
|
+
def __exit__(
|
562
|
+
self: ERMS,
|
563
|
+
exc_type: type[BaseException] | None,
|
564
|
+
exc_value: BaseException | None,
|
565
|
+
traceback: TracebackType | None,
|
566
|
+
) -> None:
|
567
|
+
if exc_value:
|
568
|
+
self.fail(exc_value)
|
569
|
+
inc_counter(self._error_counter, by=(1 if self._errors else 0))
|
570
|
+
|
571
|
+
|
572
|
+
def normalize_integration_name(integration: str) -> str:
|
573
|
+
"""
|
574
|
+
Normalize the integration name to be used in prometheus.
|
575
|
+
"""
|
576
|
+
return integration.replace("_", "-")
|