qontract-reconcile 0.10.2.dev299__py3-none-any.whl → 0.10.2.dev430__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/METADATA +13 -12
- {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/RECORD +399 -394
- 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 +4 -4
- reconcile/aws_iam_keys.py +1 -0
- reconcile/aws_saml_idp/integration.py +12 -4
- reconcile/aws_saml_roles/integration.py +32 -25
- reconcile/aws_version_sync/integration.py +125 -84
- reconcile/change_owners/bundle.py +3 -3
- reconcile/change_owners/change_log_tracking.py +3 -2
- reconcile/change_owners/change_owners.py +1 -1
- reconcile/change_owners/diff.py +2 -4
- reconcile/checkpoint.py +12 -4
- reconcile/cli.py +111 -18
- reconcile/cluster_deployment_mapper.py +2 -3
- reconcile/dashdotdb_dora.py +5 -12
- reconcile/dashdotdb_slo.py +1 -1
- reconcile/database_access_manager.py +125 -121
- reconcile/deadmanssnitch.py +1 -5
- reconcile/dynatrace_token_provider/integration.py +1 -1
- reconcile/endpoints_discovery/integration.py +4 -1
- reconcile/endpoints_discovery/merge_request.py +1 -1
- reconcile/endpoints_discovery/merge_request_manager.py +9 -11
- reconcile/external_resources/factories.py +5 -12
- reconcile/external_resources/integration.py +1 -1
- reconcile/external_resources/manager.py +8 -5
- reconcile/external_resources/meta.py +0 -1
- reconcile/external_resources/metrics.py +1 -1
- reconcile/external_resources/model.py +20 -20
- reconcile/external_resources/reconciler.py +7 -4
- reconcile/external_resources/secrets_sync.py +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 +23 -10
- 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 +492 -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 +10 -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 +3050 -1393
- 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 +448 -402
- reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +23 -17
- reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +9 -9
- reconcile/gql_definitions/vault_instances/vault_instances.py +61 -61
- reconcile/gql_definitions/vault_policies/vault_policies.py +11 -11
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +8 -8
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
- reconcile/integrations_manager.py +3 -3
- reconcile/jenkins_job_builder.py +1 -1
- reconcile/jenkins_worker_fleets.py +80 -11
- reconcile/jira_permissions_validator.py +237 -122
- reconcile/ldap_groups/integration.py +1 -1
- reconcile/ocm/types.py +35 -56
- reconcile/ocm_aws_infrastructure_access.py +1 -1
- reconcile/ocm_clusters.py +4 -4
- reconcile/ocm_labels/integration.py +3 -2
- reconcile/ocm_machine_pools.py +33 -27
- reconcile/openshift_base.py +122 -10
- reconcile/openshift_cluster_bots.py +5 -5
- reconcile/openshift_groups.py +5 -0
- reconcile/openshift_limitranges.py +1 -1
- reconcile/openshift_namespace_labels.py +1 -1
- reconcile/openshift_namespaces.py +97 -101
- reconcile/openshift_resources_base.py +10 -5
- reconcile/openshift_rhcs_certs.py +77 -40
- reconcile/openshift_rolebindings.py +230 -130
- reconcile/openshift_saas_deploy.py +6 -7
- reconcile/openshift_saas_deploy_change_tester.py +9 -7
- reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
- reconcile/openshift_serviceaccount_tokens.py +8 -7
- reconcile/openshift_tekton_resources.py +1 -1
- reconcile/openshift_upgrade_watcher.py +4 -4
- reconcile/openshift_users.py +5 -3
- reconcile/oum/labelset.py +5 -3
- reconcile/oum/models.py +1 -4
- reconcile/oum/providers.py +1 -1
- reconcile/prometheus_rules_tester/integration.py +4 -4
- reconcile/quay_mirror.py +1 -1
- reconcile/queries.py +131 -0
- reconcile/requests_sender.py +8 -3
- reconcile/resource_scraper.py +1 -5
- reconcile/rhidp/common.py +5 -5
- reconcile/rhidp/sso_client/base.py +19 -10
- reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
- reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
- reconcile/sendgrid_teammates.py +20 -9
- reconcile/skupper_network/integration.py +2 -2
- reconcile/slack_usergroups.py +35 -14
- reconcile/sql_query.py +1 -0
- reconcile/status.py +2 -2
- reconcile/status_board.py +6 -6
- reconcile/statuspage/atlassian.py +7 -7
- reconcile/statuspage/integrations/maintenances.py +4 -3
- reconcile/statuspage/page.py +4 -9
- reconcile/statuspage/status.py +5 -8
- reconcile/templates/rosa-classic-cluster-creation.sh.j2 +4 -0
- reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +3 -0
- reconcile/templating/lib/merge_request_manager.py +2 -2
- reconcile/templating/lib/rendering.py +3 -3
- reconcile/templating/renderer.py +12 -13
- reconcile/terraform_aws_route53.py +18 -8
- reconcile/terraform_cloudflare_dns.py +3 -3
- reconcile/terraform_cloudflare_resources.py +12 -13
- reconcile/terraform_cloudflare_users.py +3 -2
- reconcile/terraform_init/integration.py +187 -23
- reconcile/terraform_repo.py +16 -12
- reconcile/terraform_resources.py +18 -10
- reconcile/terraform_tgw_attachments.py +27 -19
- reconcile/terraform_users.py +29 -21
- reconcile/terraform_vpc_peerings.py +16 -4
- reconcile/terraform_vpc_resources/integration.py +32 -2
- reconcile/typed_queries/app_interface_roles.py +10 -0
- reconcile/typed_queries/aws_account_tags.py +41 -0
- reconcile/typed_queries/cost_report/app_names.py +1 -1
- reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
- reconcile/typed_queries/saas_files.py +13 -13
- reconcile/typed_queries/status_board.py +2 -2
- reconcile/unleash_feature_toggles/integration.py +4 -2
- reconcile/utils/acs/base.py +6 -3
- reconcile/utils/acs/policies.py +2 -2
- reconcile/utils/aggregated_list.py +4 -3
- reconcile/utils/aws_api.py +51 -20
- reconcile/utils/aws_api_typed/api.py +38 -9
- reconcile/utils/aws_api_typed/cloudformation.py +149 -0
- reconcile/utils/aws_api_typed/logs.py +73 -0
- reconcile/utils/aws_api_typed/organization.py +4 -2
- reconcile/utils/binary.py +7 -12
- reconcile/utils/datetime_util.py +67 -0
- reconcile/utils/deadmanssnitch_api.py +1 -1
- reconcile/utils/differ.py +2 -3
- reconcile/utils/early_exit_cache.py +11 -12
- reconcile/utils/expiration.py +7 -3
- reconcile/utils/external_resource_spec.py +24 -1
- reconcile/utils/filtering.py +1 -1
- reconcile/utils/gitlab_api.py +7 -5
- reconcile/utils/glitchtip/client.py +6 -2
- reconcile/utils/glitchtip/models.py +25 -28
- reconcile/utils/gpg.py +5 -3
- reconcile/utils/gql.py +4 -7
- reconcile/utils/helm.py +2 -1
- reconcile/utils/helpers.py +1 -1
- reconcile/utils/imap_client.py +1 -1
- reconcile/utils/instrumented_wrappers.py +1 -1
- reconcile/utils/internal_groups/client.py +2 -2
- reconcile/utils/internal_groups/models.py +8 -17
- reconcile/utils/jenkins_api.py +24 -1
- reconcile/utils/jinja2/utils.py +6 -8
- reconcile/utils/jira_client.py +82 -63
- reconcile/utils/jjb_client.py +59 -43
- reconcile/utils/jobcontroller/controller.py +2 -2
- reconcile/utils/jobcontroller/models.py +17 -1
- reconcile/utils/json.py +74 -0
- reconcile/utils/ldap_client.py +4 -3
- reconcile/utils/lean_terraform_client.py +3 -1
- reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
- reconcile/utils/membershipsources/models.py +16 -23
- reconcile/utils/membershipsources/resolver.py +4 -2
- reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
- reconcile/utils/merge_request_manager/parser.py +6 -6
- reconcile/utils/metrics.py +5 -5
- reconcile/utils/models.py +304 -82
- reconcile/utils/mr/__init__.py +3 -1
- reconcile/utils/mr/app_interface_reporter.py +6 -3
- reconcile/utils/mr/aws_access.py +1 -1
- reconcile/utils/mr/base.py +7 -13
- reconcile/utils/mr/clusters_updates.py +4 -2
- reconcile/utils/mr/notificator.py +3 -3
- reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py +4 -1
- reconcile/utils/mr/promote_qontract.py +28 -12
- reconcile/utils/mr/update_access_report_base.py +3 -4
- reconcile/utils/mr/user_maintenance.py +7 -6
- reconcile/utils/oc.py +445 -336
- reconcile/utils/oc_filters.py +3 -3
- reconcile/utils/ocm/addons.py +0 -1
- reconcile/utils/ocm/base.py +27 -20
- reconcile/utils/ocm/cluster_groups.py +1 -1
- reconcile/utils/ocm/identity_providers.py +2 -2
- reconcile/utils/ocm/labels.py +1 -1
- reconcile/utils/ocm/ocm.py +81 -71
- reconcile/utils/ocm/products.py +9 -3
- reconcile/utils/ocm/search_filters.py +3 -6
- reconcile/utils/ocm/service_log.py +4 -6
- reconcile/utils/ocm/sre_capability_labels.py +20 -13
- reconcile/utils/ocm_base_client.py +4 -4
- reconcile/utils/openshift_resource.py +83 -52
- reconcile/utils/openssl.py +2 -2
- reconcile/utils/output.py +3 -2
- reconcile/utils/pagerduty_api.py +10 -7
- reconcile/utils/promotion_state.py +6 -11
- reconcile/utils/raw_github_api.py +11 -8
- reconcile/utils/repo_owners.py +21 -29
- reconcile/utils/rhcsv2_certs.py +138 -35
- reconcile/utils/rosa/session.py +16 -0
- reconcile/utils/runtime/integration.py +2 -3
- reconcile/utils/runtime/meta.py +2 -1
- reconcile/utils/runtime/runner.py +2 -2
- reconcile/utils/saasherder/interfaces.py +13 -20
- reconcile/utils/saasherder/models.py +25 -21
- reconcile/utils/saasherder/saasherder.py +60 -32
- reconcile/utils/secret_reader.py +6 -6
- reconcile/utils/sharding.py +1 -1
- reconcile/utils/slack_api.py +26 -4
- reconcile/utils/sloth.py +224 -0
- reconcile/utils/sqs_gateway.py +16 -11
- reconcile/utils/state.py +2 -1
- reconcile/utils/structs.py +4 -4
- reconcile/utils/terraform_client.py +32 -29
- reconcile/utils/terrascript_aws_client.py +658 -480
- reconcile/utils/three_way_diff_strategy.py +1 -1
- reconcile/utils/throughput.py +1 -1
- reconcile/utils/unleash/server.py +2 -8
- reconcile/utils/vault.py +44 -41
- reconcile/utils/vcs.py +8 -8
- reconcile/vault_replication.py +119 -58
- reconcile/vpc_peerings_validator.py +2 -2
- tools/app_interface_reporter.py +4 -4
- tools/cli_commands/cost_report/cost_management_api.py +3 -3
- tools/cli_commands/cost_report/view.py +7 -6
- tools/cli_commands/erv2.py +1 -1
- tools/cli_commands/gpg_encrypt.py +4 -1
- tools/cli_commands/systems_and_tools.py +5 -1
- tools/qontract_cli.py +36 -21
- tools/sre_checkpoints/util.py +5 -3
- tools/template_validation.py +3 -1
- reconcile/gql_definitions/ocm_oidc_idp/__init__.py +0 -0
- reconcile/gql_definitions/ocm_subscription_labels/__init__.py +0 -0
- reconcile/jenkins/__init__.py +0 -0
- reconcile/jenkins/types.py +0 -77
- {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/entry_points.txt +0 -0
|
@@ -5,7 +5,6 @@ from abc import (
|
|
|
5
5
|
from collections.abc import Iterable
|
|
6
6
|
from dataclasses import dataclass
|
|
7
7
|
from datetime import (
|
|
8
|
-
UTC,
|
|
9
8
|
datetime,
|
|
10
9
|
)
|
|
11
10
|
from enum import Enum
|
|
@@ -16,6 +15,8 @@ from typing import (
|
|
|
16
15
|
|
|
17
16
|
import dateparser
|
|
18
17
|
|
|
18
|
+
from reconcile.utils.datetime_util import utc_now
|
|
19
|
+
|
|
19
20
|
|
|
20
21
|
@dataclass
|
|
21
22
|
class FilterCondition:
|
|
@@ -166,17 +167,13 @@ class DateRangeCondition(FilterCondition):
|
|
|
166
167
|
return date
|
|
167
168
|
parsed = dateparser.parse(
|
|
168
169
|
date,
|
|
169
|
-
settings={"RELATIVE_BASE":
|
|
170
|
+
settings={"RELATIVE_BASE": utc_now()},
|
|
170
171
|
)
|
|
171
172
|
if parsed is None:
|
|
172
173
|
raise InvalidFilterError(f"Invalid relative date: {date}")
|
|
173
174
|
|
|
174
175
|
return parsed
|
|
175
176
|
|
|
176
|
-
@staticmethod
|
|
177
|
-
def now() -> datetime:
|
|
178
|
-
return datetime.now(tz=UTC)
|
|
179
|
-
|
|
180
177
|
|
|
181
178
|
class InvalidFilterError(Exception):
|
|
182
179
|
pass
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
from collections.abc import Generator
|
|
2
|
-
from datetime import
|
|
3
|
-
datetime,
|
|
4
|
-
timedelta,
|
|
5
|
-
)
|
|
2
|
+
from datetime import timedelta
|
|
6
3
|
|
|
4
|
+
from reconcile.utils.datetime_util import utc_now
|
|
7
5
|
from reconcile.utils.ocm.base import (
|
|
8
6
|
OCMClusterServiceLog,
|
|
9
7
|
OCMClusterServiceLogCreateModel,
|
|
@@ -47,7 +45,7 @@ def create_service_log(
|
|
|
47
45
|
.eq("severity", service_log.severity.value)
|
|
48
46
|
.eq("summary", service_log.summary)
|
|
49
47
|
.eq("description", service_log.description)
|
|
50
|
-
.after("created_at",
|
|
48
|
+
.after("created_at", utc_now() - dedup_interval),
|
|
51
49
|
),
|
|
52
50
|
None,
|
|
53
51
|
)
|
|
@@ -57,6 +55,6 @@ def create_service_log(
|
|
|
57
55
|
return OCMClusterServiceLog(
|
|
58
56
|
**ocm_api.post(
|
|
59
57
|
api_path=CLUSTER_SERVICE_LOGS_CREATE_ENDPOINT,
|
|
60
|
-
data=service_log.
|
|
58
|
+
data=service_log.model_dump(by_alias=True),
|
|
61
59
|
)
|
|
62
60
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
|
-
from pydantic import
|
|
4
|
-
from pydantic.fields import
|
|
3
|
+
from pydantic import WithJsonSchema
|
|
4
|
+
from pydantic.fields import FieldInfo
|
|
5
5
|
|
|
6
6
|
from reconcile.utils.ocm.base import (
|
|
7
7
|
LabelContainer,
|
|
@@ -22,11 +22,11 @@ def sre_capability_label_key(
|
|
|
22
22
|
return f"sre-capabilities.{sre_capability}.{config_atom}"
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
def labelset_groupfield(group_prefix: str) ->
|
|
25
|
+
def labelset_groupfield(group_prefix: str) -> WithJsonSchema:
|
|
26
26
|
"""
|
|
27
27
|
Helper function to build the FieldMeta for a labelset field that groups labels.
|
|
28
28
|
"""
|
|
29
|
-
return
|
|
29
|
+
return WithJsonSchema({"group_by_prefix": group_prefix})
|
|
30
30
|
|
|
31
31
|
|
|
32
32
|
def build_labelset(
|
|
@@ -36,16 +36,23 @@ def build_labelset(
|
|
|
36
36
|
Instantiates a dataclass from a set of labels.
|
|
37
37
|
"""
|
|
38
38
|
raw_data = {
|
|
39
|
-
field.alias: _labelset_field_value(labels, field)
|
|
40
|
-
for field in dataclass.
|
|
39
|
+
field.alias or name: _labelset_field_value(labels, name, field)
|
|
40
|
+
for name, field in dataclass.model_fields.items()
|
|
41
41
|
}
|
|
42
42
|
return dataclass(**raw_data)
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
def _labelset_field_value(
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
45
|
+
def _labelset_field_value(
|
|
46
|
+
labels: LabelContainer, name: str, field: FieldInfo
|
|
47
|
+
) -> Any | None:
|
|
48
|
+
schema = next((m for m in field.metadata if isinstance(m, WithJsonSchema)), None)
|
|
49
|
+
if (
|
|
50
|
+
schema is None
|
|
51
|
+
or not schema.json_schema
|
|
52
|
+
or "group_by_prefix" not in schema.json_schema
|
|
53
|
+
):
|
|
54
|
+
return labels.get_label_value(field.alias or name)
|
|
55
|
+
|
|
56
|
+
return build_container_for_prefix(
|
|
57
|
+
labels, schema.json_schema["group_by_prefix"], strip_key_prefix=True
|
|
58
|
+
).get_values_dict()
|
|
@@ -49,7 +49,7 @@ class OCMBaseClient:
|
|
|
49
49
|
self._init_request_headers()
|
|
50
50
|
|
|
51
51
|
@retry()
|
|
52
|
-
def _init_access_token(self):
|
|
52
|
+
def _init_access_token(self) -> None:
|
|
53
53
|
data = {
|
|
54
54
|
"grant_type": "client_credentials",
|
|
55
55
|
"client_id": self._access_token_client_id,
|
|
@@ -61,7 +61,7 @@ class OCMBaseClient:
|
|
|
61
61
|
r.raise_for_status()
|
|
62
62
|
self._access_token = r.json().get("access_token")
|
|
63
63
|
|
|
64
|
-
def _init_request_headers(self):
|
|
64
|
+
def _init_request_headers(self) -> None:
|
|
65
65
|
self._session.headers.update({
|
|
66
66
|
"Authorization": f"Bearer {self._access_token}",
|
|
67
67
|
"accept": "application/json",
|
|
@@ -130,7 +130,7 @@ class OCMBaseClient:
|
|
|
130
130
|
api_path: str,
|
|
131
131
|
data: Mapping[str, Any],
|
|
132
132
|
params: Mapping[str, str] | None = None,
|
|
133
|
-
):
|
|
133
|
+
) -> None:
|
|
134
134
|
ocm_request.labels(verb="PATCH", client_id=self._access_token_client_id).inc()
|
|
135
135
|
r = self._session.patch(
|
|
136
136
|
f"{self._url}{api_path}",
|
|
@@ -144,7 +144,7 @@ class OCMBaseClient:
|
|
|
144
144
|
logging.error(r.text)
|
|
145
145
|
raise e
|
|
146
146
|
|
|
147
|
-
def delete(self, api_path: str):
|
|
147
|
+
def delete(self, api_path: str) -> None:
|
|
148
148
|
ocm_request.labels(verb="DELETE", client_id=self._access_token_client_id).inc()
|
|
149
149
|
r = self._session.delete(f"{self._url}{api_path}", timeout=REQUEST_TIMEOUT_SEC)
|
|
150
150
|
try:
|
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
# ruff: noqa: SIM114
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
2
4
|
import base64
|
|
3
5
|
import contextlib
|
|
4
6
|
import copy
|
|
5
|
-
import datetime
|
|
6
7
|
import hashlib
|
|
7
|
-
import
|
|
8
|
+
import logging
|
|
8
9
|
import re
|
|
9
|
-
from collections.abc import Mapping
|
|
10
10
|
from threading import Lock
|
|
11
|
+
from typing import TYPE_CHECKING, Any
|
|
11
12
|
|
|
12
13
|
import semver
|
|
13
14
|
from pydantic import BaseModel
|
|
14
15
|
|
|
15
16
|
from reconcile.external_resources.meta import SECRET_UPDATED_AT
|
|
17
|
+
from reconcile.utils.datetime_util import to_utc_seconds_iso_format, utc_now
|
|
18
|
+
from reconcile.utils.json import json_dumps
|
|
16
19
|
from reconcile.utils.metrics import GaugeMetric
|
|
17
20
|
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from collections.abc import Iterator, Mapping
|
|
23
|
+
|
|
18
24
|
SECRET_MAX_KEY_LENGTH = 253
|
|
19
25
|
|
|
20
26
|
|
|
@@ -27,7 +33,7 @@ class ResourceNotManagedError(Exception):
|
|
|
27
33
|
|
|
28
34
|
|
|
29
35
|
class ConstructResourceError(Exception):
|
|
30
|
-
def __init__(self, msg):
|
|
36
|
+
def __init__(self, msg: str) -> None:
|
|
31
37
|
super().__init__("error constructing openshift resource: " + str(msg))
|
|
32
38
|
|
|
33
39
|
|
|
@@ -73,13 +79,13 @@ QONTRACT_ANNOTATIONS = {
|
|
|
73
79
|
class OpenshiftResource:
|
|
74
80
|
def __init__(
|
|
75
81
|
self,
|
|
76
|
-
body,
|
|
77
|
-
integration,
|
|
78
|
-
integration_version,
|
|
79
|
-
error_details="",
|
|
80
|
-
caller_name=None,
|
|
81
|
-
validate_k8s_object=True,
|
|
82
|
-
):
|
|
82
|
+
body: dict[str, Any],
|
|
83
|
+
integration: str,
|
|
84
|
+
integration_version: str,
|
|
85
|
+
error_details: str = "",
|
|
86
|
+
caller_name: str | None = None,
|
|
87
|
+
validate_k8s_object: bool = True,
|
|
88
|
+
) -> None:
|
|
83
89
|
self.body = body
|
|
84
90
|
self.integration = integration
|
|
85
91
|
self.integration_version = integration_version
|
|
@@ -88,10 +94,12 @@ class OpenshiftResource:
|
|
|
88
94
|
if validate_k8s_object:
|
|
89
95
|
self.verify_valid_k8s_object()
|
|
90
96
|
|
|
91
|
-
def __eq__(self, other):
|
|
97
|
+
def __eq__(self, other: object) -> bool:
|
|
98
|
+
if not isinstance(other, OpenshiftResource):
|
|
99
|
+
return False
|
|
92
100
|
return self.obj_intersect_equal(self.body, other.body)
|
|
93
101
|
|
|
94
|
-
def obj_intersect_equal(self, obj1, obj2, depth=0):
|
|
102
|
+
def obj_intersect_equal(self, obj1: Any, obj2: Any, depth: int = 0) -> bool:
|
|
95
103
|
# obj1 == d_item
|
|
96
104
|
# obj2 == c_item
|
|
97
105
|
if obj1.__class__ != obj2.__class__:
|
|
@@ -163,7 +171,7 @@ class OpenshiftResource:
|
|
|
163
171
|
return True
|
|
164
172
|
|
|
165
173
|
@staticmethod
|
|
166
|
-
def ignorable_field(val):
|
|
174
|
+
def ignorable_field(val: str) -> bool:
|
|
167
175
|
ignorable_fields = [
|
|
168
176
|
"kubectl.kubernetes.io/last-applied-configuration",
|
|
169
177
|
"creationTimestamp",
|
|
@@ -176,14 +184,14 @@ class OpenshiftResource:
|
|
|
176
184
|
return val in ignorable_fields
|
|
177
185
|
|
|
178
186
|
@staticmethod
|
|
179
|
-
def ignorable_key_value_pair(key, val):
|
|
187
|
+
def ignorable_key_value_pair(key: str, val: Any) -> bool:
|
|
180
188
|
ignorable_key_value_pair = {"annotations": None, "divisor": "0"}
|
|
181
189
|
return bool(
|
|
182
190
|
key in ignorable_key_value_pair and ignorable_key_value_pair[key] == val
|
|
183
191
|
)
|
|
184
192
|
|
|
185
193
|
@staticmethod
|
|
186
|
-
def cpu_equal(val1, val2):
|
|
194
|
+
def cpu_equal(val1: Any, val2: Any) -> bool:
|
|
187
195
|
# normalize both to string
|
|
188
196
|
with contextlib.suppress(Exception):
|
|
189
197
|
val1 = f"{int(float(val1) * 1000)}m"
|
|
@@ -192,7 +200,7 @@ class OpenshiftResource:
|
|
|
192
200
|
return val1 == val2
|
|
193
201
|
|
|
194
202
|
@staticmethod
|
|
195
|
-
def api_version_mutation(val1, val2):
|
|
203
|
+
def api_version_mutation(val1: str, val2: str) -> bool:
|
|
196
204
|
# required temporarily, pending response on
|
|
197
205
|
# https://redhat.service-now.com/surl.do?n=INC1224482
|
|
198
206
|
if val1 == "apps/v1" and val2 == "extensions/v1beta1":
|
|
@@ -204,7 +212,7 @@ class OpenshiftResource:
|
|
|
204
212
|
return val1 == val2
|
|
205
213
|
|
|
206
214
|
@property
|
|
207
|
-
def name(self):
|
|
215
|
+
def name(self) -> str:
|
|
208
216
|
# PipelineRun name can be empty when creating
|
|
209
217
|
if self.kind == "PipelineRun" and "name" not in self.body["metadata"]:
|
|
210
218
|
return self.body["metadata"]["generateName"][:-1]
|
|
@@ -212,19 +220,19 @@ class OpenshiftResource:
|
|
|
212
220
|
return self.body["metadata"]["name"]
|
|
213
221
|
|
|
214
222
|
@property
|
|
215
|
-
def kind(self):
|
|
223
|
+
def kind(self) -> str:
|
|
216
224
|
return self.body["kind"]
|
|
217
225
|
|
|
218
226
|
@property
|
|
219
|
-
def annotations(self):
|
|
227
|
+
def annotations(self) -> dict[str, str]:
|
|
220
228
|
return self.body["metadata"].get("annotations", {})
|
|
221
229
|
|
|
222
230
|
@property
|
|
223
|
-
def kind_and_group(self):
|
|
231
|
+
def kind_and_group(self) -> str:
|
|
224
232
|
return fully_qualified_kind(self.kind, self.body["apiVersion"])
|
|
225
233
|
|
|
226
234
|
@property
|
|
227
|
-
def caller(self):
|
|
235
|
+
def caller(self) -> str | None:
|
|
228
236
|
try:
|
|
229
237
|
return (
|
|
230
238
|
self.caller_name
|
|
@@ -233,7 +241,7 @@ class OpenshiftResource:
|
|
|
233
241
|
except KeyError:
|
|
234
242
|
return None
|
|
235
243
|
|
|
236
|
-
def verify_valid_k8s_object(self):
|
|
244
|
+
def verify_valid_k8s_object(self) -> None:
|
|
237
245
|
try:
|
|
238
246
|
assert self.name
|
|
239
247
|
assert self.kind
|
|
@@ -291,7 +299,7 @@ class OpenshiftResource:
|
|
|
291
299
|
pass
|
|
292
300
|
|
|
293
301
|
@staticmethod
|
|
294
|
-
def is_controller_managed_label(kind, label) -> bool:
|
|
302
|
+
def is_controller_managed_label(kind: str, label: str) -> bool:
|
|
295
303
|
for il in CONTROLLER_MANAGED_LABELS.get(kind, []):
|
|
296
304
|
if isinstance(il, str) and il == label:
|
|
297
305
|
return True
|
|
@@ -299,7 +307,7 @@ class OpenshiftResource:
|
|
|
299
307
|
return True
|
|
300
308
|
return False
|
|
301
309
|
|
|
302
|
-
def has_qontract_annotations(self):
|
|
310
|
+
def has_qontract_annotations(self) -> bool:
|
|
303
311
|
try:
|
|
304
312
|
annotations = self.body["metadata"]["annotations"]
|
|
305
313
|
|
|
@@ -322,10 +330,10 @@ class OpenshiftResource:
|
|
|
322
330
|
|
|
323
331
|
return True
|
|
324
332
|
|
|
325
|
-
def has_owner_reference(self):
|
|
333
|
+
def has_owner_reference(self) -> bool:
|
|
326
334
|
return bool(self.body["metadata"].get("ownerReferences", []))
|
|
327
335
|
|
|
328
|
-
def has_valid_sha256sum(self):
|
|
336
|
+
def has_valid_sha256sum(self) -> bool:
|
|
329
337
|
try:
|
|
330
338
|
current_sha256sum = self.body["metadata"]["annotations"][
|
|
331
339
|
"qontract.sha256sum"
|
|
@@ -334,7 +342,7 @@ class OpenshiftResource:
|
|
|
334
342
|
except KeyError:
|
|
335
343
|
return False
|
|
336
344
|
|
|
337
|
-
def annotate(self, canonicalize=True):
|
|
345
|
+
def annotate(self, canonicalize: bool = True) -> OpenshiftResource:
|
|
338
346
|
"""
|
|
339
347
|
Creates a OpenshiftResource with the qontract annotations, and removes
|
|
340
348
|
unneeded Openshift fields.
|
|
@@ -361,24 +369,24 @@ class OpenshiftResource:
|
|
|
361
369
|
annotations[QONTRACT_ANNOTATION_INTEGRATION] = self.integration
|
|
362
370
|
annotations[QONTRACT_ANNOTATION_INTEGRATION_VERSION] = self.integration_version
|
|
363
371
|
annotations[QONTRACT_ANNOTATION_SHA256SUM] = sha256sum
|
|
364
|
-
now =
|
|
365
|
-
annotations[QONTRACT_ANNOTATION_UPDATE] = now
|
|
372
|
+
now = utc_now()
|
|
373
|
+
annotations[QONTRACT_ANNOTATION_UPDATE] = to_utc_seconds_iso_format(now)
|
|
366
374
|
if self.caller_name:
|
|
367
375
|
annotations[QONTRACT_ANNOTATION_CALLER_NAME] = self.caller_name
|
|
368
376
|
|
|
369
377
|
return OpenshiftResource(body, self.integration, self.integration_version)
|
|
370
378
|
|
|
371
|
-
def sha256sum(self):
|
|
379
|
+
def sha256sum(self) -> str:
|
|
372
380
|
body = self.annotate().body
|
|
373
381
|
|
|
374
382
|
annotations = body["metadata"]["annotations"]
|
|
375
383
|
return annotations["qontract.sha256sum"]
|
|
376
384
|
|
|
377
|
-
def to_json(self):
|
|
385
|
+
def to_json(self) -> str:
|
|
378
386
|
return self.serialize(self.body)
|
|
379
387
|
|
|
380
388
|
@staticmethod
|
|
381
|
-
def canonicalize(body):
|
|
389
|
+
def canonicalize(body: dict[str, Any]) -> dict[str, Any]:
|
|
382
390
|
body = copy.deepcopy(body)
|
|
383
391
|
|
|
384
392
|
# create annotations if not present
|
|
@@ -522,11 +530,11 @@ class OpenshiftResource:
|
|
|
522
530
|
return body
|
|
523
531
|
|
|
524
532
|
@staticmethod
|
|
525
|
-
def serialize(body):
|
|
526
|
-
return
|
|
533
|
+
def serialize(body: dict[str, Any]) -> str:
|
|
534
|
+
return json_dumps(body)
|
|
527
535
|
|
|
528
536
|
@staticmethod
|
|
529
|
-
def calculate_sha256sum(body):
|
|
537
|
+
def calculate_sha256sum(body: str) -> str:
|
|
530
538
|
m = hashlib.sha256()
|
|
531
539
|
m.update(body.encode("utf-8"))
|
|
532
540
|
return m.hexdigest()
|
|
@@ -559,19 +567,19 @@ class OpenshiftResourceInventoryGauge(OpenshiftResourceBaseMetric, GaugeMetric):
|
|
|
559
567
|
|
|
560
568
|
|
|
561
569
|
class ResourceInventory:
|
|
562
|
-
def __init__(self):
|
|
563
|
-
self._clusters = {}
|
|
570
|
+
def __init__(self) -> None:
|
|
571
|
+
self._clusters: dict[str, dict[str, dict[str, dict[str, Any]]]] = {}
|
|
564
572
|
self._error_registered = False
|
|
565
|
-
self._error_registered_clusters = {}
|
|
573
|
+
self._error_registered_clusters: dict[str, bool] = {}
|
|
566
574
|
self._lock = Lock()
|
|
567
575
|
|
|
568
576
|
def initialize_resource_type(
|
|
569
577
|
self,
|
|
570
|
-
cluster,
|
|
571
|
-
namespace,
|
|
572
|
-
resource_type,
|
|
578
|
+
cluster: str,
|
|
579
|
+
namespace: str,
|
|
580
|
+
resource_type: str,
|
|
573
581
|
managed_names: list[str] | None = None,
|
|
574
|
-
):
|
|
582
|
+
) -> None:
|
|
575
583
|
self._clusters.setdefault(cluster, {})
|
|
576
584
|
self._clusters[cluster].setdefault(namespace, {})
|
|
577
585
|
self._clusters[cluster][namespace].setdefault(
|
|
@@ -594,6 +602,10 @@ class ResourceInventory:
|
|
|
594
602
|
resource: OpenshiftResource,
|
|
595
603
|
privileged: bool = False,
|
|
596
604
|
) -> None:
|
|
605
|
+
if cluster not in self._clusters:
|
|
606
|
+
logging.error(f"Cluster {cluster} not initialized in ResourceInventory")
|
|
607
|
+
return
|
|
608
|
+
|
|
597
609
|
if resource.kind_and_group in self._clusters[cluster][namespace]:
|
|
598
610
|
kind = resource.kind_and_group
|
|
599
611
|
else:
|
|
@@ -608,8 +620,14 @@ class ResourceInventory:
|
|
|
608
620
|
)
|
|
609
621
|
|
|
610
622
|
def add_desired(
|
|
611
|
-
self,
|
|
612
|
-
|
|
623
|
+
self,
|
|
624
|
+
cluster: str,
|
|
625
|
+
namespace: str,
|
|
626
|
+
resource_type: str,
|
|
627
|
+
name: str,
|
|
628
|
+
value: OpenshiftResource,
|
|
629
|
+
privileged: bool = False,
|
|
630
|
+
) -> None:
|
|
613
631
|
# privileged permissions to apply resources to clusters are managed on
|
|
614
632
|
# a per-namespace level in qontract-schema namespace files, but are
|
|
615
633
|
# tracked on a per-resource level in ResourceInventory and the
|
|
@@ -633,41 +651,54 @@ class ResourceInventory:
|
|
|
633
651
|
]
|
|
634
652
|
admin_token_usage[name] = privileged
|
|
635
653
|
|
|
636
|
-
def get_desired(
|
|
654
|
+
def get_desired(
|
|
655
|
+
self, cluster: str, namespace: str, resource_type: str, name: str
|
|
656
|
+
) -> OpenshiftResource | None:
|
|
637
657
|
try:
|
|
638
658
|
return self._clusters[cluster][namespace][resource_type]["desired"][name]
|
|
639
659
|
except KeyError:
|
|
640
660
|
return None
|
|
641
661
|
|
|
642
|
-
def get_desired_by_type(
|
|
662
|
+
def get_desired_by_type(
|
|
663
|
+
self, cluster: str, namespace: str, resource_type: str
|
|
664
|
+
) -> dict[str, OpenshiftResource] | None:
|
|
643
665
|
try:
|
|
644
666
|
return self._clusters[cluster][namespace][resource_type]["desired"]
|
|
645
667
|
except KeyError:
|
|
646
668
|
return None
|
|
647
669
|
|
|
648
|
-
def get_current(
|
|
670
|
+
def get_current(
|
|
671
|
+
self, cluster: str, namespace: str, resource_type: str, name: str
|
|
672
|
+
) -> OpenshiftResource | None:
|
|
649
673
|
try:
|
|
650
674
|
return self._clusters[cluster][namespace][resource_type]["current"][name]
|
|
651
675
|
except KeyError:
|
|
652
676
|
return None
|
|
653
677
|
|
|
654
|
-
def add_current(
|
|
678
|
+
def add_current(
|
|
679
|
+
self,
|
|
680
|
+
cluster: str,
|
|
681
|
+
namespace: str,
|
|
682
|
+
resource_type: str,
|
|
683
|
+
name: str,
|
|
684
|
+
value: OpenshiftResource,
|
|
685
|
+
) -> None:
|
|
655
686
|
with self._lock:
|
|
656
687
|
current = self._clusters[cluster][namespace][resource_type]["current"]
|
|
657
688
|
current[name] = value
|
|
658
689
|
|
|
659
|
-
def __iter__(self):
|
|
690
|
+
def __iter__(self) -> Iterator[tuple[str, str, str, dict[str, Any]]]:
|
|
660
691
|
for cluster_name, cluster in self._clusters.items():
|
|
661
692
|
for namespace_name, namespace in cluster.items():
|
|
662
693
|
for resource_type, resource in namespace.items():
|
|
663
694
|
yield (cluster_name, namespace_name, resource_type, resource)
|
|
664
695
|
|
|
665
|
-
def register_error(self, cluster=None):
|
|
696
|
+
def register_error(self, cluster: str | None = None) -> None:
|
|
666
697
|
self._error_registered = True
|
|
667
698
|
if cluster is not None:
|
|
668
699
|
self._error_registered_clusters[cluster] = True
|
|
669
700
|
|
|
670
|
-
def has_error_registered(self, cluster=None):
|
|
701
|
+
def has_error_registered(self, cluster: str | None = None) -> bool:
|
|
671
702
|
if cluster is not None:
|
|
672
703
|
return self._error_registered_clusters.get(cluster, False)
|
|
673
704
|
return self._error_registered
|
reconcile/utils/openssl.py
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
from OpenSSL import crypto
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
def certificate_matches_host(certificate, host):
|
|
4
|
+
def certificate_matches_host(certificate: bytes, host: str) -> bool:
|
|
5
5
|
common_name = get_certificate_common_name(certificate)
|
|
6
6
|
return host.endswith(common_name.replace("*.", ""))
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def get_certificate_common_name(certificate):
|
|
9
|
+
def get_certificate_common_name(certificate: bytes) -> str:
|
|
10
10
|
cert = crypto.load_certificate(crypto.FILETYPE_PEM, certificate)
|
|
11
11
|
subject = cert.get_subject()
|
|
12
12
|
return subject.CN
|
reconcile/utils/output.py
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import json
|
|
2
1
|
import re
|
|
3
2
|
from collections.abc import Iterable, Mapping
|
|
4
3
|
|
|
5
4
|
import yaml
|
|
6
5
|
from tabulate import tabulate
|
|
7
6
|
|
|
7
|
+
from reconcile.utils.json import json_dumps
|
|
8
|
+
|
|
8
9
|
|
|
9
10
|
def print_output(
|
|
10
11
|
options: Mapping[str, str | bool],
|
|
@@ -30,7 +31,7 @@ def print_output(
|
|
|
30
31
|
)
|
|
31
32
|
print(formatted_content)
|
|
32
33
|
elif output == "json":
|
|
33
|
-
formatted_content =
|
|
34
|
+
formatted_content = json_dumps(content)
|
|
34
35
|
print(formatted_content)
|
|
35
36
|
elif output == "yaml":
|
|
36
37
|
formatted_content = yaml.dump(content)
|
reconcile/utils/pagerduty_api.py
CHANGED
|
@@ -3,8 +3,7 @@ from collections.abc import (
|
|
|
3
3
|
Callable,
|
|
4
4
|
Iterable,
|
|
5
5
|
)
|
|
6
|
-
from datetime import datetime
|
|
7
|
-
from datetime import timedelta
|
|
6
|
+
from datetime import datetime, timedelta
|
|
8
7
|
from typing import (
|
|
9
8
|
Protocol,
|
|
10
9
|
)
|
|
@@ -14,6 +13,7 @@ import requests
|
|
|
14
13
|
from pydantic import BaseModel
|
|
15
14
|
from sretoolbox.utils import retry
|
|
16
15
|
|
|
16
|
+
from reconcile.utils.datetime_util import utc_now
|
|
17
17
|
from reconcile.utils.secret_reader import (
|
|
18
18
|
HasSecret,
|
|
19
19
|
SecretReader,
|
|
@@ -52,10 +52,13 @@ class PagerDutyTarget(Protocol):
|
|
|
52
52
|
which must be implemented by a class to be compatible."""
|
|
53
53
|
|
|
54
54
|
name: str
|
|
55
|
-
instance: PagerDutyInstance
|
|
56
55
|
escalation_policy_id: str | None
|
|
57
56
|
schedule_id: str | None
|
|
58
57
|
|
|
58
|
+
@property
|
|
59
|
+
def instance(self) -> PagerDutyInstance:
|
|
60
|
+
pass
|
|
61
|
+
|
|
59
62
|
|
|
60
63
|
class PagerDutyConfig(BaseModel):
|
|
61
64
|
"""PagerDuty Config."""
|
|
@@ -80,7 +83,7 @@ class PagerDutyApi:
|
|
|
80
83
|
def get_pagerduty_users(
|
|
81
84
|
self, resource_type: str, resource_id: str
|
|
82
85
|
) -> list[pypd.User]:
|
|
83
|
-
now =
|
|
86
|
+
now = utc_now()
|
|
84
87
|
|
|
85
88
|
try:
|
|
86
89
|
if resource_type == "schedule":
|
|
@@ -103,7 +106,7 @@ class PagerDutyApi:
|
|
|
103
106
|
self.users.append(user)
|
|
104
107
|
return user.email.split("@")[0]
|
|
105
108
|
|
|
106
|
-
def get_schedule_users(self, schedule_id: str, now:
|
|
109
|
+
def get_schedule_users(self, schedule_id: str, now: datetime) -> list[pypd.User]:
|
|
107
110
|
until = now + timedelta(seconds=60)
|
|
108
111
|
s = pypd.Schedule.fetch(id=schedule_id, since=now, until=until, time_zone="UTC")
|
|
109
112
|
entries = s["final_schedule"]["rendered_schedule_entries"]
|
|
@@ -115,7 +118,7 @@ class PagerDutyApi:
|
|
|
115
118
|
]
|
|
116
119
|
|
|
117
120
|
def get_escalation_policy_users(
|
|
118
|
-
self, escalation_policy_id: str, now:
|
|
121
|
+
self, escalation_policy_id: str, now: datetime
|
|
119
122
|
) -> list[pypd.User]:
|
|
120
123
|
ep = pypd.EscalationPolicy.fetch(
|
|
121
124
|
id=escalation_policy_id, since=now, until=now, time_zone="UTC"
|
|
@@ -189,7 +192,7 @@ def get_pagerduty_name(user: PagerDutyUser) -> str:
|
|
|
189
192
|
return user.pagerduty_username or user.org_username
|
|
190
193
|
|
|
191
194
|
|
|
192
|
-
@retry(no_retry_exceptions=PagerDutyTargetError)
|
|
195
|
+
@retry(no_retry_exceptions=(PagerDutyTargetError,))
|
|
193
196
|
def get_usernames_from_pagerduty(
|
|
194
197
|
pagerduties: Iterable[PagerDutyTarget],
|
|
195
198
|
users: Iterable[PagerDutyUser],
|
|
@@ -3,13 +3,12 @@ from collections import defaultdict
|
|
|
3
3
|
|
|
4
4
|
from pydantic import (
|
|
5
5
|
BaseModel,
|
|
6
|
-
Extra,
|
|
7
6
|
)
|
|
8
7
|
|
|
9
8
|
from reconcile.utils.state import State
|
|
10
9
|
|
|
11
10
|
|
|
12
|
-
class PromotionData(BaseModel):
|
|
11
|
+
class PromotionData(BaseModel, extra="forbid"):
|
|
13
12
|
"""
|
|
14
13
|
A class that strictly corresponds to the json stored in S3.
|
|
15
14
|
|
|
@@ -20,17 +19,13 @@ class PromotionData(BaseModel):
|
|
|
20
19
|
|
|
21
20
|
# The success is primarily used for SAPM auto-promotions
|
|
22
21
|
success: bool
|
|
23
|
-
target_config_hash: str | None
|
|
24
|
-
saas_file: str | None
|
|
25
|
-
check_in: str | None
|
|
22
|
+
target_config_hash: str | None = None
|
|
23
|
+
saas_file: str | None = None
|
|
24
|
+
check_in: str | None = None
|
|
26
25
|
# Whether this promotion has ever succeeded
|
|
27
26
|
# Note, this shouldnt be overridden on subsequent promotions of same ref
|
|
28
27
|
# This attribute is primarily used by saasherder validations
|
|
29
|
-
has_succeeded_once: bool | None
|
|
30
|
-
|
|
31
|
-
class Config:
|
|
32
|
-
smart_union = True
|
|
33
|
-
extra = Extra.forbid
|
|
28
|
+
has_succeeded_once: bool | None = None
|
|
34
29
|
|
|
35
30
|
|
|
36
31
|
class PromotionState:
|
|
@@ -107,5 +102,5 @@ class PromotionState:
|
|
|
107
102
|
self, sha: str, channel: str, target_uid: str, data: PromotionData
|
|
108
103
|
) -> None:
|
|
109
104
|
state_key_v2 = f"promotions_v2/{channel}/{target_uid}/{sha}"
|
|
110
|
-
self._state.add(state_key_v2, data.
|
|
105
|
+
self._state.add(state_key_v2, data.model_dump(), force=True)
|
|
111
106
|
logging.info("Uploaded %s to %s", data, state_key_v2)
|