qontract-reconcile 0.10.2.dev334__py3-none-any.whl → 0.10.2.dev439__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of qontract-reconcile might be problematic. Click here for more details.
- {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/METADATA +13 -12
- {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/RECORD +366 -361
- reconcile/acs_rbac.py +2 -2
- reconcile/aus/advanced_upgrade_service.py +18 -12
- reconcile/aus/base.py +134 -32
- reconcile/aus/cluster_version_data.py +15 -5
- reconcile/aus/models.py +3 -1
- reconcile/aus/ocm_addons_upgrade_scheduler_org.py +1 -0
- reconcile/aus/ocm_upgrade_scheduler.py +8 -1
- reconcile/aus/ocm_upgrade_scheduler_org.py +20 -5
- reconcile/aus/version_gates/sts_version_gate_handler.py +54 -1
- reconcile/automated_actions/config/integration.py +16 -4
- reconcile/aws_account_manager/integration.py +8 -8
- reconcile/aws_account_manager/reconciler.py +3 -3
- reconcile/aws_ami_cleanup/integration.py +8 -12
- reconcile/aws_ami_share.py +69 -62
- reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
- reconcile/aws_ecr_image_pull_secrets.py +3 -3
- reconcile/aws_iam_keys.py +1 -0
- reconcile/aws_saml_idp/integration.py +12 -4
- reconcile/aws_saml_roles/integration.py +32 -25
- reconcile/aws_version_sync/integration.py +6 -12
- reconcile/change_owners/bundle.py +3 -3
- reconcile/change_owners/change_log_tracking.py +3 -2
- reconcile/change_owners/change_owners.py +1 -1
- reconcile/change_owners/diff.py +2 -4
- reconcile/checkpoint.py +11 -3
- reconcile/cli.py +111 -18
- 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 +9 -11
- reconcile/external_resources/factories.py +5 -12
- reconcile/external_resources/integration.py +1 -1
- reconcile/external_resources/manager.py +8 -5
- reconcile/external_resources/meta.py +0 -1
- reconcile/external_resources/metrics.py +1 -1
- reconcile/external_resources/model.py +20 -20
- reconcile/external_resources/reconciler.py +7 -4
- reconcile/external_resources/secrets_sync.py +8 -11
- reconcile/external_resources/state.py +26 -16
- reconcile/fleet_labeler/integration.py +1 -1
- reconcile/gabi_authorized_users.py +8 -5
- reconcile/gcp_image_mirror.py +2 -2
- reconcile/github_org.py +1 -1
- reconcile/github_owners.py +4 -0
- reconcile/gitlab_housekeeping.py +13 -15
- reconcile/gitlab_members.py +6 -12
- reconcile/gitlab_mr_sqs_consumer.py +2 -2
- reconcile/gitlab_owners.py +15 -11
- reconcile/gitlab_permissions.py +8 -12
- reconcile/glitchtip_project_alerts/integration.py +3 -1
- reconcile/gql_definitions/acs/acs_instances.py +10 -10
- reconcile/gql_definitions/acs/acs_policies.py +5 -5
- reconcile/gql_definitions/acs/acs_rbac.py +6 -6
- reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +32 -32
- reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +26 -26
- reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +6 -7
- reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
- reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
- reconcile/gql_definitions/automated_actions/instance.py +51 -12
- reconcile/gql_definitions/aws_account_manager/aws_accounts.py +11 -11
- reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +20 -10
- reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +28 -68
- reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +20 -10
- reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +20 -10
- reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
- reconcile/gql_definitions/aws_version_sync/clusters.py +10 -10
- reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
- reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
- reconcile/gql_definitions/change_owners/queries/self_service_roles.py +9 -9
- reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +18 -18
- reconcile/gql_definitions/common/alerting_services_settings.py +9 -9
- reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
- reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
- reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_roles.py +120 -0
- reconcile/gql_definitions/common/app_interface_state_settings.py +10 -10
- reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
- reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
- reconcile/gql_definitions/common/apps.py +5 -5
- reconcile/gql_definitions/common/aws_vpc_requests.py +22 -9
- reconcile/gql_definitions/common/aws_vpcs.py +11 -11
- reconcile/gql_definitions/common/clusters.py +37 -35
- reconcile/gql_definitions/common/clusters_minimal.py +14 -14
- reconcile/gql_definitions/common/clusters_with_dms.py +6 -6
- reconcile/gql_definitions/common/clusters_with_peering.py +29 -30
- reconcile/gql_definitions/common/github_orgs.py +10 -10
- reconcile/gql_definitions/common/jira_settings.py +10 -10
- reconcile/gql_definitions/common/jiralert_settings.py +5 -5
- reconcile/gql_definitions/common/ldap_settings.py +5 -5
- reconcile/gql_definitions/common/namespaces.py +42 -44
- reconcile/gql_definitions/common/namespaces_minimal.py +15 -13
- reconcile/gql_definitions/common/ocm_env_telemeter.py +12 -12
- reconcile/gql_definitions/common/ocm_environments.py +19 -19
- reconcile/gql_definitions/common/pagerduty_instances.py +9 -9
- reconcile/gql_definitions/common/pgp_reencryption_settings.py +6 -6
- reconcile/gql_definitions/common/pipeline_providers.py +29 -29
- reconcile/gql_definitions/common/quay_instances.py +5 -5
- reconcile/gql_definitions/common/quay_orgs.py +5 -5
- reconcile/gql_definitions/common/reserved_networks.py +5 -5
- reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
- reconcile/gql_definitions/common/saas_files.py +44 -44
- reconcile/gql_definitions/common/saas_target_namespaces.py +10 -10
- reconcile/gql_definitions/common/saasherder_settings.py +5 -5
- reconcile/gql_definitions/common/slack_workspaces.py +5 -5
- reconcile/gql_definitions/common/smtp_client_settings.py +19 -19
- reconcile/gql_definitions/common/state_aws_account.py +7 -8
- reconcile/gql_definitions/common/users.py +5 -5
- reconcile/gql_definitions/common/users_with_paths.py +5 -5
- reconcile/gql_definitions/cost_report/app_names.py +5 -5
- reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
- reconcile/gql_definitions/cost_report/settings.py +9 -9
- reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +43 -43
- reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +10 -10
- reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
- reconcile/gql_definitions/email_sender/apps.py +5 -5
- reconcile/gql_definitions/email_sender/emails.py +8 -8
- reconcile/gql_definitions/email_sender/users.py +6 -6
- reconcile/gql_definitions/endpoints_discovery/apps.py +10 -10
- reconcile/gql_definitions/external_resources/aws_accounts.py +9 -9
- reconcile/gql_definitions/external_resources/external_resources_modules.py +23 -23
- reconcile/gql_definitions/external_resources/external_resources_namespaces.py +494 -410
- reconcile/gql_definitions/external_resources/external_resources_settings.py +28 -26
- reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
- reconcile/gql_definitions/fleet_labeler/fleet_labels.py +40 -40
- reconcile/gql_definitions/fragments/aus_organization.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
- reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
- reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
- reconcile/gql_definitions/fragments/{aws_vpc_request_subnet.py → aws_organization.py} +12 -8
- reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
- reconcile/gql_definitions/fragments/aws_vpc_request.py +12 -5
- reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
- reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
- reconcile/gql_definitions/fragments/disable.py +5 -5
- reconcile/gql_definitions/fragments/email_service.py +5 -5
- reconcile/gql_definitions/fragments/email_user.py +5 -5
- reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
- reconcile/gql_definitions/fragments/membership_source.py +5 -5
- reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
- reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
- reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
- reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
- reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
- reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
- reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
- reconcile/gql_definitions/fragments/resource_values.py +5 -5
- reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
- reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
- reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
- reconcile/gql_definitions/fragments/terraform_state.py +5 -5
- reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
- reconcile/gql_definitions/fragments/user.py +5 -5
- reconcile/gql_definitions/fragments/vault_secret.py +5 -5
- reconcile/gql_definitions/gcp/gcp_docker_repos.py +9 -9
- reconcile/gql_definitions/gcp/gcp_projects.py +9 -9
- reconcile/gql_definitions/gitlab_members/gitlab_instances.py +9 -9
- reconcile/gql_definitions/gitlab_members/permissions.py +9 -9
- reconcile/gql_definitions/glitchtip/glitchtip_instance.py +9 -9
- reconcile/gql_definitions/glitchtip/glitchtip_project.py +11 -11
- reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +9 -9
- reconcile/gql_definitions/integrations/integrations.py +48 -51
- reconcile/gql_definitions/introspection.json +3207 -1683
- reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +11 -11
- reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +10 -10
- reconcile/gql_definitions/jira/jira_servers.py +5 -5
- reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +14 -10
- reconcile/gql_definitions/jumphosts/jumphosts.py +13 -13
- reconcile/gql_definitions/ldap_groups/roles.py +5 -5
- reconcile/gql_definitions/ldap_groups/settings.py +9 -9
- reconcile/gql_definitions/maintenance/maintenances.py +5 -5
- reconcile/gql_definitions/membershipsources/roles.py +5 -5
- reconcile/gql_definitions/ocm_labels/clusters.py +18 -19
- reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
- reconcile/gql_definitions/openshift_cluster_bots/clusters.py +22 -22
- reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
- reconcile/gql_definitions/openshift_groups/managed_roles.py +6 -6
- reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +10 -10
- reconcile/gql_definitions/quay_membership/quay_membership.py +6 -6
- reconcile/gql_definitions/rhcs/certs.py +33 -87
- reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +43 -0
- reconcile/gql_definitions/rhidp/organizations.py +18 -18
- reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
- reconcile/gql_definitions/service_dependencies/service_dependencies.py +8 -8
- reconcile/gql_definitions/sharding/aws_accounts.py +10 -10
- reconcile/gql_definitions/sharding/ocm_organization.py +8 -8
- reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
- reconcile/gql_definitions/skupper_network/skupper_networks.py +10 -10
- reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
- reconcile/gql_definitions/slack_usergroups/permissions.py +9 -9
- reconcile/gql_definitions/slack_usergroups/users.py +5 -5
- reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
- reconcile/gql_definitions/status_board/status_board.py +6 -7
- reconcile/gql_definitions/statuspage/statuspages.py +9 -9
- reconcile/gql_definitions/templating/template_collection.py +5 -5
- reconcile/gql_definitions/templating/templates.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +6 -6
- reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +11 -11
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +11 -11
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +20 -25
- reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +6 -6
- reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +12 -12
- reconcile/gql_definitions/terraform_init/aws_accounts.py +23 -9
- reconcile/gql_definitions/terraform_repo/terraform_repo.py +9 -9
- reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
- reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +440 -407
- reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +23 -17
- reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +9 -9
- reconcile/gql_definitions/vault_instances/vault_instances.py +61 -61
- reconcile/gql_definitions/vault_policies/vault_policies.py +11 -11
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +8 -8
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
- reconcile/integrations_manager.py +3 -3
- reconcile/jenkins_worker_fleets.py +10 -8
- reconcile/jira_permissions_validator.py +237 -122
- reconcile/ldap_groups/integration.py +1 -1
- reconcile/ocm/types.py +35 -56
- reconcile/ocm_aws_infrastructure_access.py +1 -1
- reconcile/ocm_clusters.py +4 -4
- reconcile/ocm_labels/integration.py +3 -2
- reconcile/ocm_machine_pools.py +33 -27
- reconcile/openshift_base.py +113 -5
- reconcile/openshift_cluster_bots.py +3 -2
- reconcile/openshift_namespace_labels.py +1 -1
- reconcile/openshift_namespaces.py +97 -101
- reconcile/openshift_resources_base.py +6 -2
- reconcile/openshift_rhcs_certs.py +74 -37
- reconcile/openshift_rolebindings.py +230 -130
- reconcile/openshift_saas_deploy.py +6 -7
- reconcile/openshift_saas_deploy_change_tester.py +9 -7
- reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
- reconcile/openshift_serviceaccount_tokens.py +2 -2
- reconcile/openshift_upgrade_watcher.py +4 -4
- reconcile/openshift_users.py +5 -3
- reconcile/oum/labelset.py +5 -3
- reconcile/oum/models.py +1 -4
- reconcile/prometheus_rules_tester/integration.py +3 -3
- reconcile/quay_mirror.py +1 -1
- reconcile/queries.py +131 -0
- 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 +5 -1
- reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +4 -1
- reconcile/templating/lib/merge_request_manager.py +2 -2
- reconcile/templating/lib/rendering.py +3 -3
- reconcile/templating/renderer.py +12 -13
- reconcile/terraform_aws_route53.py +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 +17 -7
- 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/typed_queries/app_interface_roles.py +10 -0
- reconcile/typed_queries/aws_account_tags.py +41 -0
- reconcile/typed_queries/cost_report/app_names.py +1 -1
- reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
- reconcile/typed_queries/saas_files.py +13 -13
- reconcile/typed_queries/status_board.py +2 -2
- reconcile/unleash_feature_toggles/integration.py +4 -2
- reconcile/utils/acs/base.py +6 -3
- reconcile/utils/acs/policies.py +2 -2
- reconcile/utils/aggregated_list.py +4 -3
- reconcile/utils/aws_api.py +51 -20
- reconcile/utils/aws_api_typed/api.py +38 -9
- reconcile/utils/aws_api_typed/cloudformation.py +149 -0
- reconcile/utils/aws_api_typed/logs.py +73 -0
- reconcile/utils/aws_api_typed/organization.py +4 -2
- reconcile/utils/binary.py +7 -12
- reconcile/utils/datetime_util.py +67 -0
- reconcile/utils/deadmanssnitch_api.py +1 -1
- reconcile/utils/differ.py +2 -3
- reconcile/utils/early_exit_cache.py +11 -12
- reconcile/utils/expiration.py +7 -3
- reconcile/utils/external_resource_spec.py +24 -1
- reconcile/utils/filtering.py +1 -1
- reconcile/utils/gitlab_api.py +7 -5
- reconcile/utils/glitchtip/client.py +6 -2
- reconcile/utils/glitchtip/models.py +25 -28
- reconcile/utils/gql.py +4 -7
- reconcile/utils/helm.py +2 -1
- 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 -8
- reconcile/utils/jira_client.py +82 -63
- reconcile/utils/jjb_client.py +28 -15
- reconcile/utils/jobcontroller/controller.py +2 -2
- reconcile/utils/jobcontroller/models.py +17 -1
- reconcile/utils/json.py +74 -0
- 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/base.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 +249 -203
- reconcile/utils/oc_filters.py +3 -3
- reconcile/utils/ocm/addons.py +0 -1
- reconcile/utils/ocm/base.py +18 -21
- reconcile/utils/ocm/cluster_groups.py +1 -1
- reconcile/utils/ocm/identity_providers.py +2 -2
- reconcile/utils/ocm/labels.py +1 -1
- reconcile/utils/ocm/products.py +9 -3
- reconcile/utils/ocm/search_filters.py +3 -6
- reconcile/utils/ocm/service_log.py +4 -6
- reconcile/utils/ocm/sre_capability_labels.py +20 -13
- reconcile/utils/openshift_resource.py +10 -5
- reconcile/utils/output.py +3 -2
- reconcile/utils/pagerduty_api.py +10 -7
- reconcile/utils/promotion_state.py +6 -11
- reconcile/utils/raw_github_api.py +1 -1
- reconcile/utils/rhcsv2_certs.py +138 -35
- 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 +25 -21
- reconcile/utils/saasherder/saasherder.py +55 -31
- reconcile/utils/slack_api.py +26 -4
- reconcile/utils/sloth.py +224 -0
- reconcile/utils/sqs_gateway.py +2 -1
- reconcile/utils/state.py +2 -1
- reconcile/utils/structs.py +1 -1
- reconcile/utils/terraform_client.py +5 -4
- reconcile/utils/terrascript_aws_client.py +192 -114
- 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
- tools/app_interface_reporter.py +4 -4
- tools/cli_commands/cost_report/cost_management_api.py +3 -3
- tools/cli_commands/cost_report/view.py +7 -6
- tools/cli_commands/erv2.py +1 -1
- tools/cli_commands/systems_and_tools.py +5 -1
- tools/qontract_cli.py +31 -18
- tools/template_validation.py +3 -1
- reconcile/gql_definitions/cna/__init__.py +0 -0
- reconcile/gql_definitions/cna/queries/__init__.py +0 -0
- reconcile/gql_definitions/ocm_oidc_idp/__init__.py +0 -0
- reconcile/gql_definitions/ocm_subscription_labels/__init__.py +0 -0
- {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/entry_points.txt +0 -0
|
@@ -24,30 +24,24 @@ class Environment(BaseModel):
|
|
|
24
24
|
return self.name == other
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
class FeatureToggle(BaseModel):
|
|
27
|
+
class FeatureToggle(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
28
28
|
name: str
|
|
29
29
|
type: FeatureToggleType = FeatureToggleType.release
|
|
30
30
|
description: str | None = None
|
|
31
31
|
impression_data: bool = Field(False, alias="impressionData")
|
|
32
32
|
environments: list[Environment]
|
|
33
33
|
|
|
34
|
-
class Config:
|
|
35
|
-
allow_population_by_field_name = True
|
|
36
|
-
|
|
37
34
|
def __eq__(self, other: object) -> bool:
|
|
38
35
|
if isinstance(other, FeatureToggle):
|
|
39
36
|
return self.name == other.name
|
|
40
37
|
return self.name == other
|
|
41
38
|
|
|
42
39
|
|
|
43
|
-
class Project(BaseModel):
|
|
40
|
+
class Project(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
44
41
|
pk: str = Field(alias="id")
|
|
45
42
|
name: str
|
|
46
43
|
feature_toggles: list[FeatureToggle] = []
|
|
47
44
|
|
|
48
|
-
class Config:
|
|
49
|
-
allow_population_by_field_name = True
|
|
50
|
-
|
|
51
45
|
|
|
52
46
|
class TokenAuth(BearerTokenAuth):
|
|
53
47
|
def __call__(self, r: requests.PreparedRequest) -> requests.PreparedRequest:
|
reconcile/utils/vault.py
CHANGED
|
@@ -6,7 +6,7 @@ import threading
|
|
|
6
6
|
import time
|
|
7
7
|
from collections.abc import Mapping
|
|
8
8
|
from functools import lru_cache
|
|
9
|
-
from typing import Any, Self
|
|
9
|
+
from typing import Any, Self
|
|
10
10
|
|
|
11
11
|
import hvac
|
|
12
12
|
import requests
|
|
@@ -48,13 +48,6 @@ class VaultConnectionError(Exception):
|
|
|
48
48
|
pass
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
class Secret(TypedDict):
|
|
52
|
-
path: str
|
|
53
|
-
field: str
|
|
54
|
-
format: str | None
|
|
55
|
-
version: str | None
|
|
56
|
-
|
|
57
|
-
|
|
58
51
|
SECRET_VERSION_LATEST = "LATEST"
|
|
59
52
|
|
|
60
53
|
|
|
@@ -197,7 +190,7 @@ class VaultClient:
|
|
|
197
190
|
self._client.auth_approle(self.role_id, self.secret_id)
|
|
198
191
|
|
|
199
192
|
@retry()
|
|
200
|
-
def read_all_with_version(self, secret: Mapping) -> tuple[
|
|
193
|
+
def read_all_with_version(self, secret: Mapping) -> tuple[dict, int | None]:
|
|
201
194
|
"""Returns a dictionary of keys and values in a Vault secret and the
|
|
202
195
|
version of the secret, for V1 secrets, version will be None.
|
|
203
196
|
|
|
@@ -207,7 +200,7 @@ class VaultClient:
|
|
|
207
200
|
a v2 KV engine)
|
|
208
201
|
"""
|
|
209
202
|
secret_path = secret["path"]
|
|
210
|
-
secret_version = secret.get("version")
|
|
203
|
+
secret_version = secret.get("version") or SECRET_VERSION_LATEST
|
|
211
204
|
|
|
212
205
|
kv_version = self._get_mount_version_by_secret_path(secret_path)
|
|
213
206
|
|
|
@@ -250,7 +243,7 @@ class VaultClient:
|
|
|
250
243
|
|
|
251
244
|
def __read_all_v2(
|
|
252
245
|
self, path: str, version: str | None
|
|
253
|
-
) -> tuple[dict[str, Any],
|
|
246
|
+
) -> tuple[dict[str, Any], int]:
|
|
254
247
|
path_split = path.split("/")
|
|
255
248
|
mount_point = path_split[0]
|
|
256
249
|
read_path = "/".join(path_split[1:])
|
|
@@ -294,7 +287,7 @@ class VaultClient:
|
|
|
294
287
|
return secret["data"]
|
|
295
288
|
|
|
296
289
|
@retry()
|
|
297
|
-
def read(self, secret:
|
|
290
|
+
def read(self, secret: Mapping[str, Any]) -> Any:
|
|
298
291
|
"""Returns a value of a key in a Vault secret.
|
|
299
292
|
|
|
300
293
|
The input secret is a dictionary which contains the following fields:
|
reconcile/utils/vcs.py
CHANGED
|
@@ -140,7 +140,7 @@ class VCS:
|
|
|
140
140
|
gitlab_instances: Iterable[GitlabInstanceV1],
|
|
141
141
|
) -> GitLabApi:
|
|
142
142
|
return GitLabApi(
|
|
143
|
-
next(iter(gitlab_instances)).
|
|
143
|
+
next(iter(gitlab_instances)).model_dump(by_alias=True),
|
|
144
144
|
secret_reader=self._secret_reader,
|
|
145
145
|
)
|
|
146
146
|
|
|
@@ -150,7 +150,7 @@ class VCS:
|
|
|
150
150
|
app_interface_repo_url: str,
|
|
151
151
|
) -> GitLabApi:
|
|
152
152
|
return GitLabApi(
|
|
153
|
-
next(iter(gitlab_instances)).
|
|
153
|
+
next(iter(gitlab_instances)).model_dump(by_alias=True),
|
|
154
154
|
secret_reader=self._secret_reader,
|
|
155
155
|
project_url=app_interface_repo_url,
|
|
156
156
|
)
|
|
@@ -221,26 +221,26 @@ class VCS:
|
|
|
221
221
|
match repo_info.platform:
|
|
222
222
|
case "github":
|
|
223
223
|
github = self._init_github(repo_url=repo_url, auth_code=auth_code)
|
|
224
|
-
data = github.compare(commit_from=commit_from, commit_to=commit_to)
|
|
225
224
|
return [
|
|
226
225
|
Commit(
|
|
227
226
|
repo=repo_url,
|
|
228
227
|
sha=gh_commit.sha,
|
|
229
228
|
date=gh_commit.commit.committer.date,
|
|
230
229
|
)
|
|
231
|
-
for gh_commit in
|
|
230
|
+
for gh_commit in github.compare(
|
|
231
|
+
commit_from=commit_from, commit_to=commit_to
|
|
232
|
+
)
|
|
232
233
|
]
|
|
233
234
|
case "gitlab":
|
|
234
|
-
data = self._gitlab_instance.repository_compare(
|
|
235
|
-
repo_url=repo_url, ref_from=commit_from, ref_to=commit_to
|
|
236
|
-
)
|
|
237
235
|
return [
|
|
238
236
|
Commit(
|
|
239
237
|
repo=repo_url,
|
|
240
238
|
sha=gl_commit["id"],
|
|
241
239
|
date=datetime.fromisoformat(gl_commit["committed_date"]),
|
|
242
240
|
)
|
|
243
|
-
for gl_commit in
|
|
241
|
+
for gl_commit in self._gitlab_instance.repository_compare(
|
|
242
|
+
repo_url=repo_url, ref_from=commit_from, ref_to=commit_to
|
|
243
|
+
)
|
|
244
244
|
]
|
|
245
245
|
case _:
|
|
246
246
|
raise ValueError(f"Unsupported repository URL: {repo_url}")
|
reconcile/vault_replication.py
CHANGED
|
@@ -84,6 +84,54 @@ def deep_copy_versions(
|
|
|
84
84
|
dest_vault.write(secret=write_dict, decode_base64=False, force=True)
|
|
85
85
|
|
|
86
86
|
|
|
87
|
+
def _handle_missing_destination_secret(
|
|
88
|
+
dry_run: bool,
|
|
89
|
+
source_vault: VaultClient,
|
|
90
|
+
dest_vault: VaultClient,
|
|
91
|
+
source_data: dict,
|
|
92
|
+
source_version: int | None,
|
|
93
|
+
path: str,
|
|
94
|
+
) -> None:
|
|
95
|
+
"""Handles replication when destination secret is missing or has no accessible versions.
|
|
96
|
+
|
|
97
|
+
This covers two scenarios:
|
|
98
|
+
1. Secret doesn't exist at all in destination vault (SecretNotFoundError)
|
|
99
|
+
2. Secret exists but all versions are deleted in KV v2 (SecretVersionNotFoundError)
|
|
100
|
+
|
|
101
|
+
For both cases, we replicate from source starting from version 0 (or copy directly for v1).
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
dry_run: Whether this is a dry run
|
|
105
|
+
source_vault: Source vault client (needed for v2 deep copy)
|
|
106
|
+
dest_vault: Destination vault client
|
|
107
|
+
source_data: Already retrieved source secret data
|
|
108
|
+
source_version: Source secret version (None for v1 secrets)
|
|
109
|
+
path: Secret path
|
|
110
|
+
"""
|
|
111
|
+
if source_version is None:
|
|
112
|
+
# v1 secret - just copy it over using the already-retrieved source data
|
|
113
|
+
logging.info(["replicate_vault_secret", "Copying v1 secret", path])
|
|
114
|
+
if not dry_run:
|
|
115
|
+
write_dict = {"path": path, "data": source_data}
|
|
116
|
+
dest_vault.write(secret=write_dict, decode_base64=False, force=True)
|
|
117
|
+
else:
|
|
118
|
+
# v2 secret - deep copy all versions starting from 0
|
|
119
|
+
# Note: deep_copy_versions will read individual versions from source as needed
|
|
120
|
+
logging.info([
|
|
121
|
+
"replicate_vault_secret",
|
|
122
|
+
"Deep copying v2 secret versions",
|
|
123
|
+
path,
|
|
124
|
+
])
|
|
125
|
+
deep_copy_versions(
|
|
126
|
+
dry_run=dry_run,
|
|
127
|
+
source_vault=source_vault,
|
|
128
|
+
dest_vault=dest_vault,
|
|
129
|
+
current_dest_version=0,
|
|
130
|
+
current_source_version=source_version,
|
|
131
|
+
path=path,
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
|
|
87
135
|
def write_dummy_versions(
|
|
88
136
|
dry_run: bool,
|
|
89
137
|
dest_vault: VaultClient,
|
|
@@ -133,48 +181,65 @@ def copy_vault_secret(
|
|
|
133
181
|
|
|
134
182
|
try:
|
|
135
183
|
dest_data, dest_version = dest_vault.read_all_with_version(secret_dict)
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
184
|
+
except SecretVersionNotFoundError:
|
|
185
|
+
# Handle KV v2 case where secret metadata exists but latest version is deleted
|
|
186
|
+
# This occurs when someone manually deletes the latest version but the secret
|
|
187
|
+
# metadata still exists in Vault. This should only happen for v2 secrets.
|
|
188
|
+
logging.info([
|
|
189
|
+
"replicate_vault_secret",
|
|
190
|
+
"KV v2 latest version deleted, replicating all versions",
|
|
191
|
+
path,
|
|
192
|
+
])
|
|
193
|
+
_handle_missing_destination_secret(
|
|
194
|
+
dry_run=dry_run,
|
|
195
|
+
source_vault=source_vault,
|
|
196
|
+
dest_vault=dest_vault,
|
|
197
|
+
source_data=source_data,
|
|
198
|
+
source_version=version,
|
|
199
|
+
path=path,
|
|
200
|
+
)
|
|
201
|
+
return
|
|
202
|
+
except SecretNotFoundError:
|
|
203
|
+
# Handle case where secret doesn't exist at all in destination vault
|
|
204
|
+
logging.info([
|
|
205
|
+
"replicate_vault_secret",
|
|
206
|
+
"Secret not found in destination",
|
|
207
|
+
path,
|
|
208
|
+
])
|
|
209
|
+
_handle_missing_destination_secret(
|
|
210
|
+
dry_run=dry_run,
|
|
211
|
+
source_vault=source_vault,
|
|
212
|
+
dest_vault=dest_vault,
|
|
213
|
+
source_data=source_data,
|
|
214
|
+
source_version=version,
|
|
215
|
+
path=path,
|
|
216
|
+
)
|
|
217
|
+
return
|
|
218
|
+
|
|
219
|
+
# If we reach here, we successfully read the destination secret
|
|
220
|
+
if dest_version is None or version is None:
|
|
221
|
+
# v1 secrets don't have version
|
|
222
|
+
if source_data == dest_data:
|
|
223
|
+
# If the secret is the same in both vaults, we don't need
|
|
224
|
+
# to copy it again
|
|
225
|
+
return
|
|
226
|
+
|
|
227
|
+
write_dict = {"path": path, "data": source_data}
|
|
228
|
+
logging.info(["replicate_vault_secret", path])
|
|
229
|
+
if not dry_run:
|
|
230
|
+
# Using force=True to write the secret to force the vault client even
|
|
231
|
+
# if the data is the same as the previous version. This happens in
|
|
232
|
+
# some secrets even tho the library does not create it
|
|
233
|
+
dest_vault.write(secret=write_dict, decode_base64=False, force=True)
|
|
234
|
+
elif dest_version < version:
|
|
235
|
+
deep_copy_versions(
|
|
236
|
+
dry_run=dry_run,
|
|
237
|
+
source_vault=source_vault,
|
|
238
|
+
dest_vault=dest_vault,
|
|
239
|
+
current_dest_version=dest_version,
|
|
240
|
+
current_source_version=version,
|
|
241
|
+
path=path,
|
|
242
|
+
)
|
|
178
243
|
|
|
179
244
|
|
|
180
245
|
def check_invalid_paths(
|
tools/app_interface_reporter.py
CHANGED
|
@@ -4,7 +4,6 @@ import os
|
|
|
4
4
|
import textwrap
|
|
5
5
|
from collections.abc import Mapping, MutableMapping
|
|
6
6
|
from datetime import (
|
|
7
|
-
UTC,
|
|
8
7
|
datetime,
|
|
9
8
|
)
|
|
10
9
|
|
|
@@ -29,6 +28,7 @@ from reconcile.cli import (
|
|
|
29
28
|
)
|
|
30
29
|
from reconcile.jenkins_job_builder import init_jjb
|
|
31
30
|
from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
|
|
31
|
+
from reconcile.utils.datetime_util import ensure_utc, utc_now
|
|
32
32
|
from reconcile.utils.mr import CreateAppInterfaceReporter
|
|
33
33
|
from reconcile.utils.runtime.environment import init_env
|
|
34
34
|
from reconcile.utils.secret_reader import SecretReader
|
|
@@ -189,8 +189,8 @@ def get_apps_data(
|
|
|
189
189
|
apps = queries.get_apps()
|
|
190
190
|
jjb = init_jjb(secret_reader)
|
|
191
191
|
jenkins_map = jenkins_base.get_jenkins_map()
|
|
192
|
-
time_limit = date - relativedelta(months=month_delta)
|
|
193
|
-
timestamp_limit = int(time_limit.
|
|
192
|
+
time_limit = ensure_utc(date) - relativedelta(months=month_delta)
|
|
193
|
+
timestamp_limit = int(time_limit.timestamp())
|
|
194
194
|
|
|
195
195
|
secret_content = secret_reader.read_all({"path": DASHDOTDB_SECRET})
|
|
196
196
|
dashdotdb_url = secret_content["url"]
|
|
@@ -411,7 +411,7 @@ def main(
|
|
|
411
411
|
) -> None:
|
|
412
412
|
init_env(log_level=log_level, config_file=configfile)
|
|
413
413
|
|
|
414
|
-
now =
|
|
414
|
+
now = utc_now()
|
|
415
415
|
apps = get_apps_data(now, thread_pool_size=thread_pool_size)
|
|
416
416
|
|
|
417
417
|
reports = [Report(app, now).to_message() for app in apps]
|
|
@@ -74,7 +74,7 @@ class CostManagementApi(ApiBase):
|
|
|
74
74
|
timeout=self.read_timeout,
|
|
75
75
|
)
|
|
76
76
|
response.raise_for_status()
|
|
77
|
-
return AwsReportCostResponse.
|
|
77
|
+
return AwsReportCostResponse.model_validate(response.json())
|
|
78
78
|
|
|
79
79
|
def get_openshift_costs_report(
|
|
80
80
|
self,
|
|
@@ -97,7 +97,7 @@ class CostManagementApi(ApiBase):
|
|
|
97
97
|
timeout=self.read_timeout,
|
|
98
98
|
)
|
|
99
99
|
response.raise_for_status()
|
|
100
|
-
return OpenShiftReportCostResponse.
|
|
100
|
+
return OpenShiftReportCostResponse.model_validate(response.json())
|
|
101
101
|
|
|
102
102
|
def get_openshift_cost_optimization_report(
|
|
103
103
|
self,
|
|
@@ -120,7 +120,7 @@ class CostManagementApi(ApiBase):
|
|
|
120
120
|
response.raise_for_status()
|
|
121
121
|
|
|
122
122
|
data = self._get_paginated(response)
|
|
123
|
-
return OpenShiftCostOptimizationReportResponse.
|
|
123
|
+
return OpenShiftCostOptimizationReportResponse.model_validate(data)
|
|
124
124
|
|
|
125
125
|
def _get_paginated(
|
|
126
126
|
self,
|
|
@@ -4,6 +4,7 @@ from typing import Any
|
|
|
4
4
|
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
|
|
7
|
+
from reconcile.utils.json import json_dumps
|
|
7
8
|
from tools.cli_commands.cost_report.model import OptimizationReport, Report
|
|
8
9
|
|
|
9
10
|
LAYOUT = """\
|
|
@@ -244,7 +245,7 @@ def render_summary(
|
|
|
244
245
|
return template.format(
|
|
245
246
|
date=get_date(reports),
|
|
246
247
|
total_cost=format_cost_value(total_cost),
|
|
247
|
-
json_table=json_table
|
|
248
|
+
json_table=json_dumps(json_table, indent=2, mode="python"),
|
|
248
249
|
)
|
|
249
250
|
|
|
250
251
|
|
|
@@ -274,7 +275,7 @@ def render_month_over_month_change(reports: Mapping[str, Report]) -> str:
|
|
|
274
275
|
)
|
|
275
276
|
return MONTH_OVER_MONTH_CHANGE.format(
|
|
276
277
|
date=get_date(reports),
|
|
277
|
-
json_table=json_table
|
|
278
|
+
json_table=json_dumps(json_table, indent=2, mode="python"),
|
|
278
279
|
)
|
|
279
280
|
|
|
280
281
|
|
|
@@ -304,7 +305,7 @@ def render_aws_services_cost(
|
|
|
304
305
|
items_total=format_cost_value(report.items_total),
|
|
305
306
|
items_delta_value=format_delta_value(report.items_delta_value),
|
|
306
307
|
items_delta_percent=format_delta_percent(report.items_delta_percent),
|
|
307
|
-
json_table=json_table
|
|
308
|
+
json_table=json_dumps(json_table, indent=2, mode="python"),
|
|
308
309
|
)
|
|
309
310
|
|
|
310
311
|
|
|
@@ -316,7 +317,7 @@ def render_openshift_workloads_cost(
|
|
|
316
317
|
items_total=format_cost_value(report.items_total),
|
|
317
318
|
items_delta_value=format_delta_value(report.items_delta_value),
|
|
318
319
|
items_delta_percent=format_delta_percent(report.items_delta_percent),
|
|
319
|
-
json_table=json_table
|
|
320
|
+
json_table=json_dumps(json_table, indent=2, mode="python"),
|
|
320
321
|
)
|
|
321
322
|
|
|
322
323
|
|
|
@@ -362,7 +363,7 @@ def render_child_apps_cost(report: Report) -> str:
|
|
|
362
363
|
)
|
|
363
364
|
return CHILD_APPS_COST.format(
|
|
364
365
|
child_apps_total=format_cost_value(report.child_apps_total),
|
|
365
|
-
json_table=json_table
|
|
366
|
+
json_table=json_dumps(json_table, indent=2, mode="python"),
|
|
366
367
|
)
|
|
367
368
|
|
|
368
369
|
|
|
@@ -509,7 +510,7 @@ def render_optimization(
|
|
|
509
510
|
)
|
|
510
511
|
return OPTIMIZATION.format(
|
|
511
512
|
app_name=report.app_name,
|
|
512
|
-
json_table=json_table
|
|
513
|
+
json_table=json_dumps(json_table, indent=2, mode="python"),
|
|
513
514
|
)
|
|
514
515
|
|
|
515
516
|
|
tools/cli_commands/erv2.py
CHANGED
|
@@ -133,7 +133,7 @@ class Erv2Cli:
|
|
|
133
133
|
|
|
134
134
|
@property
|
|
135
135
|
def input_data(self) -> str:
|
|
136
|
-
return self._resource.
|
|
136
|
+
return self._resource.export(exclude={"data": {FLAG_RESOURCE_MANAGED_BY_ERV2}})
|
|
137
137
|
|
|
138
138
|
@property
|
|
139
139
|
def image(self) -> str:
|
|
@@ -132,6 +132,7 @@ from reconcile.typed_queries.vault import get_vault_instances
|
|
|
132
132
|
from reconcile.utils import (
|
|
133
133
|
gql,
|
|
134
134
|
)
|
|
135
|
+
from reconcile.utils.slack_api import is_gov_slack_workspace
|
|
135
136
|
|
|
136
137
|
|
|
137
138
|
class SystemTool(BaseModel):
|
|
@@ -322,11 +323,14 @@ class SystemTool(BaseModel):
|
|
|
322
323
|
|
|
323
324
|
@classmethod
|
|
324
325
|
def init_from_slack_workspace(cls, s: SlackWorkspaceV1, enumeration: Any) -> Self:
|
|
326
|
+
# Automatically determine the correct Slack domain based on GOV_SLACK environment variable
|
|
327
|
+
domain = "slack-gov.com" if is_gov_slack_workspace() else "slack.com"
|
|
328
|
+
|
|
325
329
|
return cls(
|
|
326
330
|
system_type="slack",
|
|
327
331
|
system_id=s.name,
|
|
328
332
|
name=s.name,
|
|
329
|
-
url=f"https://{s.name}.
|
|
333
|
+
url=f"https://{s.name}.{domain}",
|
|
330
334
|
description=s.description,
|
|
331
335
|
enumeration=enumeration,
|
|
332
336
|
)
|
tools/qontract_cli.py
CHANGED
|
@@ -13,7 +13,6 @@ import tempfile
|
|
|
13
13
|
import textwrap
|
|
14
14
|
from collections import defaultdict
|
|
15
15
|
from datetime import (
|
|
16
|
-
UTC,
|
|
17
16
|
datetime,
|
|
18
17
|
timedelta,
|
|
19
18
|
)
|
|
@@ -122,6 +121,7 @@ from reconcile.utils.binary import (
|
|
|
122
121
|
binary,
|
|
123
122
|
binary_version,
|
|
124
123
|
)
|
|
124
|
+
from reconcile.utils.datetime_util import from_utc_iso_format, utc_now
|
|
125
125
|
from reconcile.utils.early_exit_cache import (
|
|
126
126
|
CacheKey,
|
|
127
127
|
CacheKeyWithDigest,
|
|
@@ -141,6 +141,7 @@ from reconcile.utils.gitlab_api import (
|
|
|
141
141
|
)
|
|
142
142
|
from reconcile.utils.glitchtip.client import GlitchtipClient
|
|
143
143
|
from reconcile.utils.gql import GqlApiSingleton
|
|
144
|
+
from reconcile.utils.json import json_dumps
|
|
144
145
|
from reconcile.utils.keycloak import (
|
|
145
146
|
KeycloakAPI,
|
|
146
147
|
SSOClient,
|
|
@@ -417,8 +418,8 @@ def get_upgrade_policies_data(
|
|
|
417
418
|
upgrade_next_run = None
|
|
418
419
|
upgrade_emoji = "💫"
|
|
419
420
|
if upgrade_next_run:
|
|
420
|
-
dt =
|
|
421
|
-
now =
|
|
421
|
+
dt = from_utc_iso_format(upgrade_next_run)
|
|
422
|
+
now = utc_now()
|
|
422
423
|
if dt > now:
|
|
423
424
|
upgrade_emoji = "⏰"
|
|
424
425
|
hours_ago = (now - dt).total_seconds() / 3600
|
|
@@ -841,7 +842,7 @@ def alert_report(
|
|
|
841
842
|
)
|
|
842
843
|
sys.exit(1)
|
|
843
844
|
|
|
844
|
-
now =
|
|
845
|
+
now = utc_now()
|
|
845
846
|
from_timestamp = int((now - timedelta(days=days)).timestamp())
|
|
846
847
|
to_timestamp = int(now.timestamp())
|
|
847
848
|
|
|
@@ -887,7 +888,9 @@ def alert_report(
|
|
|
887
888
|
"Triggered": str(data.triggered_alerts),
|
|
888
889
|
"Resolved": str(data.resolved_alerts),
|
|
889
890
|
"Median time to resolve (h:mm:ss)": median_elapsed,
|
|
890
|
-
"Response Rate": f"{data.responsed_alerts / data.triggered_alerts * 100:.2f}%"
|
|
891
|
+
"Response Rate": f"{data.responsed_alerts / data.triggered_alerts * 100:.2f}%"
|
|
892
|
+
if data.triggered_alerts != 0
|
|
893
|
+
else "0.00%",
|
|
891
894
|
})
|
|
892
895
|
|
|
893
896
|
# TODO(mafriedm, rporres): Fix this
|
|
@@ -1564,7 +1567,7 @@ def rosa_create_cluster_command(ctx: click.Context, cluster_name: str) -> None:
|
|
|
1564
1567
|
billing_account = account.billing_account.uid
|
|
1565
1568
|
else:
|
|
1566
1569
|
with AWSApi(
|
|
1567
|
-
1, [account.
|
|
1570
|
+
1, [account.model_dump(by_alias=True)], settings=settings, init_users=False
|
|
1568
1571
|
) as aws_api:
|
|
1569
1572
|
billing_account = aws_api.get_organization_billing_account(account.name)
|
|
1570
1573
|
|
|
@@ -1748,7 +1751,7 @@ def aws_terraform_resources(ctx: click.Context) -> None:
|
|
|
1748
1751
|
for ns_info in namespaces:
|
|
1749
1752
|
specs = (
|
|
1750
1753
|
get_external_resource_specs(
|
|
1751
|
-
ns_info.
|
|
1754
|
+
ns_info.model_dump(by_alias=True), provision_provider=PROVIDER_AWS
|
|
1752
1755
|
)
|
|
1753
1756
|
or []
|
|
1754
1757
|
)
|
|
@@ -1806,7 +1809,7 @@ def rds(ctx: click.Context) -> None:
|
|
|
1806
1809
|
specs = [
|
|
1807
1810
|
s
|
|
1808
1811
|
for s in get_external_resource_specs(
|
|
1809
|
-
namespace.
|
|
1812
|
+
namespace.model_dump(by_alias=True), provision_provider=PROVIDER_AWS
|
|
1810
1813
|
)
|
|
1811
1814
|
if s.provider == "rds"
|
|
1812
1815
|
]
|
|
@@ -2272,7 +2275,7 @@ def app_interface_merge_queue(ctx: click.Context) -> None:
|
|
|
2272
2275
|
"labels",
|
|
2273
2276
|
]
|
|
2274
2277
|
merge_queue_data = []
|
|
2275
|
-
now =
|
|
2278
|
+
now = utc_now()
|
|
2276
2279
|
for mr in merge_requests:
|
|
2277
2280
|
item = {
|
|
2278
2281
|
"id": f"[{mr['mr'].iid}]({mr['mr'].web_url})",
|
|
@@ -2281,7 +2284,7 @@ def app_interface_merge_queue(ctx: click.Context) -> None:
|
|
|
2281
2284
|
+ 1, # adding 1 for human readability
|
|
2282
2285
|
"approved_at": mr["approved_at"],
|
|
2283
2286
|
"approved_span_minutes": (
|
|
2284
|
-
now -
|
|
2287
|
+
now - from_utc_iso_format(mr["approved_at"])
|
|
2285
2288
|
).total_seconds()
|
|
2286
2289
|
/ 60,
|
|
2287
2290
|
"approved_by": mr["approved_by"],
|
|
@@ -2695,7 +2698,7 @@ def ec2_jenkins_workers(
|
|
|
2695
2698
|
client = boto3.client("autoscaling")
|
|
2696
2699
|
ec2 = boto3.resource("ec2")
|
|
2697
2700
|
results = []
|
|
2698
|
-
now =
|
|
2701
|
+
now = utc_now()
|
|
2699
2702
|
columns = [
|
|
2700
2703
|
"type",
|
|
2701
2704
|
"id",
|
|
@@ -2955,11 +2958,11 @@ def osd_component_versions(ctx: click.Context) -> None:
|
|
|
2955
2958
|
@get.command()
|
|
2956
2959
|
@click.pass_context
|
|
2957
2960
|
def maintenances(ctx: click.Context) -> None:
|
|
2958
|
-
now =
|
|
2961
|
+
now = utc_now()
|
|
2959
2962
|
maintenances = maintenances_gql.query(gql.get_api().query).maintenances or []
|
|
2960
2963
|
data = [
|
|
2961
2964
|
{
|
|
2962
|
-
**m.
|
|
2965
|
+
**m.model_dump(),
|
|
2963
2966
|
"services": ", ".join(a.name for a in m.affected_services),
|
|
2964
2967
|
}
|
|
2965
2968
|
for m in maintenances
|
|
@@ -4097,7 +4100,9 @@ def sre_checkpoint_metadata(
|
|
|
4097
4100
|
) -> None:
|
|
4098
4101
|
"""Check an app path for checkpoint-related metadata."""
|
|
4099
4102
|
data = queries.get_app_metadata(app_path)
|
|
4100
|
-
|
|
4103
|
+
vault_settings = get_app_interface_vault_settings()
|
|
4104
|
+
secret_reader = create_secret_reader(use_vault=vault_settings.vault)
|
|
4105
|
+
|
|
4101
4106
|
app = data[0]
|
|
4102
4107
|
|
|
4103
4108
|
if jiradef:
|
|
@@ -4110,7 +4115,14 @@ def sre_checkpoint_metadata(
|
|
|
4110
4115
|
# Overrides for easier testing
|
|
4111
4116
|
if jiraboard:
|
|
4112
4117
|
board["name"] = jiraboard
|
|
4113
|
-
report_invalid_metadata(
|
|
4118
|
+
report_invalid_metadata(
|
|
4119
|
+
app=app,
|
|
4120
|
+
path=app_path,
|
|
4121
|
+
board=board,
|
|
4122
|
+
secret_reader=secret_reader,
|
|
4123
|
+
parent=parent_ticket,
|
|
4124
|
+
dry_run=dry_run,
|
|
4125
|
+
)
|
|
4114
4126
|
|
|
4115
4127
|
|
|
4116
4128
|
@root.command()
|
|
@@ -4287,7 +4299,7 @@ def create(
|
|
|
4287
4299
|
bg="red",
|
|
4288
4300
|
fg="white",
|
|
4289
4301
|
)
|
|
4290
|
-
print(sso_client
|
|
4302
|
+
print(json_dumps(sso_client, indent=2))
|
|
4291
4303
|
|
|
4292
4304
|
|
|
4293
4305
|
@sso_client.command()
|
|
@@ -4839,11 +4851,12 @@ def top_talkers(ctx: click.Context, top: int) -> None:
|
|
|
4839
4851
|
assert project.organization # make mypy happy
|
|
4840
4852
|
assert project.pk # make mypy happy
|
|
4841
4853
|
|
|
4854
|
+
now = utc_now()
|
|
4842
4855
|
stat = client.project_statistics(
|
|
4843
4856
|
organization_slug=project.organization.slug,
|
|
4844
4857
|
project_pk=project.pk,
|
|
4845
|
-
start=
|
|
4846
|
-
end=
|
|
4858
|
+
start=now - timedelta(hours=24),
|
|
4859
|
+
end=now,
|
|
4847
4860
|
)
|
|
4848
4861
|
stats.append((project, stat))
|
|
4849
4862
|
|
tools/template_validation.py
CHANGED
|
@@ -67,7 +67,9 @@ def main(templates: tuple[str]) -> None:
|
|
|
67
67
|
tests.append(test_yaml)
|
|
68
68
|
|
|
69
69
|
template_raw["templateTest"] = tests
|
|
70
|
-
|
|
70
|
+
data = data_default_none(TemplateV1, template_raw)
|
|
71
|
+
assert isinstance(data, dict)
|
|
72
|
+
template: TemplateV1 = TemplateV1(**data)
|
|
71
73
|
|
|
72
74
|
# templates_to_validate = {}
|
|
73
75
|
for test in template.template_test:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|