qontract-reconcile 0.10.2.dev394__py3-none-any.whl → 0.10.2.dev414__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.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/METADATA +4 -3
- {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/RECORD +308 -308
- reconcile/acs_rbac.py +2 -2
- reconcile/aus/advanced_upgrade_service.py +15 -12
- reconcile/aus/base.py +9 -13
- reconcile/aus/cluster_version_data.py +15 -5
- reconcile/aus/models.py +1 -1
- reconcile/automated_actions/config/integration.py +15 -3
- reconcile/aws_account_manager/integration.py +6 -6
- reconcile/aws_account_manager/reconciler.py +3 -3
- reconcile/aws_ami_cleanup/integration.py +2 -5
- reconcile/aws_ami_share.py +69 -62
- reconcile/aws_saml_idp/integration.py +5 -3
- reconcile/aws_saml_roles/integration.py +23 -22
- 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/dashdotdb_dora.py +1 -1
- reconcile/dashdotdb_slo.py +1 -1
- reconcile/database_access_manager.py +8 -9
- 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 +1 -1
- reconcile/external_resources/integration.py +1 -1
- reconcile/external_resources/manager.py +19 -7
- reconcile/external_resources/metrics.py +1 -1
- reconcile/external_resources/model.py +6 -6
- reconcile/external_resources/reconciler.py +7 -4
- reconcile/external_resources/secrets_sync.py +2 -2
- reconcile/external_resources/state.py +56 -14
- reconcile/fleet_labeler/integration.py +1 -1
- reconcile/gcp_image_mirror.py +2 -2
- reconcile/github_org.py +1 -1
- reconcile/github_owners.py +4 -0
- reconcile/gitlab_members.py +6 -12
- reconcile/gitlab_permissions.py +8 -12
- reconcile/glitchtip_project_alerts/integration.py +3 -1
- reconcile/gql_definitions/acs/acs_instances.py +5 -5
- reconcile/gql_definitions/acs/acs_policies.py +5 -5
- reconcile/gql_definitions/acs/acs_rbac.py +5 -5
- reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +5 -5
- reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +5 -5
- reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +5 -5
- reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
- reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
- reconcile/gql_definitions/automated_actions/instance.py +46 -7
- reconcile/gql_definitions/aws_account_manager/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
- reconcile/gql_definitions/aws_version_sync/clusters.py +5 -5
- reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
- reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
- reconcile/gql_definitions/change_owners/queries/self_service_roles.py +5 -5
- reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +5 -5
- reconcile/gql_definitions/common/alerting_services_settings.py +5 -5
- reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
- reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
- reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_roles.py +5 -5
- reconcile/gql_definitions/common/app_interface_state_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
- reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
- reconcile/gql_definitions/common/apps.py +5 -5
- reconcile/gql_definitions/common/aws_vpc_requests.py +5 -5
- reconcile/gql_definitions/common/aws_vpcs.py +5 -5
- reconcile/gql_definitions/common/clusters.py +5 -5
- reconcile/gql_definitions/common/clusters_minimal.py +5 -5
- reconcile/gql_definitions/common/clusters_with_dms.py +5 -5
- reconcile/gql_definitions/common/clusters_with_peering.py +5 -5
- reconcile/gql_definitions/common/github_orgs.py +5 -5
- reconcile/gql_definitions/common/jira_settings.py +5 -5
- reconcile/gql_definitions/common/jiralert_settings.py +5 -5
- reconcile/gql_definitions/common/ldap_settings.py +5 -5
- reconcile/gql_definitions/common/namespaces.py +5 -5
- reconcile/gql_definitions/common/namespaces_minimal.py +7 -5
- reconcile/gql_definitions/common/ocm_env_telemeter.py +5 -5
- reconcile/gql_definitions/common/ocm_environments.py +5 -5
- reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
- reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -5
- reconcile/gql_definitions/common/pipeline_providers.py +5 -5
- reconcile/gql_definitions/common/quay_instances.py +5 -5
- reconcile/gql_definitions/common/quay_orgs.py +5 -5
- reconcile/gql_definitions/common/reserved_networks.py +5 -5
- reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
- reconcile/gql_definitions/common/saas_files.py +5 -5
- reconcile/gql_definitions/common/saas_target_namespaces.py +5 -5
- reconcile/gql_definitions/common/saasherder_settings.py +5 -5
- reconcile/gql_definitions/common/slack_workspaces.py +5 -5
- reconcile/gql_definitions/common/smtp_client_settings.py +5 -5
- reconcile/gql_definitions/common/state_aws_account.py +5 -5
- reconcile/gql_definitions/common/users.py +5 -5
- reconcile/gql_definitions/common/users_with_paths.py +5 -5
- reconcile/gql_definitions/cost_report/app_names.py +5 -5
- reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
- reconcile/gql_definitions/cost_report/settings.py +5 -5
- reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +5 -5
- reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +5 -5
- reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
- reconcile/gql_definitions/email_sender/apps.py +5 -5
- reconcile/gql_definitions/email_sender/emails.py +5 -5
- reconcile/gql_definitions/email_sender/users.py +5 -5
- reconcile/gql_definitions/endpoints_discovery/apps.py +5 -5
- reconcile/gql_definitions/external_resources/aws_accounts.py +5 -5
- reconcile/gql_definitions/external_resources/external_resources_modules.py +5 -5
- reconcile/gql_definitions/external_resources/external_resources_namespaces.py +5 -5
- reconcile/gql_definitions/external_resources/external_resources_settings.py +5 -5
- reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
- reconcile/gql_definitions/fleet_labeler/fleet_labels.py +5 -5
- reconcile/gql_definitions/fragments/aus_organization.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_common.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
- reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
- reconcile/gql_definitions/fragments/aws_organization.py +5 -5
- reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
- reconcile/gql_definitions/fragments/aws_vpc_request.py +5 -5
- reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
- reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
- reconcile/gql_definitions/fragments/disable.py +5 -5
- reconcile/gql_definitions/fragments/email_service.py +5 -5
- reconcile/gql_definitions/fragments/email_user.py +5 -5
- reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
- reconcile/gql_definitions/fragments/membership_source.py +5 -5
- reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
- reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
- reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
- reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
- reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
- reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
- reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
- reconcile/gql_definitions/fragments/resource_values.py +5 -5
- reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
- reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
- reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
- reconcile/gql_definitions/fragments/terraform_state.py +5 -5
- reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
- reconcile/gql_definitions/fragments/user.py +5 -5
- reconcile/gql_definitions/fragments/vault_secret.py +5 -5
- reconcile/gql_definitions/gcp/gcp_docker_repos.py +5 -5
- reconcile/gql_definitions/gcp/gcp_projects.py +5 -5
- reconcile/gql_definitions/gitlab_members/gitlab_instances.py +5 -5
- reconcile/gql_definitions/gitlab_members/permissions.py +5 -5
- reconcile/gql_definitions/glitchtip/glitchtip_instance.py +5 -5
- reconcile/gql_definitions/glitchtip/glitchtip_project.py +5 -5
- reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +5 -5
- reconcile/gql_definitions/integrations/integrations.py +5 -5
- reconcile/gql_definitions/introspection.json +231 -0
- reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +5 -5
- reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +5 -5
- reconcile/gql_definitions/jira/jira_servers.py +5 -5
- reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +5 -5
- reconcile/gql_definitions/jumphosts/jumphosts.py +5 -5
- reconcile/gql_definitions/ldap_groups/roles.py +5 -5
- reconcile/gql_definitions/ldap_groups/settings.py +5 -5
- reconcile/gql_definitions/maintenance/maintenances.py +5 -5
- reconcile/gql_definitions/membershipsources/roles.py +5 -5
- reconcile/gql_definitions/ocm_labels/clusters.py +5 -5
- reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
- reconcile/gql_definitions/openshift_cluster_bots/clusters.py +5 -5
- reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
- reconcile/gql_definitions/openshift_groups/managed_roles.py +5 -5
- reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +5 -5
- reconcile/gql_definitions/quay_membership/quay_membership.py +5 -5
- reconcile/gql_definitions/rhcs/certs.py +5 -5
- reconcile/gql_definitions/rhidp/organizations.py +5 -5
- reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
- reconcile/gql_definitions/service_dependencies/service_dependencies.py +5 -5
- reconcile/gql_definitions/sharding/aws_accounts.py +5 -5
- reconcile/gql_definitions/sharding/ocm_organization.py +5 -5
- reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
- reconcile/gql_definitions/skupper_network/skupper_networks.py +5 -5
- reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
- reconcile/gql_definitions/slack_usergroups/permissions.py +5 -5
- reconcile/gql_definitions/slack_usergroups/users.py +5 -5
- reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
- reconcile/gql_definitions/status_board/status_board.py +5 -5
- reconcile/gql_definitions/statuspage/statuspages.py +5 -5
- reconcile/gql_definitions/templating/template_collection.py +5 -5
- reconcile/gql_definitions/templating/templates.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +5 -5
- reconcile/gql_definitions/terraform_init/aws_accounts.py +5 -5
- reconcile/gql_definitions/terraform_repo/terraform_repo.py +5 -5
- reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
- reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +5 -5
- reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +5 -5
- reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +5 -5
- reconcile/gql_definitions/vault_instances/vault_instances.py +5 -5
- reconcile/gql_definitions/vault_policies/vault_policies.py +5 -5
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +5 -5
- 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 +9 -8
- reconcile/jira_permissions_validator.py +2 -2
- reconcile/ldap_groups/integration.py +1 -1
- reconcile/ocm/types.py +35 -57
- reconcile/ocm_aws_infrastructure_access.py +1 -1
- reconcile/ocm_clusters.py +4 -4
- reconcile/ocm_labels/integration.py +3 -2
- reconcile/ocm_machine_pools.py +23 -23
- reconcile/openshift_base.py +53 -2
- reconcile/openshift_cluster_bots.py +1 -1
- 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 +5 -5
- reconcile/openshift_rolebindings.py +7 -11
- reconcile/openshift_saas_deploy.py +4 -5
- reconcile/openshift_saas_deploy_change_tester.py +9 -7
- reconcile/openshift_serviceaccount_tokens.py +2 -2
- reconcile/openshift_upgrade_watcher.py +1 -1
- 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 +6 -0
- reconcile/rhidp/common.py +3 -5
- reconcile/rhidp/sso_client/base.py +1 -1
- reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
- reconcile/skupper_network/integration.py +2 -2
- reconcile/slack_usergroups.py +31 -11
- reconcile/status_board.py +6 -6
- reconcile/statuspage/atlassian.py +7 -7
- reconcile/statuspage/page.py +4 -9
- reconcile/templating/lib/rendering.py +3 -3
- reconcile/templating/renderer.py +2 -2
- 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 +2 -2
- reconcile/terraform_repo.py +16 -12
- reconcile/terraform_resources.py +6 -6
- reconcile/terraform_tgw_attachments.py +20 -18
- reconcile/terraform_vpc_resources/integration.py +3 -1
- 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 +11 -11
- reconcile/typed_queries/status_board.py +2 -2
- reconcile/unleash_feature_toggles/integration.py +4 -2
- reconcile/utils/acs/base.py +6 -3
- reconcile/utils/acs/policies.py +2 -2
- reconcile/utils/aws_api.py +51 -20
- reconcile/utils/aws_api_typed/organization.py +4 -2
- reconcile/utils/deadmanssnitch_api.py +1 -1
- reconcile/utils/early_exit_cache.py +8 -10
- 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/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 +2 -5
- reconcile/utils/jobcontroller/controller.py +1 -1
- reconcile/utils/jobcontroller/models.py +17 -1
- reconcile/utils/json.py +39 -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 +1 -1
- reconcile/utils/merge_request_manager/parser.py +4 -4
- reconcile/utils/metrics.py +5 -5
- reconcile/utils/models.py +304 -82
- reconcile/utils/mr/notificator.py +1 -1
- reconcile/utils/mr/user_maintenance.py +3 -2
- reconcile/utils/oc.py +112 -92
- reconcile/utils/ocm/addons.py +0 -1
- reconcile/utils/ocm/base.py +17 -20
- reconcile/utils/ocm/cluster_groups.py +1 -1
- reconcile/utils/ocm/identity_providers.py +2 -2
- reconcile/utils/ocm/labels.py +1 -1
- reconcile/utils/ocm/products.py +8 -8
- reconcile/utils/ocm/service_log.py +1 -1
- reconcile/utils/ocm/sre_capability_labels.py +20 -13
- reconcile/utils/openshift_resource.py +5 -0
- reconcile/utils/pagerduty_api.py +5 -2
- reconcile/utils/promotion_state.py +6 -11
- reconcile/utils/raw_github_api.py +1 -1
- reconcile/utils/rhcsv2_certs.py +1 -4
- reconcile/utils/runtime/integration.py +1 -1
- reconcile/utils/saasherder/interfaces.py +13 -20
- reconcile/utils/saasherder/models.py +23 -20
- reconcile/utils/saasherder/saasherder.py +26 -17
- reconcile/utils/slack_api.py +2 -2
- reconcile/utils/structs.py +1 -1
- reconcile/utils/terraform_client.py +1 -1
- reconcile/utils/terrascript_aws_client.py +47 -43
- reconcile/utils/unleash/server.py +2 -8
- reconcile/utils/vault.py +4 -11
- reconcile/utils/vcs.py +8 -8
- reconcile/vault_replication.py +1 -1
- 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 +3 -1
- tools/qontract_cli.py +6 -5
- tools/template_validation.py +3 -1
- {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/entry_points.txt +0 -0
|
@@ -4,7 +4,7 @@ import logging
|
|
|
4
4
|
from typing import TYPE_CHECKING, Any
|
|
5
5
|
|
|
6
6
|
from deepdiff import DeepHash
|
|
7
|
-
from pydantic import
|
|
7
|
+
from pydantic import field_validator
|
|
8
8
|
|
|
9
9
|
from reconcile.aus.aus_label_source import (
|
|
10
10
|
init_aus_cluster_label_source,
|
|
@@ -62,7 +62,8 @@ class OcmLabelsIntegrationParams(PydanticRunParams):
|
|
|
62
62
|
managed_label_prefixes: list[str] = []
|
|
63
63
|
ignored_label_prefixes: list[str] = []
|
|
64
64
|
|
|
65
|
-
@
|
|
65
|
+
@field_validator("managed_label_prefixes", "ignored_label_prefixes")
|
|
66
|
+
@classmethod
|
|
66
67
|
def must_end_with_dot(cls, v: list[str]) -> list[str]:
|
|
67
68
|
return [prefix + "." if not prefix.endswith(".") else prefix for prefix in v]
|
|
68
69
|
|
reconcile/ocm_machine_pools.py
CHANGED
|
@@ -7,11 +7,7 @@ from collections.abc import Iterable, Mapping
|
|
|
7
7
|
from enum import Enum
|
|
8
8
|
from typing import Any, Self
|
|
9
9
|
|
|
10
|
-
from pydantic import
|
|
11
|
-
BaseModel,
|
|
12
|
-
Field,
|
|
13
|
-
root_validator,
|
|
14
|
-
)
|
|
10
|
+
from pydantic import BaseModel, Field, model_validator
|
|
15
11
|
|
|
16
12
|
from reconcile import queries
|
|
17
13
|
from reconcile.gql_definitions.common.clusters import (
|
|
@@ -22,6 +18,7 @@ from reconcile.gql_definitions.common.clusters import (
|
|
|
22
18
|
from reconcile.typed_queries.clusters import get_clusters
|
|
23
19
|
from reconcile.utils.differ import diff_mappings
|
|
24
20
|
from reconcile.utils.disabled_integrations import integration_is_enabled
|
|
21
|
+
from reconcile.utils.json import json_dumps
|
|
25
22
|
from reconcile.utils.ocm import (
|
|
26
23
|
DEFAULT_OCM_MACHINE_POOL_ID,
|
|
27
24
|
OCM,
|
|
@@ -61,7 +58,8 @@ class MachinePoolAutoscaling(AbstractAutoscaling):
|
|
|
61
58
|
min_replicas: int
|
|
62
59
|
max_replicas: int
|
|
63
60
|
|
|
64
|
-
@
|
|
61
|
+
@model_validator(mode="before")
|
|
62
|
+
@classmethod
|
|
65
63
|
def max_greater_min(cls, field_values: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
66
64
|
min_replicas = field_values.get("min_replicas")
|
|
67
65
|
max_replicas = field_values.get("max_replicas")
|
|
@@ -82,7 +80,8 @@ class NodePoolAutoscaling(AbstractAutoscaling):
|
|
|
82
80
|
min_replica: int
|
|
83
81
|
max_replica: int
|
|
84
82
|
|
|
85
|
-
@
|
|
83
|
+
@model_validator(mode="before")
|
|
84
|
+
@classmethod
|
|
86
85
|
def max_greater_min(cls, field_values: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
87
86
|
min_replica = field_values.get("min_replica")
|
|
88
87
|
max_replica = field_values.get("max_replica")
|
|
@@ -103,14 +102,15 @@ class AbstractPool(ABC, BaseModel):
|
|
|
103
102
|
# Abstract class for machine pools, to be implemented by OSD/HyperShift classes
|
|
104
103
|
|
|
105
104
|
id: str
|
|
106
|
-
replicas: int | None
|
|
107
|
-
taints: list[Mapping[str, str]] | None
|
|
108
|
-
labels: Mapping[str, str] | None
|
|
105
|
+
replicas: int | None = None
|
|
106
|
+
taints: list[Mapping[str, str]] | None = None
|
|
107
|
+
labels: Mapping[str, str] | None = None
|
|
109
108
|
cluster: str
|
|
110
109
|
cluster_type: ClusterType = Field(..., exclude=True)
|
|
111
|
-
autoscaling: AbstractAutoscaling | None
|
|
110
|
+
autoscaling: AbstractAutoscaling | None = None
|
|
112
111
|
|
|
113
|
-
@
|
|
112
|
+
@model_validator(mode="before")
|
|
113
|
+
@classmethod
|
|
114
114
|
def validate_scaling(cls, field_values: Mapping[str, Any]) -> Mapping[str, Any]:
|
|
115
115
|
if field_values.get("autoscaling") and field_values.get("replicas"):
|
|
116
116
|
raise ValueError("autoscaling and replicas are mutually exclusive")
|
|
@@ -154,13 +154,13 @@ class MachinePool(AbstractPool):
|
|
|
154
154
|
instance_type: str
|
|
155
155
|
|
|
156
156
|
def delete(self, ocm: OCM) -> None:
|
|
157
|
-
ocm.delete_machine_pool(self.cluster, self.
|
|
157
|
+
ocm.delete_machine_pool(self.cluster, self.model_dump(by_alias=True))
|
|
158
158
|
|
|
159
159
|
def create(self, ocm: OCM) -> None:
|
|
160
|
-
ocm.create_machine_pool(self.cluster, self.
|
|
160
|
+
ocm.create_machine_pool(self.cluster, self.model_dump(by_alias=True))
|
|
161
161
|
|
|
162
162
|
def update(self, ocm: OCM) -> None:
|
|
163
|
-
update_dict = self.
|
|
163
|
+
update_dict = self.model_dump(by_alias=True)
|
|
164
164
|
# can not update instance_type
|
|
165
165
|
del update_dict["instance_type"]
|
|
166
166
|
if not update_dict["labels"]:
|
|
@@ -214,7 +214,7 @@ class MachinePool(AbstractPool):
|
|
|
214
214
|
replicas=pool.replicas,
|
|
215
215
|
autoscaling=autoscaling,
|
|
216
216
|
instance_type=pool.instance_type,
|
|
217
|
-
taints=[p.
|
|
217
|
+
taints=[p.model_dump(by_alias=True) for p in pool.taints or []],
|
|
218
218
|
labels=pool.labels,
|
|
219
219
|
cluster=cluster,
|
|
220
220
|
cluster_type=cluster_type,
|
|
@@ -232,14 +232,14 @@ class NodePool(AbstractPool):
|
|
|
232
232
|
subnet: str | None
|
|
233
233
|
|
|
234
234
|
def delete(self, ocm: OCM) -> None:
|
|
235
|
-
ocm.delete_node_pool(self.cluster, self.
|
|
235
|
+
ocm.delete_node_pool(self.cluster, self.model_dump(by_alias=True))
|
|
236
236
|
|
|
237
237
|
def create(self, ocm: OCM) -> None:
|
|
238
|
-
spec = self.
|
|
238
|
+
spec = self.model_dump(by_alias=True)
|
|
239
239
|
ocm.create_node_pool(self.cluster, spec)
|
|
240
240
|
|
|
241
241
|
def update(self, ocm: OCM) -> None:
|
|
242
|
-
update_dict = self.
|
|
242
|
+
update_dict = self.model_dump(by_alias=True)
|
|
243
243
|
# can not update instance_type
|
|
244
244
|
del update_dict["aws_node_pool"]
|
|
245
245
|
# can not update subnet
|
|
@@ -297,7 +297,7 @@ class NodePool(AbstractPool):
|
|
|
297
297
|
aws_node_pool=AWSNodePool(
|
|
298
298
|
instance_type=pool.instance_type,
|
|
299
299
|
),
|
|
300
|
-
taints=[p.
|
|
300
|
+
taints=[p.model_dump(by_alias=True) for p in pool.taints or []],
|
|
301
301
|
labels=pool.labels,
|
|
302
302
|
subnet=pool.subnet,
|
|
303
303
|
cluster=cluster,
|
|
@@ -312,7 +312,7 @@ class PoolHandler(BaseModel):
|
|
|
312
312
|
pool: AbstractPool
|
|
313
313
|
|
|
314
314
|
def act(self, dry_run: bool, ocm: OCM) -> None:
|
|
315
|
-
logging.info(f"{self.action} {self.pool.
|
|
315
|
+
logging.info(f"{self.action} {self.pool.model_dump(by_alias=True)}")
|
|
316
316
|
if dry_run:
|
|
317
317
|
return
|
|
318
318
|
|
|
@@ -469,7 +469,7 @@ def calculate_diff(
|
|
|
469
469
|
if invalid_diff:
|
|
470
470
|
errors.append(
|
|
471
471
|
InvalidUpdateError(
|
|
472
|
-
f"can not update {invalid_diff} for existing machine pool on cluster {cluster_name}, CURRENT: {diff_pair.current
|
|
472
|
+
f"can not update {invalid_diff} for existing machine pool on cluster {cluster_name}, CURRENT: {json_dumps(diff_pair.current)}, DESIRED: {json_dumps(diff_pair.desired)}"
|
|
473
473
|
)
|
|
474
474
|
)
|
|
475
475
|
else:
|
|
@@ -531,7 +531,7 @@ def run(dry_run: bool) -> None:
|
|
|
531
531
|
|
|
532
532
|
settings = queries.get_app_interface_settings()
|
|
533
533
|
cluster_like_objects = [
|
|
534
|
-
cluster.
|
|
534
|
+
cluster.model_dump(by_alias=True) for cluster in filtered_clusters
|
|
535
535
|
]
|
|
536
536
|
ocm_map = OCMMap(
|
|
537
537
|
clusters=cluster_like_objects,
|
reconcile/openshift_base.py
CHANGED
|
@@ -30,9 +30,11 @@ from reconcile.utils import (
|
|
|
30
30
|
)
|
|
31
31
|
from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
|
|
32
32
|
from reconcile.utils.oc import (
|
|
33
|
+
AmbiguousResourceTypeError,
|
|
33
34
|
DeploymentFieldIsImmutableError,
|
|
34
35
|
FieldIsImmutableError,
|
|
35
36
|
InvalidValueApplyError,
|
|
37
|
+
KindNotFoundError,
|
|
36
38
|
MayNotChangeOnceSetError,
|
|
37
39
|
MetaDataAnnotationsTooLongApplyError,
|
|
38
40
|
OC_Map,
|
|
@@ -128,6 +130,29 @@ class ClusterMap(Protocol):
|
|
|
128
130
|
) -> list[str]: ...
|
|
129
131
|
|
|
130
132
|
|
|
133
|
+
def validate_managed_resource_types(
|
|
134
|
+
oc: OCCli,
|
|
135
|
+
managed_resource_types: Iterable[str],
|
|
136
|
+
managed_resource_names: Iterable[Mapping[str, Any]],
|
|
137
|
+
cluster_scope_resource_validation: bool,
|
|
138
|
+
) -> None:
|
|
139
|
+
"""Validate the managed resource types."""
|
|
140
|
+
managed_resources = [
|
|
141
|
+
managed_resource_name["resource"]
|
|
142
|
+
for managed_resource_name in managed_resource_names
|
|
143
|
+
]
|
|
144
|
+
for managed_resource_type in managed_resource_types:
|
|
145
|
+
# The k8s kind must be supported by the cluster
|
|
146
|
+
resource = oc.get_api_resource(managed_resource_type)
|
|
147
|
+
|
|
148
|
+
if cluster_scope_resource_validation and not resource.namespaced:
|
|
149
|
+
# cluster-scoped resources must be use managedResourceNames!
|
|
150
|
+
if managed_resource_type not in managed_resources:
|
|
151
|
+
raise ValidationError(
|
|
152
|
+
f"Cluster-scoped resource {managed_resource_type} must be managed by name only. Please use 'managedResourceNames' field to specify the names of the resources to manage."
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
131
156
|
def init_specs_to_fetch(
|
|
132
157
|
ri: ResourceInventory,
|
|
133
158
|
oc_map: ClusterMap,
|
|
@@ -136,6 +161,7 @@ def init_specs_to_fetch(
|
|
|
136
161
|
override_managed_types: Iterable[str] | None = None,
|
|
137
162
|
managed_types_key: str = "managedResourceTypes",
|
|
138
163
|
cluster_admin: bool = False,
|
|
164
|
+
cluster_scope_resource_validation: bool = False,
|
|
139
165
|
) -> list[StateSpec]:
|
|
140
166
|
state_specs: list[StateSpec] = []
|
|
141
167
|
|
|
@@ -163,9 +189,27 @@ def init_specs_to_fetch(
|
|
|
163
189
|
logging.log(level=ex.log_level, msg=ex.message)
|
|
164
190
|
continue
|
|
165
191
|
|
|
192
|
+
managed_resource_names = namespace_info.get("managedResourceNames") or []
|
|
193
|
+
try:
|
|
194
|
+
validate_managed_resource_types(
|
|
195
|
+
oc,
|
|
196
|
+
managed_types,
|
|
197
|
+
managed_resource_names,
|
|
198
|
+
cluster_scope_resource_validation=cluster_scope_resource_validation,
|
|
199
|
+
)
|
|
200
|
+
except KindNotFoundError:
|
|
201
|
+
# We must allow kinds that are not supported by the cluster because:
|
|
202
|
+
# 1. We install CRD with an operator in the same MR
|
|
203
|
+
# 2. SAAS files initialize the namespace objects with managedResourceTypes from the SAAS file
|
|
204
|
+
# and we can't expect that all of those are valid for all clusters
|
|
205
|
+
pass
|
|
206
|
+
except (AmbiguousResourceTypeError, ValidationError) as e:
|
|
207
|
+
ri.register_error()
|
|
208
|
+
logging.error(f"[{cluster}/{namespace_info['name']}] {e}")
|
|
209
|
+
continue
|
|
210
|
+
|
|
166
211
|
namespace = namespace_info["name"]
|
|
167
212
|
# These may exit but have a value of None
|
|
168
|
-
managed_resource_names = namespace_info.get("managedResourceNames") or []
|
|
169
213
|
managed_resource_type_overrides = (
|
|
170
214
|
namespace_info.get("managedResourceTypeOverrides") or []
|
|
171
215
|
)
|
|
@@ -340,6 +384,7 @@ def fetch_current_state(
|
|
|
340
384
|
cluster_admin: bool = False,
|
|
341
385
|
caller: str | None = None,
|
|
342
386
|
init_projects: bool = False,
|
|
387
|
+
cluster_scope_resource_validation: bool = False,
|
|
343
388
|
) -> tuple[ResourceInventory, OC_Map]:
|
|
344
389
|
ri = ResourceInventory()
|
|
345
390
|
settings = queries.get_app_interface_settings()
|
|
@@ -362,6 +407,7 @@ def fetch_current_state(
|
|
|
362
407
|
clusters=clusters,
|
|
363
408
|
override_managed_types=override_managed_types,
|
|
364
409
|
cluster_admin=cluster_admin,
|
|
410
|
+
cluster_scope_resource_validation=cluster_scope_resource_validation,
|
|
365
411
|
)
|
|
366
412
|
threaded.run(
|
|
367
413
|
populate_current_state,
|
|
@@ -1363,6 +1409,11 @@ class HasOpenShiftResources(Protocol):
|
|
|
1363
1409
|
openshift_resources: list | None
|
|
1364
1410
|
|
|
1365
1411
|
|
|
1412
|
+
@runtime_checkable
|
|
1413
|
+
class HasOpenShiftResourcesRequired(Protocol):
|
|
1414
|
+
openshift_resources: list
|
|
1415
|
+
|
|
1416
|
+
|
|
1366
1417
|
@runtime_checkable
|
|
1367
1418
|
class HasOpenshiftServiceAccountTokens(Protocol):
|
|
1368
1419
|
openshift_service_account_tokens: list | None
|
|
@@ -1371,7 +1422,7 @@ class HasOpenshiftServiceAccountTokens(Protocol):
|
|
|
1371
1422
|
@runtime_checkable
|
|
1372
1423
|
class HasSharedResourcesOpenShiftResources(Protocol):
|
|
1373
1424
|
@property
|
|
1374
|
-
def shared_resources(self) -> Sequence[
|
|
1425
|
+
def shared_resources(self) -> Sequence[HasOpenShiftResourcesRequired] | None: ...
|
|
1375
1426
|
|
|
1376
1427
|
|
|
1377
1428
|
@runtime_checkable
|
|
@@ -301,7 +301,7 @@ def filter_clusters(clusters: list[ClusterV1]) -> list[ClusterV1]:
|
|
|
301
301
|
|
|
302
302
|
def get_ocm_map(clusters: list[ClusterV1]) -> OCMMap:
|
|
303
303
|
settings = queries.get_app_interface_settings()
|
|
304
|
-
clusters_info = [c.
|
|
304
|
+
clusters_info = [c.model_dump(by_alias=True) for c in clusters]
|
|
305
305
|
return OCMMap(
|
|
306
306
|
settings=settings,
|
|
307
307
|
clusters=clusters_info,
|
|
@@ -229,7 +229,7 @@ def get_gql_namespaces_in_shard() -> list[NamespaceV1]:
|
|
|
229
229
|
return [
|
|
230
230
|
ns
|
|
231
231
|
for ns in all_namespaces
|
|
232
|
-
if not ob.is_namespace_deleted(ns.
|
|
232
|
+
if not ob.is_namespace_deleted(ns.model_dump(by_alias=True))
|
|
233
233
|
and is_in_shard(f"{ns.cluster.name}/{ns.name}")
|
|
234
234
|
]
|
|
235
235
|
|
|
@@ -1,25 +1,27 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
import
|
|
2
|
+
from collections import defaultdict
|
|
3
3
|
from collections.abc import (
|
|
4
4
|
Callable,
|
|
5
5
|
Iterable,
|
|
6
|
-
Mapping,
|
|
7
6
|
Sequence,
|
|
8
7
|
)
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from enum import StrEnum
|
|
9
10
|
from typing import Any
|
|
10
11
|
|
|
11
12
|
from sretoolbox.utils import threaded
|
|
12
13
|
|
|
13
14
|
import reconcile.openshift_base as ob
|
|
14
15
|
from reconcile.gql_definitions.common.namespaces_minimal import NamespaceV1
|
|
15
|
-
from reconcile.status import ExitCodes
|
|
16
16
|
from reconcile.typed_queries.app_interface_vault_settings import (
|
|
17
17
|
get_app_interface_vault_settings,
|
|
18
18
|
)
|
|
19
19
|
from reconcile.typed_queries.namespaces_minimal import get_namespaces_minimal
|
|
20
20
|
from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
|
|
21
21
|
from reconcile.utils.defer import defer
|
|
22
|
-
from reconcile.utils.oc_filters import
|
|
22
|
+
from reconcile.utils.oc_filters import (
|
|
23
|
+
filter_namespaces_by_cluster_and_namespace,
|
|
24
|
+
)
|
|
23
25
|
from reconcile.utils.oc_map import (
|
|
24
26
|
OCLogMsg,
|
|
25
27
|
OCMap,
|
|
@@ -30,113 +32,112 @@ from reconcile.utils.sharding import is_in_shard
|
|
|
30
32
|
|
|
31
33
|
QONTRACT_INTEGRATION = "openshift-namespaces"
|
|
32
34
|
|
|
33
|
-
NS_STATE_PRESENT = "present"
|
|
34
|
-
NS_STATE_ABSENT = "absent"
|
|
35
|
-
|
|
36
|
-
NS_ACTION_CREATE = "create"
|
|
37
|
-
NS_ACTION_DELETE = "delete"
|
|
38
35
|
|
|
36
|
+
class Action(StrEnum):
|
|
37
|
+
CREATE = "create"
|
|
38
|
+
DELETE = "delete"
|
|
39
39
|
|
|
40
|
-
DUPLICATES_LOG_MSG = "Found multiple definitions for the namespace {key}"
|
|
41
40
|
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class DesiredState:
|
|
43
|
+
cluster: str
|
|
44
|
+
namespace: str
|
|
45
|
+
delete: bool
|
|
42
46
|
|
|
43
|
-
def get_desired_state(namespaces: Iterable[NamespaceV1]) -> list[dict[str, str]]:
|
|
44
|
-
desired_state: list[dict[str, str]] = []
|
|
45
|
-
for ns in namespaces:
|
|
46
|
-
state = NS_STATE_PRESENT
|
|
47
|
-
if ns.delete:
|
|
48
|
-
state = NS_STATE_ABSENT
|
|
49
47
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
"namespace": ns.name,
|
|
53
|
-
"desired_state": state,
|
|
54
|
-
})
|
|
48
|
+
class NamespaceDuplicateError(Exception):
|
|
49
|
+
pass
|
|
55
50
|
|
|
56
|
-
return desired_state
|
|
57
51
|
|
|
52
|
+
def get_namespaces(
|
|
53
|
+
cluster_name: Sequence[str] | None,
|
|
54
|
+
namespace_name: Sequence[str] | None,
|
|
55
|
+
) -> tuple[list[NamespaceV1], list[NamespaceDuplicateError]]:
|
|
56
|
+
all_namespaces = get_namespaces_minimal()
|
|
58
57
|
|
|
59
|
-
|
|
60
|
-
namespaces: Iterable[NamespaceV1],
|
|
61
|
-
) -> tuple[list[NamespaceV1], bool]:
|
|
62
|
-
# Structure holding duplicates by namespace key
|
|
63
|
-
duplicates: dict[str, list[NamespaceV1]] = {}
|
|
64
|
-
# namespace filtered list without duplicates
|
|
65
|
-
filtered_ns: dict[str, NamespaceV1] = {}
|
|
66
|
-
|
|
67
|
-
err = False
|
|
68
|
-
for ns in namespaces:
|
|
69
|
-
key = f"{ns.cluster.name}/{ns.name}"
|
|
58
|
+
namespaces_by_shard_key = defaultdict(list)
|
|
70
59
|
|
|
60
|
+
for namespace in all_namespaces:
|
|
61
|
+
key = f"{namespace.cluster.name}/{namespace.name}"
|
|
71
62
|
if is_in_shard(key):
|
|
72
|
-
|
|
73
|
-
filtered_ns[key] = ns
|
|
74
|
-
else:
|
|
75
|
-
# Duplicated NS
|
|
76
|
-
dupe_list_by_key = duplicates.setdefault(key, [])
|
|
77
|
-
dupe_list_by_key.append(ns)
|
|
78
|
-
|
|
79
|
-
for key, dupe_list in duplicates.items():
|
|
80
|
-
dupe_list.append(filtered_ns[key])
|
|
81
|
-
delete_flags = (
|
|
82
|
-
[ns.delete for ns in dupe_list_by_key] if dupe_list_by_key else []
|
|
83
|
-
)
|
|
63
|
+
namespaces_by_shard_key[key].append(namespace)
|
|
84
64
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
65
|
+
managed_namespaces = []
|
|
66
|
+
duplicate_errors = []
|
|
67
|
+
|
|
68
|
+
for key, namespaces in namespaces_by_shard_key.items():
|
|
69
|
+
if len(namespaces) == 1:
|
|
70
|
+
namespace = namespaces[0]
|
|
71
|
+
if not namespace.managed_by_external:
|
|
72
|
+
managed_namespaces.append(namespace)
|
|
92
73
|
else:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
logging.
|
|
74
|
+
msg = f"Found multiple definitions for the namespace {key}"
|
|
75
|
+
duplicate_errors.append(NamespaceDuplicateError(msg))
|
|
76
|
+
logging.error(msg)
|
|
77
|
+
|
|
78
|
+
namespaces = filter_namespaces_by_cluster_and_namespace(
|
|
79
|
+
namespaces=managed_namespaces,
|
|
80
|
+
cluster_names=cluster_name,
|
|
81
|
+
namespace_names=namespace_name,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
return namespaces, duplicate_errors
|
|
85
|
+
|
|
96
86
|
|
|
97
|
-
|
|
87
|
+
def build_desired_state(
|
|
88
|
+
namespaces: Iterable[NamespaceV1],
|
|
89
|
+
) -> list[DesiredState]:
|
|
90
|
+
return [
|
|
91
|
+
DesiredState(
|
|
92
|
+
cluster=namespace.cluster.name,
|
|
93
|
+
namespace=namespace.name,
|
|
94
|
+
delete=namespace.delete or False,
|
|
95
|
+
)
|
|
96
|
+
for namespace in namespaces
|
|
97
|
+
]
|
|
98
98
|
|
|
99
99
|
|
|
100
|
-
def
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
def manage_namespace(
|
|
101
|
+
desired_state: DesiredState,
|
|
102
|
+
oc_map: OCMap,
|
|
103
|
+
dry_run: bool,
|
|
104
|
+
) -> None:
|
|
105
|
+
namespace = desired_state.namespace
|
|
104
106
|
|
|
105
|
-
oc = oc_map.get(cluster)
|
|
107
|
+
oc = oc_map.get(desired_state.cluster)
|
|
106
108
|
if isinstance(oc, OCLogMsg):
|
|
107
109
|
logging.log(level=oc.log_level, msg=oc.message)
|
|
108
|
-
return
|
|
110
|
+
return
|
|
109
111
|
|
|
110
|
-
|
|
112
|
+
current_delete = not oc.project_exists(namespace)
|
|
111
113
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if not exists and desired_state == NS_STATE_PRESENT:
|
|
115
|
-
if namespace.startswith("openshift-"):
|
|
116
|
-
raise ValueError('cannot request a project starting with "openshift-"')
|
|
117
|
-
action = NS_ACTION_CREATE
|
|
118
|
-
elif exists and desired_state == NS_STATE_ABSENT:
|
|
119
|
-
action = NS_ACTION_DELETE
|
|
114
|
+
if desired_state.delete == current_delete:
|
|
115
|
+
return
|
|
120
116
|
|
|
121
|
-
if
|
|
122
|
-
logging.info([action, cluster, namespace])
|
|
123
|
-
if not dry_run:
|
|
124
|
-
act[action](namespace)
|
|
117
|
+
action = Action.DELETE if desired_state.delete else Action.CREATE
|
|
125
118
|
|
|
119
|
+
if namespace.startswith("openshift-"):
|
|
120
|
+
raise ValueError(f'cannot {action} a project starting with "openshift-"')
|
|
126
121
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
122
|
+
logging.info([str(action), desired_state.cluster, namespace])
|
|
123
|
+
if not dry_run:
|
|
124
|
+
match action:
|
|
125
|
+
case Action.CREATE:
|
|
126
|
+
oc.new_project(namespace)
|
|
127
|
+
case Action.DELETE:
|
|
128
|
+
oc.delete_project(namespace)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def build_runtime_errors(
|
|
132
|
+
desired_state: Iterable[DesiredState],
|
|
133
|
+
results: Iterable[Any],
|
|
134
|
+
) -> list[Exception]:
|
|
135
|
+
exceptions = []
|
|
131
136
|
for s, e in zip(desired_state, results, strict=False):
|
|
132
137
|
if isinstance(e, Exception):
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
f"exception: {e!s}"
|
|
137
|
-
)
|
|
138
|
-
logging.error(msg)
|
|
139
|
-
return err
|
|
138
|
+
e.add_note(f"cluster: {s.cluster}, namespace: {s.namespace}")
|
|
139
|
+
exceptions.append(e)
|
|
140
|
+
return exceptions
|
|
140
141
|
|
|
141
142
|
|
|
142
143
|
@defer
|
|
@@ -149,15 +150,8 @@ def run(
|
|
|
149
150
|
namespace_name: Sequence[str] | None = None,
|
|
150
151
|
defer: Callable | None = None,
|
|
151
152
|
) -> None:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
namespaces = filter_namespaces_by_cluster_and_namespace(
|
|
155
|
-
namespaces=shard_namespaces,
|
|
156
|
-
cluster_names=cluster_name,
|
|
157
|
-
namespace_names=namespace_name,
|
|
158
|
-
)
|
|
159
|
-
|
|
160
|
-
desired_state = get_desired_state(namespaces)
|
|
153
|
+
namespaces, duplicate_errors = get_namespaces(cluster_name, namespace_name)
|
|
154
|
+
desired_state = build_desired_state(namespaces)
|
|
161
155
|
|
|
162
156
|
vault_settings = get_app_interface_vault_settings()
|
|
163
157
|
secret_reader = create_secret_reader(use_vault=vault_settings.vault)
|
|
@@ -171,16 +165,17 @@ def run(
|
|
|
171
165
|
thread_pool_size=thread_pool_size,
|
|
172
166
|
init_projects=True,
|
|
173
167
|
)
|
|
174
|
-
|
|
175
168
|
if defer:
|
|
176
169
|
defer(oc_map.cleanup)
|
|
177
170
|
|
|
178
171
|
ob.publish_cluster_desired_metrics_from_state(
|
|
179
|
-
desired_state,
|
|
172
|
+
state=({"cluster": s.cluster} for s in desired_state),
|
|
173
|
+
integration=QONTRACT_INTEGRATION,
|
|
174
|
+
kind="Namespace",
|
|
180
175
|
)
|
|
181
176
|
|
|
182
177
|
results = threaded.run(
|
|
183
|
-
|
|
178
|
+
manage_namespace,
|
|
184
179
|
desired_state,
|
|
185
180
|
thread_pool_size,
|
|
186
181
|
return_exceptions=True,
|
|
@@ -188,6 +183,7 @@ def run(
|
|
|
188
183
|
oc_map=oc_map,
|
|
189
184
|
)
|
|
190
185
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
186
|
+
runtime_errors = build_runtime_errors(desired_state, results)
|
|
187
|
+
errors = runtime_errors + duplicate_errors
|
|
188
|
+
if errors:
|
|
189
|
+
raise ExceptionGroup("Reconcile errors occurred", errors)
|
|
@@ -806,7 +806,11 @@ def fetch_data(
|
|
|
806
806
|
init_api_resources=init_api_resources,
|
|
807
807
|
)
|
|
808
808
|
state_specs = ob.init_specs_to_fetch(
|
|
809
|
-
ri,
|
|
809
|
+
ri,
|
|
810
|
+
oc_map,
|
|
811
|
+
namespaces=namespaces,
|
|
812
|
+
override_managed_types=overrides,
|
|
813
|
+
cluster_scope_resource_validation=True,
|
|
810
814
|
)
|
|
811
815
|
threaded.run(fetch_states, state_specs, thread_pool_size, ri=ri, settings=settings)
|
|
812
816
|
|
|
@@ -861,7 +865,7 @@ def canonicalize_namespaces(
|
|
|
861
865
|
elif providers[0] == "route":
|
|
862
866
|
override = ["Route"]
|
|
863
867
|
elif providers[0] == "prometheus-rule":
|
|
864
|
-
override = ["PrometheusRule"]
|
|
868
|
+
override = ["PrometheusRule.monitoring.coreos.com"]
|
|
865
869
|
|
|
866
870
|
namespace_info["openshiftResources"] = ors
|
|
867
871
|
canonicalized_namespaces.append(namespace_info)
|
|
@@ -77,7 +77,7 @@ def get_namespaces_with_rhcs_certs(
|
|
|
77
77
|
) -> list[NamespaceV1]:
|
|
78
78
|
result: list[NamespaceV1] = []
|
|
79
79
|
for ns in rhcs_certs_query(query_func=query_func).namespaces or []:
|
|
80
|
-
ob.aggregate_shared_resources_typed(
|
|
80
|
+
ob.aggregate_shared_resources_typed(ns)
|
|
81
81
|
if (
|
|
82
82
|
integration_is_enabled(QONTRACT_INTEGRATION, ns.cluster)
|
|
83
83
|
and not bool(ns.delete)
|
|
@@ -149,7 +149,7 @@ def generate_vault_cert_secret(
|
|
|
149
149
|
logging.info(
|
|
150
150
|
f"Creating cert with service account credentials for '{cert_resource.service_account_name}'. cluster='{ns.cluster.name}', namespace='{ns.name}', secret='{cert_resource.secret_name}'"
|
|
151
151
|
)
|
|
152
|
-
sa_password = vault.read(cert_resource.service_account_password.
|
|
152
|
+
sa_password = vault.read(cert_resource.service_account_password.model_dump())
|
|
153
153
|
if dry_run:
|
|
154
154
|
rhcs_cert = RhcsV2Cert(
|
|
155
155
|
certificate="PLACEHOLDER_CERT",
|
|
@@ -171,12 +171,12 @@ def generate_vault_cert_secret(
|
|
|
171
171
|
)
|
|
172
172
|
vault.write(
|
|
173
173
|
secret={
|
|
174
|
-
"data": rhcs_cert.
|
|
174
|
+
"data": rhcs_cert.model_dump(by_alias=True),
|
|
175
175
|
"path": f"{vault_base_path}/{ns.cluster.name}/{ns.name}/{cert_resource.secret_name}",
|
|
176
176
|
},
|
|
177
177
|
decode_base64=False,
|
|
178
178
|
)
|
|
179
|
-
return rhcs_cert.
|
|
179
|
+
return rhcs_cert.model_dump(by_alias=True)
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
def fetch_openshift_resource_for_cert_resource(
|
|
@@ -277,7 +277,7 @@ def run(
|
|
|
277
277
|
state_specs = ob.init_specs_to_fetch(
|
|
278
278
|
ri,
|
|
279
279
|
oc_map,
|
|
280
|
-
namespaces=[ns.
|
|
280
|
+
namespaces=[ns.model_dump(by_alias=True) for ns in namespaces],
|
|
281
281
|
override_managed_types=["Secret"],
|
|
282
282
|
)
|
|
283
283
|
for spec in state_specs:
|