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
reconcile/utils/oc.py
CHANGED
|
@@ -68,7 +68,8 @@ if TYPE_CHECKING:
|
|
|
68
68
|
urllib3.disable_warnings()
|
|
69
69
|
|
|
70
70
|
GET_REPLICASET_MAX_ATTEMPTS = 20
|
|
71
|
-
|
|
71
|
+
DEFAULT_GROUP = ""
|
|
72
|
+
PROJECT_KIND = "Project.project.openshift.io"
|
|
72
73
|
|
|
73
74
|
oc_run_execution_counter = Counter(
|
|
74
75
|
name="oc_run_execution_counter",
|
|
@@ -145,6 +146,14 @@ class RequestEntityTooLargeError(Exception):
|
|
|
145
146
|
pass
|
|
146
147
|
|
|
147
148
|
|
|
149
|
+
class KindNotFoundError(Exception):
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class AmbiguousResourceTypeError(Exception):
|
|
154
|
+
pass
|
|
155
|
+
|
|
156
|
+
|
|
148
157
|
class OCDecorators:
|
|
149
158
|
@classmethod
|
|
150
159
|
def process_reconcile_time(cls, function: Callable) -> Callable:
|
|
@@ -380,10 +389,7 @@ class OCCli:
|
|
|
380
389
|
|
|
381
390
|
self.init_projects = init_projects
|
|
382
391
|
if self.init_projects:
|
|
383
|
-
if self.is_kind_supported("
|
|
384
|
-
kind = "Project.project.openshift.io"
|
|
385
|
-
else:
|
|
386
|
-
kind = "Namespace"
|
|
392
|
+
kind = PROJECT_KIND if self.is_kind_supported(PROJECT_KIND) else "Namespace"
|
|
387
393
|
self.projects = {p["metadata"]["name"] for p in self.get_all(kind)["items"]}
|
|
388
394
|
|
|
389
395
|
self.slow_oc_reconcile_threshold = float(
|
|
@@ -453,10 +459,7 @@ class OCCli:
|
|
|
453
459
|
|
|
454
460
|
self.init_projects = init_projects
|
|
455
461
|
if self.init_projects:
|
|
456
|
-
if self.is_kind_supported("
|
|
457
|
-
kind = "Project.project.openshift.io"
|
|
458
|
-
else:
|
|
459
|
-
kind = "Namespace"
|
|
462
|
+
kind = PROJECT_KIND if self.is_kind_supported(PROJECT_KIND) else "Namespace"
|
|
460
463
|
self.projects = {p["metadata"]["name"] for p in self.get_all(kind)["items"]}
|
|
461
464
|
|
|
462
465
|
self.slow_oc_reconcile_threshold = float(
|
|
@@ -637,11 +640,9 @@ class OCCli:
|
|
|
637
640
|
def project_exists(self, name: str) -> bool:
|
|
638
641
|
if name in self.projects:
|
|
639
642
|
return True
|
|
643
|
+
kind = PROJECT_KIND if self.is_kind_supported(PROJECT_KIND) else "Namespace"
|
|
640
644
|
try:
|
|
641
|
-
|
|
642
|
-
self.get(None, "Project.project.openshift.io", name)
|
|
643
|
-
else:
|
|
644
|
-
self.get(None, "Namespace", name)
|
|
645
|
+
self.get(None, kind, name)
|
|
645
646
|
except StatusCodeError as e:
|
|
646
647
|
if "NotFound" in str(e):
|
|
647
648
|
return False
|
|
@@ -650,7 +651,7 @@ class OCCli:
|
|
|
650
651
|
|
|
651
652
|
@OCDecorators.process_reconcile_time
|
|
652
653
|
def new_project(self, namespace: str) -> OCProcessReconcileTimeDecoratorMsg:
|
|
653
|
-
if self.is_kind_supported(
|
|
654
|
+
if self.is_kind_supported(PROJECT_KIND):
|
|
654
655
|
cmd = ["new-project", namespace]
|
|
655
656
|
else:
|
|
656
657
|
cmd = ["create", "namespace", namespace]
|
|
@@ -666,7 +667,7 @@ class OCCli:
|
|
|
666
667
|
|
|
667
668
|
@OCDecorators.process_reconcile_time
|
|
668
669
|
def delete_project(self, namespace: str) -> OCProcessReconcileTimeDecoratorMsg:
|
|
669
|
-
if self.is_kind_supported(
|
|
670
|
+
if self.is_kind_supported(PROJECT_KIND):
|
|
670
671
|
cmd = ["delete", "project", namespace]
|
|
671
672
|
else:
|
|
672
673
|
cmd = ["delete", "namespace", namespace]
|
|
@@ -715,9 +716,9 @@ class OCCli:
|
|
|
715
716
|
|
|
716
717
|
def sa_get_token(self, namespace: str, name: str) -> str:
|
|
717
718
|
cmd = ["sa", "-n", namespace, "get-token", name]
|
|
718
|
-
return self._run(cmd)
|
|
719
|
+
return self._run(cmd).decode("utf-8")
|
|
719
720
|
|
|
720
|
-
def get_api_resources(self) -> dict[str,
|
|
721
|
+
def get_api_resources(self) -> dict[str, list[OCCliApiResource]]:
|
|
721
722
|
with self.api_resources_lock:
|
|
722
723
|
if not self.api_resources:
|
|
723
724
|
cmd = ["api-resources", "--no-headers"]
|
|
@@ -1187,7 +1188,7 @@ class OCCli:
|
|
|
1187
1188
|
def _run_json(
|
|
1188
1189
|
self, cmd: list[str], allow_not_found: bool = False
|
|
1189
1190
|
) -> dict[str, Any]:
|
|
1190
|
-
out = self._run(cmd, allow_not_found=allow_not_found)
|
|
1191
|
+
out = self._run(cmd, allow_not_found=allow_not_found).decode("utf-8")
|
|
1191
1192
|
|
|
1192
1193
|
try:
|
|
1193
1194
|
out_json = json.loads(out)
|
|
@@ -1196,76 +1197,90 @@ class OCCli:
|
|
|
1196
1197
|
|
|
1197
1198
|
return out_json
|
|
1198
1199
|
|
|
1199
|
-
def
|
|
1200
|
-
|
|
1201
|
-
# the api resources initialization.
|
|
1202
|
-
if not self.api_resources:
|
|
1203
|
-
self.get_api_resources()
|
|
1200
|
+
def parse_kind(self, kind: str) -> tuple[str, str, str]:
|
|
1201
|
+
"""Parse a Kubernetes kind string into its components.
|
|
1204
1202
|
|
|
1205
|
-
|
|
1206
|
-
kind
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
else:
|
|
1210
|
-
raise StatusCodeError(f"{self.server}: {kind} does not exist")
|
|
1211
|
-
|
|
1212
|
-
# if a kind_group has more than 1 entry than the kind_name is in
|
|
1213
|
-
# the format kind.apigroup. Find the apigroup/version that matches
|
|
1214
|
-
# the apigroup passed with the kind_name
|
|
1215
|
-
if len(kind_group) > 1:
|
|
1216
|
-
apigroup_override = kind_group[1]
|
|
1217
|
-
find = False
|
|
1218
|
-
for gv in self.api_resources[kind]:
|
|
1219
|
-
if apigroup_override == gv.group:
|
|
1220
|
-
if not gv.group:
|
|
1221
|
-
group_version = gv.api_version
|
|
1222
|
-
else:
|
|
1223
|
-
group_version = f"{gv.group}/{gv.api_version}"
|
|
1224
|
-
find = True
|
|
1225
|
-
break
|
|
1203
|
+
Supports three formats:
|
|
1204
|
+
- kind
|
|
1205
|
+
- kind.group.whatever
|
|
1206
|
+
- kind.group.whatever/version
|
|
1226
1207
|
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1208
|
+
Args:
|
|
1209
|
+
kind: A Kubernetes kind string in one of the supported formats
|
|
1210
|
+
|
|
1211
|
+
Returns:
|
|
1212
|
+
Tuple of (kind, group, version) where missing parts are empty strings
|
|
1213
|
+
|
|
1214
|
+
Raises:
|
|
1215
|
+
ValueError: If the kind string format is invalid
|
|
1216
|
+
|
|
1217
|
+
Examples:
|
|
1218
|
+
>>> parse_kind_string("Deployment")
|
|
1219
|
+
('Deployment', '', '')
|
|
1220
|
+
>>> parse_kind_string("ClusterRoleBinding.rbac.authorization.k8s.io")
|
|
1221
|
+
('ClusterRoleBinding', 'rbac.authorization.k8s.io', '')
|
|
1222
|
+
>>> parse_kind_string("CustomResource.mygroup.example.com/v1")
|
|
1223
|
+
('CustomResource', 'mygroup.example.com', 'v1')
|
|
1224
|
+
"""
|
|
1225
|
+
pattern = r"^(?P<kind>[^./]+)(?:\.(?P<group>[^/]+))?(?:/(?P<version>.+))?$"
|
|
1226
|
+
match = re.match(pattern, kind)
|
|
1227
|
+
if not match:
|
|
1228
|
+
raise ValueError(f"Invalid kind string: {kind}")
|
|
1229
|
+
|
|
1230
|
+
kind = match.group("kind") or ""
|
|
1231
|
+
group = match.group("group") or DEFAULT_GROUP
|
|
1232
|
+
version = match.group("version") or ""
|
|
1233
|
+
|
|
1234
|
+
return kind, group, version
|
|
1232
1235
|
|
|
1233
1236
|
def is_kind_supported(self, kind: str) -> bool:
|
|
1234
|
-
|
|
1235
|
-
# the api resources initialization.
|
|
1236
|
-
if not self.api_resources:
|
|
1237
|
-
self.get_api_resources()
|
|
1237
|
+
"""Returns True if the given kind is supported by the cluster, False otherwise.
|
|
1238
1238
|
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
else:
|
|
1246
|
-
return kind in self.api_resources
|
|
1239
|
+
Kind can be either kind, kind.group or kind.group/version."""
|
|
1240
|
+
try:
|
|
1241
|
+
self.get_api_resource(kind)
|
|
1242
|
+
return True
|
|
1243
|
+
except KindNotFoundError:
|
|
1244
|
+
return False
|
|
1247
1245
|
|
|
1248
1246
|
def is_kind_namespaced(self, kind: str) -> bool:
|
|
1249
|
-
|
|
1250
|
-
|
|
1247
|
+
"""Returns True if the given kind is namespaced, False if it's cluster scoped.
|
|
1248
|
+
|
|
1249
|
+
Kind can be either kind, kind.group or kind.group/version."""
|
|
1250
|
+
return self.get_api_resource(kind).namespaced
|
|
1251
|
+
|
|
1252
|
+
def get_api_resource(self, kind: str) -> OCCliApiResource:
|
|
1253
|
+
"""Return the OCCliApiResource for the given resource type.
|
|
1254
|
+
|
|
1255
|
+
Resource type can be either kind, kind.group or kind.group/version.
|
|
1256
|
+
If kind is not unique, group must be specified."""
|
|
1257
|
+
|
|
1251
1258
|
if not self.api_resources:
|
|
1252
|
-
|
|
1259
|
+
raise RuntimeError("API resources not initialized")
|
|
1260
|
+
|
|
1261
|
+
kind, group, _ = self.parse_kind(kind)
|
|
1253
1262
|
|
|
1254
|
-
|
|
1255
|
-
|
|
1263
|
+
if not (resources := self.api_resources.get(kind)):
|
|
1264
|
+
# the kind not found at all
|
|
1265
|
+
raise KindNotFoundError(f"Unsupported resource type: {kind}")
|
|
1256
1266
|
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
if not kind_resources:
|
|
1260
|
-
raise StatusCodeError(f"Kind {kind} does not exist in the ApiServer")
|
|
1267
|
+
if len(resources) == 1 and group == DEFAULT_GROUP:
|
|
1268
|
+
return resources[0]
|
|
1261
1269
|
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1270
|
+
# get the resource with the specified group
|
|
1271
|
+
if resource := next((r for r in resources if r.group == group), None):
|
|
1272
|
+
return resource
|
|
1273
|
+
|
|
1274
|
+
# no resource with the specified group found
|
|
1275
|
+
if group == DEFAULT_GROUP:
|
|
1276
|
+
message = (
|
|
1277
|
+
f"Ambiguous resource type: {kind}. "
|
|
1278
|
+
"Please fully qualify it with its API group. E.g., ClusterRoleBinding -> ClusterRoleBinding.rbac.authorization.k8s.io"
|
|
1279
|
+
)
|
|
1280
|
+
raise AmbiguousResourceTypeError(message)
|
|
1281
|
+
|
|
1282
|
+
# group was specified but no matching resource found
|
|
1283
|
+
raise KindNotFoundError(f"Unsupported resource type: {kind}")
|
|
1269
1284
|
|
|
1270
1285
|
|
|
1271
1286
|
REQUEST_TIMEOUT = 60
|
|
@@ -1305,20 +1320,19 @@ class OCNative(OCCli):
|
|
|
1305
1320
|
|
|
1306
1321
|
server = connection_parameters.server_url
|
|
1307
1322
|
|
|
1308
|
-
if server:
|
|
1309
|
-
|
|
1310
|
-
self.api_resources = self.get_api_resources()
|
|
1323
|
+
if not server:
|
|
1324
|
+
raise Exception("Server name is required!")
|
|
1311
1325
|
|
|
1312
|
-
|
|
1313
|
-
raise Exception("
|
|
1326
|
+
if not token:
|
|
1327
|
+
raise Exception("Token is required!")
|
|
1328
|
+
|
|
1329
|
+
self.client = self._get_client(server, token)
|
|
1330
|
+
self.api_resources = self.get_api_resources()
|
|
1314
1331
|
|
|
1315
1332
|
self.projects = set()
|
|
1316
1333
|
self.init_projects = init_projects
|
|
1317
1334
|
if self.init_projects:
|
|
1318
|
-
if self.is_kind_supported("
|
|
1319
|
-
kind = "Project.project.openshift.io"
|
|
1320
|
-
else:
|
|
1321
|
-
kind = "Namespace"
|
|
1335
|
+
kind = PROJECT_KIND if self.is_kind_supported(PROJECT_KIND) else "Namespace"
|
|
1322
1336
|
self.projects = {p["metadata"]["name"] for p in self.get_all(kind)["items"]}
|
|
1323
1337
|
|
|
1324
1338
|
def __enter__(self) -> OCNative:
|
|
@@ -1368,8 +1382,10 @@ class OCNative(OCCli):
|
|
|
1368
1382
|
|
|
1369
1383
|
@retry(max_attempts=5, exceptions=(ServerTimeoutError))
|
|
1370
1384
|
def get_items(self, kind: str, **kwargs: Any) -> list[dict[str, Any]]:
|
|
1371
|
-
|
|
1372
|
-
obj_client = self._get_obj_client(
|
|
1385
|
+
resource = self.get_api_resource(kind)
|
|
1386
|
+
obj_client = self._get_obj_client(
|
|
1387
|
+
group_version=resource.group_version, kind=resource.kind
|
|
1388
|
+
)
|
|
1373
1389
|
|
|
1374
1390
|
namespace = ""
|
|
1375
1391
|
if "namespace" in kwargs:
|
|
@@ -1421,8 +1437,10 @@ class OCNative(OCCli):
|
|
|
1421
1437
|
name: str | None = None,
|
|
1422
1438
|
allow_not_found: bool = False,
|
|
1423
1439
|
) -> dict[str, Any]:
|
|
1424
|
-
|
|
1425
|
-
obj_client = self._get_obj_client(
|
|
1440
|
+
resource = self.get_api_resource(kind)
|
|
1441
|
+
obj_client = self._get_obj_client(
|
|
1442
|
+
group_version=resource.group_version, kind=resource.kind
|
|
1443
|
+
)
|
|
1426
1444
|
try:
|
|
1427
1445
|
obj = obj_client.get(
|
|
1428
1446
|
name=name,
|
|
@@ -1436,8 +1454,10 @@ class OCNative(OCCli):
|
|
|
1436
1454
|
raise StatusCodeError(f"[{self.server}]: {e}") from None
|
|
1437
1455
|
|
|
1438
1456
|
def get_all(self, kind: str, all_namespaces: bool = False) -> dict[str, Any]:
|
|
1439
|
-
|
|
1440
|
-
obj_client = self._get_obj_client(
|
|
1457
|
+
resource = self.get_api_resource(kind)
|
|
1458
|
+
obj_client = self._get_obj_client(
|
|
1459
|
+
group_version=resource.group_version, kind=resource.kind
|
|
1460
|
+
)
|
|
1441
1461
|
try:
|
|
1442
1462
|
return obj_client.get(_request_timeout=REQUEST_TIMEOUT).to_dict()
|
|
1443
1463
|
except NotFoundError as e:
|
reconcile/utils/ocm/addons.py
CHANGED
reconcile/utils/ocm/base.py
CHANGED
|
@@ -141,16 +141,16 @@ class OCMClusterAWSOperatorRole(BaseModel):
|
|
|
141
141
|
|
|
142
142
|
|
|
143
143
|
class OCMAWSSTS(OCMClusterFlag):
|
|
144
|
-
role_arn: str | None
|
|
145
|
-
support_role_arn: str | None
|
|
146
|
-
oidc_endpoint_url: str | None
|
|
147
|
-
operator_iam_roles: list[OCMClusterAWSOperatorRole] | None
|
|
148
|
-
instance_iam_roles: dict[str, str] | None
|
|
149
|
-
operator_role_prefix: str | None
|
|
144
|
+
role_arn: str | None = None
|
|
145
|
+
support_role_arn: str | None = None
|
|
146
|
+
oidc_endpoint_url: str | None = None
|
|
147
|
+
operator_iam_roles: list[OCMClusterAWSOperatorRole] | None = None
|
|
148
|
+
instance_iam_roles: dict[str, str] | None = None
|
|
149
|
+
operator_role_prefix: str | None = None
|
|
150
150
|
|
|
151
151
|
|
|
152
152
|
class OCMClusterAWSSettings(BaseModel):
|
|
153
|
-
sts: OCMAWSSTS | None
|
|
153
|
+
sts: OCMAWSSTS | None = None
|
|
154
154
|
|
|
155
155
|
@property
|
|
156
156
|
def sts_enabled(self) -> bool:
|
|
@@ -264,21 +264,21 @@ class OCMCluster(BaseModel):
|
|
|
264
264
|
product: OCMModelLink
|
|
265
265
|
identity_providers: OCMCollectionLink
|
|
266
266
|
|
|
267
|
-
aws: OCMClusterAWSSettings | None
|
|
267
|
+
aws: OCMClusterAWSSettings | None = None
|
|
268
268
|
|
|
269
269
|
version: OCMClusterVersion
|
|
270
270
|
|
|
271
271
|
hypershift: OCMClusterFlag
|
|
272
272
|
|
|
273
|
-
console: OCMClusterConsole | None
|
|
273
|
+
console: OCMClusterConsole | None = None
|
|
274
274
|
|
|
275
|
-
api: OCMClusterAPI | None
|
|
275
|
+
api: OCMClusterAPI | None = None
|
|
276
276
|
|
|
277
|
-
dns: OCMClusterDns | None
|
|
277
|
+
dns: OCMClusterDns | None = None
|
|
278
278
|
|
|
279
|
-
external_configuration: OCMExternalConfiguration | None
|
|
279
|
+
external_configuration: OCMExternalConfiguration | None = None
|
|
280
280
|
|
|
281
|
-
external_auth_config: OCMExternalAuthConfig | None
|
|
281
|
+
external_auth_config: OCMExternalAuthConfig | None = None
|
|
282
282
|
|
|
283
283
|
def minor_version(self) -> str:
|
|
284
284
|
version_info = parse_semver(self.version.raw_id)
|
|
@@ -570,15 +570,12 @@ class OCMOIdentityProviderGithub(OCMOIdentityProvider):
|
|
|
570
570
|
)
|
|
571
571
|
|
|
572
572
|
|
|
573
|
-
class OCMOIdentityProviderOidcOpenIdClaims(BaseModel):
|
|
573
|
+
class OCMOIdentityProviderOidcOpenIdClaims(BaseModel, frozen=True):
|
|
574
574
|
email: list[str]
|
|
575
575
|
name: list[str] = []
|
|
576
576
|
preferred_username: list[str]
|
|
577
577
|
groups: list[str] = []
|
|
578
578
|
|
|
579
|
-
class Config:
|
|
580
|
-
frozen = True
|
|
581
|
-
|
|
582
579
|
|
|
583
580
|
class OCMOIdentityProviderOidcOpenId(BaseModel):
|
|
584
581
|
client_id: str
|
|
@@ -618,11 +615,11 @@ class OCMAddonUpgradePolicy(BaseModel):
|
|
|
618
615
|
id: str
|
|
619
616
|
addon_id: str
|
|
620
617
|
cluster_id: str
|
|
621
|
-
next_run: str | None
|
|
622
|
-
schedule: str | None
|
|
618
|
+
next_run: str | None = None
|
|
619
|
+
schedule: str | None = None
|
|
623
620
|
schedule_type: str
|
|
624
621
|
version: str
|
|
625
|
-
state: str | None
|
|
622
|
+
state: str | None = None
|
|
626
623
|
|
|
627
624
|
|
|
628
625
|
def build_label_container(
|
|
@@ -42,7 +42,7 @@ def add_identity_provider(
|
|
|
42
42
|
)
|
|
43
43
|
ocm_api.post(
|
|
44
44
|
api_path=ocm_cluster.identity_providers.href,
|
|
45
|
-
data=idp.
|
|
45
|
+
data=idp.model_dump(by_alias=True, exclude_none=True),
|
|
46
46
|
)
|
|
47
47
|
|
|
48
48
|
|
|
@@ -55,7 +55,7 @@ def update_identity_provider(
|
|
|
55
55
|
raise ValueError(f"IDP {idp.name} does not have a href!")
|
|
56
56
|
ocm_api.patch(
|
|
57
57
|
api_path=idp.href,
|
|
58
|
-
data=idp.
|
|
58
|
+
data=idp.model_dump(by_alias=True, exclude_none=True, exclude={"name"}),
|
|
59
59
|
)
|
|
60
60
|
|
|
61
61
|
|
reconcile/utils/ocm/labels.py
CHANGED
|
@@ -159,7 +159,7 @@ def build_container_for_prefix(
|
|
|
159
159
|
|
|
160
160
|
return LabelContainer(
|
|
161
161
|
labels={
|
|
162
|
-
strip_prefix_if_needed(label.key): label.
|
|
162
|
+
strip_prefix_if_needed(label.key): label.model_copy(
|
|
163
163
|
update={"key": strip_prefix_if_needed(label.key)}
|
|
164
164
|
)
|
|
165
165
|
for label in container.labels.values()
|
reconcile/utils/ocm/products.py
CHANGED
|
@@ -178,11 +178,11 @@ class OCMProductOsd(OCMProduct):
|
|
|
178
178
|
],
|
|
179
179
|
provision_shard_id=provision_shard_id,
|
|
180
180
|
hypershift=cluster["hypershift"]["enabled"],
|
|
181
|
-
fips=cluster.get("fips"),
|
|
181
|
+
fips=cluster.get("fips") or False,
|
|
182
182
|
)
|
|
183
183
|
|
|
184
184
|
if not cluster["ccs"]["enabled"]:
|
|
185
|
-
cluster_spec_data = spec.
|
|
185
|
+
cluster_spec_data = spec.model_dump()
|
|
186
186
|
cluster_spec_data["storage"] = (
|
|
187
187
|
cluster["storage_quota"]["value"] // BYTES_IN_GIGABYTE
|
|
188
188
|
)
|
|
@@ -229,7 +229,7 @@ class OCMProductOsd(OCMProduct):
|
|
|
229
229
|
"compute_machine_type": {"id": default_machine_pool.instance_type},
|
|
230
230
|
}
|
|
231
231
|
if default_machine_pool.autoscale is not None:
|
|
232
|
-
spec["autoscale_compute"] = default_machine_pool.autoscale.
|
|
232
|
+
spec["autoscale_compute"] = default_machine_pool.autoscale.model_dump()
|
|
233
233
|
else:
|
|
234
234
|
spec["compute"] = default_machine_pool.replicas
|
|
235
235
|
return spec
|
|
@@ -259,7 +259,7 @@ class OCMProductOsd(OCMProduct):
|
|
|
259
259
|
if (duwm := cluster.spec.disable_user_workload_monitoring) is not None
|
|
260
260
|
else True
|
|
261
261
|
),
|
|
262
|
-
"fips":
|
|
262
|
+
"fips": cluster.spec.fips,
|
|
263
263
|
}
|
|
264
264
|
|
|
265
265
|
# Workaround to enable type checks.
|
|
@@ -429,7 +429,7 @@ class OCMProductRosa(OCMProduct):
|
|
|
429
429
|
subnet_ids=cluster["aws"].get("subnet_ids"),
|
|
430
430
|
availability_zones=cluster["nodes"].get("availability_zones"),
|
|
431
431
|
oidc_endpoint_url=oidc_endpoint_url,
|
|
432
|
-
fips=cluster.get("fips"),
|
|
432
|
+
fips=cluster.get("fips") or False,
|
|
433
433
|
)
|
|
434
434
|
|
|
435
435
|
machine_pools = [
|
|
@@ -474,7 +474,7 @@ class OCMProductRosa(OCMProduct):
|
|
|
474
474
|
"compute_machine_type": {"id": default_machine_pool.instance_type},
|
|
475
475
|
}
|
|
476
476
|
if default_machine_pool.autoscale is not None:
|
|
477
|
-
spec["autoscale_compute"] = default_machine_pool.autoscale.
|
|
477
|
+
spec["autoscale_compute"] = default_machine_pool.autoscale.model_dump()
|
|
478
478
|
else:
|
|
479
479
|
spec["compute"] = default_machine_pool.replicas
|
|
480
480
|
return spec
|
|
@@ -517,7 +517,7 @@ class OCMProductRosa(OCMProduct):
|
|
|
517
517
|
if (duwm := cluster.spec.disable_user_workload_monitoring) is not None
|
|
518
518
|
else True
|
|
519
519
|
),
|
|
520
|
-
"fips":
|
|
520
|
+
"fips": cluster.spec.fips,
|
|
521
521
|
}
|
|
522
522
|
|
|
523
523
|
provision_shard_id = cluster.spec.provision_shard_id
|
|
@@ -706,7 +706,7 @@ class OCMProductHypershift(OCMProduct):
|
|
|
706
706
|
availability_zones=cluster["nodes"].get("availability_zones"),
|
|
707
707
|
hypershift=cluster["hypershift"]["enabled"],
|
|
708
708
|
oidc_endpoint_url=oidc_endpoint_url,
|
|
709
|
-
fips=cluster.get("fips"),
|
|
709
|
+
fips=cluster.get("fips") or False,
|
|
710
710
|
)
|
|
711
711
|
|
|
712
712
|
network = OCMClusterNetwork(
|
|
@@ -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()
|
|
@@ -5,6 +5,7 @@ import base64
|
|
|
5
5
|
import contextlib
|
|
6
6
|
import copy
|
|
7
7
|
import hashlib
|
|
8
|
+
import logging
|
|
8
9
|
import re
|
|
9
10
|
from threading import Lock
|
|
10
11
|
from typing import TYPE_CHECKING, Any
|
|
@@ -601,6 +602,10 @@ class ResourceInventory:
|
|
|
601
602
|
resource: OpenshiftResource,
|
|
602
603
|
privileged: bool = False,
|
|
603
604
|
) -> None:
|
|
605
|
+
if cluster not in self._clusters:
|
|
606
|
+
logging.error(f"Cluster {cluster} not initialized in ResourceInventory")
|
|
607
|
+
return
|
|
608
|
+
|
|
604
609
|
if resource.kind_and_group in self._clusters[cluster][namespace]:
|
|
605
610
|
kind = resource.kind_and_group
|
|
606
611
|
else:
|
reconcile/utils/pagerduty_api.py
CHANGED
|
@@ -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."""
|
|
@@ -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)
|