qontract-reconcile 0.10.2.dev334__py3-none-any.whl → 0.10.2.dev439__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of qontract-reconcile might be problematic. Click here for more details.
- {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/METADATA +13 -12
- {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/RECORD +366 -361
- reconcile/acs_rbac.py +2 -2
- reconcile/aus/advanced_upgrade_service.py +18 -12
- reconcile/aus/base.py +134 -32
- reconcile/aus/cluster_version_data.py +15 -5
- reconcile/aus/models.py +3 -1
- reconcile/aus/ocm_addons_upgrade_scheduler_org.py +1 -0
- reconcile/aus/ocm_upgrade_scheduler.py +8 -1
- reconcile/aus/ocm_upgrade_scheduler_org.py +20 -5
- reconcile/aus/version_gates/sts_version_gate_handler.py +54 -1
- reconcile/automated_actions/config/integration.py +16 -4
- reconcile/aws_account_manager/integration.py +8 -8
- reconcile/aws_account_manager/reconciler.py +3 -3
- reconcile/aws_ami_cleanup/integration.py +8 -12
- reconcile/aws_ami_share.py +69 -62
- reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
- reconcile/aws_ecr_image_pull_secrets.py +3 -3
- reconcile/aws_iam_keys.py +1 -0
- reconcile/aws_saml_idp/integration.py +12 -4
- reconcile/aws_saml_roles/integration.py +32 -25
- reconcile/aws_version_sync/integration.py +6 -12
- reconcile/change_owners/bundle.py +3 -3
- reconcile/change_owners/change_log_tracking.py +3 -2
- reconcile/change_owners/change_owners.py +1 -1
- reconcile/change_owners/diff.py +2 -4
- reconcile/checkpoint.py +11 -3
- reconcile/cli.py +111 -18
- reconcile/dashdotdb_dora.py +5 -12
- reconcile/dashdotdb_slo.py +1 -1
- reconcile/database_access_manager.py +123 -117
- reconcile/dynatrace_token_provider/integration.py +1 -1
- reconcile/endpoints_discovery/integration.py +4 -1
- reconcile/endpoints_discovery/merge_request.py +1 -1
- reconcile/endpoints_discovery/merge_request_manager.py +9 -11
- reconcile/external_resources/factories.py +5 -12
- reconcile/external_resources/integration.py +1 -1
- reconcile/external_resources/manager.py +8 -5
- reconcile/external_resources/meta.py +0 -1
- reconcile/external_resources/metrics.py +1 -1
- reconcile/external_resources/model.py +20 -20
- reconcile/external_resources/reconciler.py +7 -4
- reconcile/external_resources/secrets_sync.py +8 -11
- reconcile/external_resources/state.py +26 -16
- reconcile/fleet_labeler/integration.py +1 -1
- reconcile/gabi_authorized_users.py +8 -5
- reconcile/gcp_image_mirror.py +2 -2
- reconcile/github_org.py +1 -1
- reconcile/github_owners.py +4 -0
- reconcile/gitlab_housekeeping.py +13 -15
- reconcile/gitlab_members.py +6 -12
- reconcile/gitlab_mr_sqs_consumer.py +2 -2
- reconcile/gitlab_owners.py +15 -11
- reconcile/gitlab_permissions.py +8 -12
- reconcile/glitchtip_project_alerts/integration.py +3 -1
- reconcile/gql_definitions/acs/acs_instances.py +10 -10
- reconcile/gql_definitions/acs/acs_policies.py +5 -5
- reconcile/gql_definitions/acs/acs_rbac.py +6 -6
- reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +32 -32
- reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +26 -26
- reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +6 -7
- reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
- reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
- reconcile/gql_definitions/automated_actions/instance.py +51 -12
- reconcile/gql_definitions/aws_account_manager/aws_accounts.py +11 -11
- reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +20 -10
- reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +28 -68
- reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +20 -10
- reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +20 -10
- reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
- reconcile/gql_definitions/aws_version_sync/clusters.py +10 -10
- reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
- reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
- reconcile/gql_definitions/change_owners/queries/self_service_roles.py +9 -9
- reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +18 -18
- reconcile/gql_definitions/common/alerting_services_settings.py +9 -9
- reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
- reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
- reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_roles.py +120 -0
- reconcile/gql_definitions/common/app_interface_state_settings.py +10 -10
- reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
- reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
- reconcile/gql_definitions/common/apps.py +5 -5
- reconcile/gql_definitions/common/aws_vpc_requests.py +22 -9
- reconcile/gql_definitions/common/aws_vpcs.py +11 -11
- reconcile/gql_definitions/common/clusters.py +37 -35
- reconcile/gql_definitions/common/clusters_minimal.py +14 -14
- reconcile/gql_definitions/common/clusters_with_dms.py +6 -6
- reconcile/gql_definitions/common/clusters_with_peering.py +29 -30
- reconcile/gql_definitions/common/github_orgs.py +10 -10
- reconcile/gql_definitions/common/jira_settings.py +10 -10
- reconcile/gql_definitions/common/jiralert_settings.py +5 -5
- reconcile/gql_definitions/common/ldap_settings.py +5 -5
- reconcile/gql_definitions/common/namespaces.py +42 -44
- reconcile/gql_definitions/common/namespaces_minimal.py +15 -13
- reconcile/gql_definitions/common/ocm_env_telemeter.py +12 -12
- reconcile/gql_definitions/common/ocm_environments.py +19 -19
- reconcile/gql_definitions/common/pagerduty_instances.py +9 -9
- reconcile/gql_definitions/common/pgp_reencryption_settings.py +6 -6
- reconcile/gql_definitions/common/pipeline_providers.py +29 -29
- reconcile/gql_definitions/common/quay_instances.py +5 -5
- reconcile/gql_definitions/common/quay_orgs.py +5 -5
- reconcile/gql_definitions/common/reserved_networks.py +5 -5
- reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
- reconcile/gql_definitions/common/saas_files.py +44 -44
- reconcile/gql_definitions/common/saas_target_namespaces.py +10 -10
- reconcile/gql_definitions/common/saasherder_settings.py +5 -5
- reconcile/gql_definitions/common/slack_workspaces.py +5 -5
- reconcile/gql_definitions/common/smtp_client_settings.py +19 -19
- reconcile/gql_definitions/common/state_aws_account.py +7 -8
- reconcile/gql_definitions/common/users.py +5 -5
- reconcile/gql_definitions/common/users_with_paths.py +5 -5
- reconcile/gql_definitions/cost_report/app_names.py +5 -5
- reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
- reconcile/gql_definitions/cost_report/settings.py +9 -9
- reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +43 -43
- reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +10 -10
- reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
- reconcile/gql_definitions/email_sender/apps.py +5 -5
- reconcile/gql_definitions/email_sender/emails.py +8 -8
- reconcile/gql_definitions/email_sender/users.py +6 -6
- reconcile/gql_definitions/endpoints_discovery/apps.py +10 -10
- reconcile/gql_definitions/external_resources/aws_accounts.py +9 -9
- reconcile/gql_definitions/external_resources/external_resources_modules.py +23 -23
- reconcile/gql_definitions/external_resources/external_resources_namespaces.py +494 -410
- reconcile/gql_definitions/external_resources/external_resources_settings.py +28 -26
- reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
- reconcile/gql_definitions/fleet_labeler/fleet_labels.py +40 -40
- reconcile/gql_definitions/fragments/aus_organization.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
- reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
- reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
- reconcile/gql_definitions/fragments/{aws_vpc_request_subnet.py → aws_organization.py} +12 -8
- reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
- reconcile/gql_definitions/fragments/aws_vpc_request.py +12 -5
- reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
- reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
- reconcile/gql_definitions/fragments/disable.py +5 -5
- reconcile/gql_definitions/fragments/email_service.py +5 -5
- reconcile/gql_definitions/fragments/email_user.py +5 -5
- reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
- reconcile/gql_definitions/fragments/membership_source.py +5 -5
- reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
- reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
- reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
- reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
- reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
- reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
- reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
- reconcile/gql_definitions/fragments/resource_values.py +5 -5
- reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
- reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
- reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
- reconcile/gql_definitions/fragments/terraform_state.py +5 -5
- reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
- reconcile/gql_definitions/fragments/user.py +5 -5
- reconcile/gql_definitions/fragments/vault_secret.py +5 -5
- reconcile/gql_definitions/gcp/gcp_docker_repos.py +9 -9
- reconcile/gql_definitions/gcp/gcp_projects.py +9 -9
- reconcile/gql_definitions/gitlab_members/gitlab_instances.py +9 -9
- reconcile/gql_definitions/gitlab_members/permissions.py +9 -9
- reconcile/gql_definitions/glitchtip/glitchtip_instance.py +9 -9
- reconcile/gql_definitions/glitchtip/glitchtip_project.py +11 -11
- reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +9 -9
- reconcile/gql_definitions/integrations/integrations.py +48 -51
- reconcile/gql_definitions/introspection.json +3207 -1683
- reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +11 -11
- reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +10 -10
- reconcile/gql_definitions/jira/jira_servers.py +5 -5
- reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +14 -10
- reconcile/gql_definitions/jumphosts/jumphosts.py +13 -13
- reconcile/gql_definitions/ldap_groups/roles.py +5 -5
- reconcile/gql_definitions/ldap_groups/settings.py +9 -9
- reconcile/gql_definitions/maintenance/maintenances.py +5 -5
- reconcile/gql_definitions/membershipsources/roles.py +5 -5
- reconcile/gql_definitions/ocm_labels/clusters.py +18 -19
- reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
- reconcile/gql_definitions/openshift_cluster_bots/clusters.py +22 -22
- reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
- reconcile/gql_definitions/openshift_groups/managed_roles.py +6 -6
- reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +10 -10
- reconcile/gql_definitions/quay_membership/quay_membership.py +6 -6
- reconcile/gql_definitions/rhcs/certs.py +33 -87
- reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +43 -0
- reconcile/gql_definitions/rhidp/organizations.py +18 -18
- reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
- reconcile/gql_definitions/service_dependencies/service_dependencies.py +8 -8
- reconcile/gql_definitions/sharding/aws_accounts.py +10 -10
- reconcile/gql_definitions/sharding/ocm_organization.py +8 -8
- reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
- reconcile/gql_definitions/skupper_network/skupper_networks.py +10 -10
- reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
- reconcile/gql_definitions/slack_usergroups/permissions.py +9 -9
- reconcile/gql_definitions/slack_usergroups/users.py +5 -5
- reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
- reconcile/gql_definitions/status_board/status_board.py +6 -7
- reconcile/gql_definitions/statuspage/statuspages.py +9 -9
- reconcile/gql_definitions/templating/template_collection.py +5 -5
- reconcile/gql_definitions/templating/templates.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +6 -6
- reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +11 -11
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +11 -11
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +20 -25
- reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +6 -6
- reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +12 -12
- reconcile/gql_definitions/terraform_init/aws_accounts.py +23 -9
- reconcile/gql_definitions/terraform_repo/terraform_repo.py +9 -9
- reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
- reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +440 -407
- reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +23 -17
- reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +9 -9
- reconcile/gql_definitions/vault_instances/vault_instances.py +61 -61
- reconcile/gql_definitions/vault_policies/vault_policies.py +11 -11
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +8 -8
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
- reconcile/integrations_manager.py +3 -3
- reconcile/jenkins_worker_fleets.py +10 -8
- reconcile/jira_permissions_validator.py +237 -122
- reconcile/ldap_groups/integration.py +1 -1
- reconcile/ocm/types.py +35 -56
- reconcile/ocm_aws_infrastructure_access.py +1 -1
- reconcile/ocm_clusters.py +4 -4
- reconcile/ocm_labels/integration.py +3 -2
- reconcile/ocm_machine_pools.py +33 -27
- reconcile/openshift_base.py +113 -5
- reconcile/openshift_cluster_bots.py +3 -2
- reconcile/openshift_namespace_labels.py +1 -1
- reconcile/openshift_namespaces.py +97 -101
- reconcile/openshift_resources_base.py +6 -2
- reconcile/openshift_rhcs_certs.py +74 -37
- reconcile/openshift_rolebindings.py +230 -130
- reconcile/openshift_saas_deploy.py +6 -7
- reconcile/openshift_saas_deploy_change_tester.py +9 -7
- reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
- reconcile/openshift_serviceaccount_tokens.py +2 -2
- reconcile/openshift_upgrade_watcher.py +4 -4
- reconcile/openshift_users.py +5 -3
- reconcile/oum/labelset.py +5 -3
- reconcile/oum/models.py +1 -4
- reconcile/prometheus_rules_tester/integration.py +3 -3
- reconcile/quay_mirror.py +1 -1
- reconcile/queries.py +131 -0
- reconcile/rhidp/common.py +3 -5
- reconcile/rhidp/sso_client/base.py +16 -5
- reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
- reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
- reconcile/skupper_network/integration.py +2 -2
- reconcile/slack_usergroups.py +35 -14
- reconcile/sql_query.py +1 -0
- reconcile/status_board.py +6 -6
- reconcile/statuspage/atlassian.py +7 -7
- reconcile/statuspage/integrations/maintenances.py +4 -3
- reconcile/statuspage/page.py +4 -9
- reconcile/statuspage/status.py +5 -8
- reconcile/templates/rosa-classic-cluster-creation.sh.j2 +5 -1
- reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +4 -1
- reconcile/templating/lib/merge_request_manager.py +2 -2
- reconcile/templating/lib/rendering.py +3 -3
- reconcile/templating/renderer.py +12 -13
- reconcile/terraform_aws_route53.py +7 -1
- reconcile/terraform_cloudflare_dns.py +3 -3
- reconcile/terraform_cloudflare_resources.py +5 -5
- reconcile/terraform_cloudflare_users.py +3 -2
- reconcile/terraform_init/integration.py +187 -23
- reconcile/terraform_repo.py +16 -12
- reconcile/terraform_resources.py +17 -7
- reconcile/terraform_tgw_attachments.py +27 -19
- reconcile/terraform_users.py +7 -0
- reconcile/terraform_vpc_peerings.py +14 -3
- reconcile/terraform_vpc_resources/integration.py +20 -8
- reconcile/typed_queries/app_interface_roles.py +10 -0
- reconcile/typed_queries/aws_account_tags.py +41 -0
- reconcile/typed_queries/cost_report/app_names.py +1 -1
- reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
- reconcile/typed_queries/saas_files.py +13 -13
- reconcile/typed_queries/status_board.py +2 -2
- reconcile/unleash_feature_toggles/integration.py +4 -2
- reconcile/utils/acs/base.py +6 -3
- reconcile/utils/acs/policies.py +2 -2
- reconcile/utils/aggregated_list.py +4 -3
- reconcile/utils/aws_api.py +51 -20
- reconcile/utils/aws_api_typed/api.py +38 -9
- reconcile/utils/aws_api_typed/cloudformation.py +149 -0
- reconcile/utils/aws_api_typed/logs.py +73 -0
- reconcile/utils/aws_api_typed/organization.py +4 -2
- reconcile/utils/binary.py +7 -12
- reconcile/utils/datetime_util.py +67 -0
- reconcile/utils/deadmanssnitch_api.py +1 -1
- reconcile/utils/differ.py +2 -3
- reconcile/utils/early_exit_cache.py +11 -12
- reconcile/utils/expiration.py +7 -3
- reconcile/utils/external_resource_spec.py +24 -1
- reconcile/utils/filtering.py +1 -1
- reconcile/utils/gitlab_api.py +7 -5
- reconcile/utils/glitchtip/client.py +6 -2
- reconcile/utils/glitchtip/models.py +25 -28
- reconcile/utils/gql.py +4 -7
- reconcile/utils/helm.py +2 -1
- reconcile/utils/helpers.py +1 -1
- reconcile/utils/instrumented_wrappers.py +1 -1
- reconcile/utils/internal_groups/client.py +2 -2
- reconcile/utils/internal_groups/models.py +8 -17
- reconcile/utils/jinja2/utils.py +6 -8
- reconcile/utils/jira_client.py +82 -63
- reconcile/utils/jjb_client.py +28 -15
- reconcile/utils/jobcontroller/controller.py +2 -2
- reconcile/utils/jobcontroller/models.py +17 -1
- reconcile/utils/json.py +74 -0
- reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
- reconcile/utils/membershipsources/models.py +16 -23
- reconcile/utils/membershipsources/resolver.py +4 -2
- reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
- reconcile/utils/merge_request_manager/parser.py +6 -6
- reconcile/utils/metrics.py +5 -5
- reconcile/utils/models.py +304 -82
- reconcile/utils/mr/app_interface_reporter.py +2 -2
- reconcile/utils/mr/base.py +2 -2
- reconcile/utils/mr/notificator.py +3 -3
- reconcile/utils/mr/update_access_report_base.py +3 -4
- reconcile/utils/mr/user_maintenance.py +3 -2
- reconcile/utils/oc.py +249 -203
- reconcile/utils/oc_filters.py +3 -3
- reconcile/utils/ocm/addons.py +0 -1
- reconcile/utils/ocm/base.py +18 -21
- reconcile/utils/ocm/cluster_groups.py +1 -1
- reconcile/utils/ocm/identity_providers.py +2 -2
- reconcile/utils/ocm/labels.py +1 -1
- reconcile/utils/ocm/products.py +9 -3
- reconcile/utils/ocm/search_filters.py +3 -6
- reconcile/utils/ocm/service_log.py +4 -6
- reconcile/utils/ocm/sre_capability_labels.py +20 -13
- reconcile/utils/openshift_resource.py +10 -5
- reconcile/utils/output.py +3 -2
- reconcile/utils/pagerduty_api.py +10 -7
- reconcile/utils/promotion_state.py +6 -11
- reconcile/utils/raw_github_api.py +1 -1
- reconcile/utils/rhcsv2_certs.py +138 -35
- reconcile/utils/rosa/session.py +16 -0
- reconcile/utils/runtime/integration.py +2 -3
- reconcile/utils/runtime/runner.py +2 -2
- reconcile/utils/saasherder/interfaces.py +13 -20
- reconcile/utils/saasherder/models.py +25 -21
- reconcile/utils/saasherder/saasherder.py +55 -31
- reconcile/utils/slack_api.py +26 -4
- reconcile/utils/sloth.py +224 -0
- reconcile/utils/sqs_gateway.py +2 -1
- reconcile/utils/state.py +2 -1
- reconcile/utils/structs.py +1 -1
- reconcile/utils/terraform_client.py +5 -4
- reconcile/utils/terrascript_aws_client.py +192 -114
- reconcile/utils/unleash/server.py +2 -8
- reconcile/utils/vault.py +5 -12
- reconcile/utils/vcs.py +8 -8
- reconcile/vault_replication.py +107 -42
- tools/app_interface_reporter.py +4 -4
- tools/cli_commands/cost_report/cost_management_api.py +3 -3
- tools/cli_commands/cost_report/view.py +7 -6
- tools/cli_commands/erv2.py +1 -1
- tools/cli_commands/systems_and_tools.py +5 -1
- tools/qontract_cli.py +31 -18
- tools/template_validation.py +3 -1
- reconcile/gql_definitions/cna/__init__.py +0 -0
- reconcile/gql_definitions/cna/queries/__init__.py +0 -0
- reconcile/gql_definitions/ocm_oidc_idp/__init__.py +0 -0
- reconcile/gql_definitions/ocm_subscription_labels/__init__.py +0 -0
- {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev334.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/entry_points.txt +0 -0
reconcile/utils/oc.py
CHANGED
|
@@ -10,16 +10,16 @@ import re
|
|
|
10
10
|
import subprocess
|
|
11
11
|
import threading
|
|
12
12
|
import time
|
|
13
|
+
from collections import defaultdict
|
|
13
14
|
from contextlib import suppress
|
|
14
15
|
from dataclasses import dataclass
|
|
15
|
-
from datetime import datetime
|
|
16
16
|
from functools import cache, wraps
|
|
17
17
|
from subprocess import Popen
|
|
18
18
|
from threading import Lock
|
|
19
19
|
from typing import TYPE_CHECKING, Any, TextIO, cast
|
|
20
20
|
|
|
21
21
|
import urllib3
|
|
22
|
-
from kubernetes.client import (
|
|
22
|
+
from kubernetes.client import (
|
|
23
23
|
ApiClient,
|
|
24
24
|
Configuration,
|
|
25
25
|
)
|
|
@@ -47,6 +47,7 @@ from sretoolbox.utils import (
|
|
|
47
47
|
)
|
|
48
48
|
|
|
49
49
|
from reconcile.status import RunningState
|
|
50
|
+
from reconcile.utils.json import json_dumps
|
|
50
51
|
from reconcile.utils.jump_host import (
|
|
51
52
|
JumphostParameters,
|
|
52
53
|
JumpHostSSH,
|
|
@@ -67,7 +68,18 @@ if TYPE_CHECKING:
|
|
|
67
68
|
urllib3.disable_warnings()
|
|
68
69
|
|
|
69
70
|
GET_REPLICASET_MAX_ATTEMPTS = 20
|
|
70
|
-
|
|
71
|
+
DEFAULT_GROUP = ""
|
|
72
|
+
PROJECT_KIND = "Project.project.openshift.io"
|
|
73
|
+
POD_RECYCLE_SUPPORTED_TRIGGER_KINDS = [
|
|
74
|
+
"ConfigMap",
|
|
75
|
+
"Secret",
|
|
76
|
+
]
|
|
77
|
+
POD_RECYCLE_SUPPORTED_OWNER_KINDS = [
|
|
78
|
+
"DaemonSet",
|
|
79
|
+
"Deployment",
|
|
80
|
+
"DeploymentConfig",
|
|
81
|
+
"StatefulSet",
|
|
82
|
+
]
|
|
71
83
|
|
|
72
84
|
oc_run_execution_counter = Counter(
|
|
73
85
|
name="oc_run_execution_counter",
|
|
@@ -124,23 +136,23 @@ class JSONParsingError(Exception):
|
|
|
124
136
|
pass
|
|
125
137
|
|
|
126
138
|
|
|
127
|
-
class
|
|
139
|
+
class PodNotReadyError(Exception):
|
|
128
140
|
pass
|
|
129
141
|
|
|
130
142
|
|
|
131
|
-
class
|
|
143
|
+
class JobNotRunningError(Exception):
|
|
132
144
|
pass
|
|
133
145
|
|
|
134
146
|
|
|
135
|
-
class
|
|
147
|
+
class RequestEntityTooLargeError(Exception):
|
|
136
148
|
pass
|
|
137
149
|
|
|
138
150
|
|
|
139
|
-
class
|
|
151
|
+
class KindNotFoundError(Exception):
|
|
140
152
|
pass
|
|
141
153
|
|
|
142
154
|
|
|
143
|
-
class
|
|
155
|
+
class AmbiguousResourceTypeError(Exception):
|
|
144
156
|
pass
|
|
145
157
|
|
|
146
158
|
|
|
@@ -379,10 +391,7 @@ class OCCli:
|
|
|
379
391
|
|
|
380
392
|
self.init_projects = init_projects
|
|
381
393
|
if self.init_projects:
|
|
382
|
-
if self.is_kind_supported("
|
|
383
|
-
kind = "Project.project.openshift.io"
|
|
384
|
-
else:
|
|
385
|
-
kind = "Namespace"
|
|
394
|
+
kind = PROJECT_KIND if self.is_kind_supported(PROJECT_KIND) else "Namespace"
|
|
386
395
|
self.projects = {p["metadata"]["name"] for p in self.get_all(kind)["items"]}
|
|
387
396
|
|
|
388
397
|
self.slow_oc_reconcile_threshold = float(
|
|
@@ -452,10 +461,7 @@ class OCCli:
|
|
|
452
461
|
|
|
453
462
|
self.init_projects = init_projects
|
|
454
463
|
if self.init_projects:
|
|
455
|
-
if self.is_kind_supported("
|
|
456
|
-
kind = "Project.project.openshift.io"
|
|
457
|
-
else:
|
|
458
|
-
kind = "Namespace"
|
|
464
|
+
kind = PROJECT_KIND if self.is_kind_supported(PROJECT_KIND) else "Namespace"
|
|
459
465
|
self.projects = {p["metadata"]["name"] for p in self.get_all(kind)["items"]}
|
|
460
466
|
|
|
461
467
|
self.slow_oc_reconcile_threshold = float(
|
|
@@ -563,7 +569,7 @@ class OCCli:
|
|
|
563
569
|
"-f",
|
|
564
570
|
"-",
|
|
565
571
|
] + parameters_to_process
|
|
566
|
-
result = self._run(cmd, stdin=
|
|
572
|
+
result = self._run(cmd, stdin=json_dumps(template))
|
|
567
573
|
return json.loads(result)["items"]
|
|
568
574
|
|
|
569
575
|
@OCDecorators.process_reconcile_time
|
|
@@ -592,7 +598,7 @@ class OCCli:
|
|
|
592
598
|
def patch(
|
|
593
599
|
self, namespace: str, kind: str, name: str, patch: Mapping[str, Any]
|
|
594
600
|
) -> OCProcessReconcileTimeDecoratorMsg:
|
|
595
|
-
cmd = ["patch", "-n", namespace, kind, name, "-p",
|
|
601
|
+
cmd = ["patch", "-n", namespace, kind, name, "-p", json_dumps(patch)]
|
|
596
602
|
self._run(cmd)
|
|
597
603
|
resource = OR({"kind": kind, "metadata": {"name": name}}, "", "")
|
|
598
604
|
return self._msg_to_process_reconcile_time(namespace, resource)
|
|
@@ -636,11 +642,9 @@ class OCCli:
|
|
|
636
642
|
def project_exists(self, name: str) -> bool:
|
|
637
643
|
if name in self.projects:
|
|
638
644
|
return True
|
|
645
|
+
kind = PROJECT_KIND if self.is_kind_supported(PROJECT_KIND) else "Namespace"
|
|
639
646
|
try:
|
|
640
|
-
|
|
641
|
-
self.get(None, "Project.project.openshift.io", name)
|
|
642
|
-
else:
|
|
643
|
-
self.get(None, "Namespace", name)
|
|
647
|
+
self.get(None, kind, name)
|
|
644
648
|
except StatusCodeError as e:
|
|
645
649
|
if "NotFound" in str(e):
|
|
646
650
|
return False
|
|
@@ -649,7 +653,7 @@ class OCCli:
|
|
|
649
653
|
|
|
650
654
|
@OCDecorators.process_reconcile_time
|
|
651
655
|
def new_project(self, namespace: str) -> OCProcessReconcileTimeDecoratorMsg:
|
|
652
|
-
if self.is_kind_supported(
|
|
656
|
+
if self.is_kind_supported(PROJECT_KIND):
|
|
653
657
|
cmd = ["new-project", namespace]
|
|
654
658
|
else:
|
|
655
659
|
cmd = ["create", "namespace", namespace]
|
|
@@ -665,7 +669,7 @@ class OCCli:
|
|
|
665
669
|
|
|
666
670
|
@OCDecorators.process_reconcile_time
|
|
667
671
|
def delete_project(self, namespace: str) -> OCProcessReconcileTimeDecoratorMsg:
|
|
668
|
-
if self.is_kind_supported(
|
|
672
|
+
if self.is_kind_supported(PROJECT_KIND):
|
|
669
673
|
cmd = ["delete", "project", namespace]
|
|
670
674
|
else:
|
|
671
675
|
cmd = ["delete", "namespace", namespace]
|
|
@@ -714,9 +718,9 @@ class OCCli:
|
|
|
714
718
|
|
|
715
719
|
def sa_get_token(self, namespace: str, name: str) -> str:
|
|
716
720
|
cmd = ["sa", "-n", namespace, "get-token", name]
|
|
717
|
-
return self._run(cmd)
|
|
721
|
+
return self._run(cmd).decode("utf-8")
|
|
718
722
|
|
|
719
|
-
def get_api_resources(self) -> dict[str,
|
|
723
|
+
def get_api_resources(self) -> dict[str, list[OCCliApiResource]]:
|
|
720
724
|
with self.api_resources_lock:
|
|
721
725
|
if not self.api_resources:
|
|
722
726
|
cmd = ["api-resources", "--no-headers"]
|
|
@@ -920,108 +924,105 @@ class OCCli:
|
|
|
920
924
|
if not status["ready"]:
|
|
921
925
|
raise PodNotReadyError(name)
|
|
922
926
|
|
|
923
|
-
def
|
|
924
|
-
self,
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
namespace: namespace in which dependant resource is applied.
|
|
930
|
-
dep_kind: dependant resource kind. currently only supports Secret.
|
|
931
|
-
dep_resource: dependant resource."""
|
|
932
|
-
|
|
933
|
-
supported_kinds = ["Secret", "ConfigMap"]
|
|
934
|
-
if dep_kind not in supported_kinds:
|
|
927
|
+
def _is_resource_supported_to_trigger_recycle(
|
|
928
|
+
self,
|
|
929
|
+
namespace: str,
|
|
930
|
+
resource: OR,
|
|
931
|
+
) -> bool:
|
|
932
|
+
if resource.kind not in POD_RECYCLE_SUPPORTED_TRIGGER_KINDS:
|
|
935
933
|
logging.debug([
|
|
936
934
|
"skipping_pod_recycle_unsupported",
|
|
937
935
|
self.cluster_name,
|
|
938
936
|
namespace,
|
|
939
|
-
|
|
937
|
+
resource.kind,
|
|
938
|
+
resource.name,
|
|
940
939
|
])
|
|
941
|
-
return
|
|
940
|
+
return False
|
|
942
941
|
|
|
943
|
-
dep_annotations = dep_resource.body["metadata"].get("annotations", {})
|
|
944
942
|
# Note, that annotations might have been set to None explicitly
|
|
945
|
-
|
|
946
|
-
qontract_recycle =
|
|
947
|
-
if qontract_recycle is True:
|
|
948
|
-
raise RecyclePodsInvalidAnnotationValueError('should be "true"')
|
|
943
|
+
annotations = resource.body["metadata"].get("annotations") or {}
|
|
944
|
+
qontract_recycle = annotations.get("qontract.recycle")
|
|
949
945
|
if qontract_recycle != "true":
|
|
950
946
|
logging.debug([
|
|
951
947
|
"skipping_pod_recycle_no_annotation",
|
|
952
948
|
self.cluster_name,
|
|
953
949
|
namespace,
|
|
954
|
-
|
|
950
|
+
resource.kind,
|
|
951
|
+
resource.name,
|
|
955
952
|
])
|
|
953
|
+
return False
|
|
954
|
+
return True
|
|
955
|
+
|
|
956
|
+
def recycle_pods(
|
|
957
|
+
self,
|
|
958
|
+
dry_run: bool,
|
|
959
|
+
namespace: str,
|
|
960
|
+
resource: OR,
|
|
961
|
+
) -> None:
|
|
962
|
+
"""
|
|
963
|
+
recycles pods which are using the specified resources.
|
|
964
|
+
will only act on Secret or ConfigMap containing the 'qontract.recycle' annotation.
|
|
965
|
+
|
|
966
|
+
Args:
|
|
967
|
+
dry_run (bool): if True, will only log the recycle action without executing it
|
|
968
|
+
namespace (str): namespace of the resource
|
|
969
|
+
resource (OR): resource object (Secret or ConfigMap) to check for pod usage
|
|
970
|
+
"""
|
|
971
|
+
|
|
972
|
+
if not self._is_resource_supported_to_trigger_recycle(namespace, resource):
|
|
956
973
|
return
|
|
957
974
|
|
|
958
|
-
dep_name = dep_resource.name
|
|
959
975
|
pods = self.get(namespace, "Pod")["items"]
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
]
|
|
969
|
-
else:
|
|
970
|
-
raise RecyclePodsUnsupportedKindError(dep_kind)
|
|
971
|
-
|
|
972
|
-
recyclables: dict[str, list[dict[str, Any]]] = {}
|
|
973
|
-
supported_recyclables = [
|
|
974
|
-
"Deployment",
|
|
975
|
-
"DeploymentConfig",
|
|
976
|
-
"StatefulSet",
|
|
977
|
-
"DaemonSet",
|
|
976
|
+
pods_to_recycle = [
|
|
977
|
+
pod
|
|
978
|
+
for pod in pods
|
|
979
|
+
if self.is_resource_used_in_pod(
|
|
980
|
+
name=resource.name,
|
|
981
|
+
kind=resource.kind,
|
|
982
|
+
pod=pod,
|
|
983
|
+
)
|
|
978
984
|
]
|
|
985
|
+
|
|
986
|
+
recycle_names_by_kind = defaultdict(set)
|
|
979
987
|
for pod in pods_to_recycle:
|
|
980
988
|
owner = self.get_obj_root_owner(namespace, pod, allow_not_found=True)
|
|
981
989
|
kind = owner["kind"]
|
|
982
|
-
if kind
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
for
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
for kind, objs in recyclables.items():
|
|
995
|
-
for obj in objs:
|
|
996
|
-
self.recycle(dry_run, namespace, kind, obj)
|
|
997
|
-
|
|
998
|
-
@retry(exceptions=ObjectHasBeenModifiedError)
|
|
990
|
+
if kind in POD_RECYCLE_SUPPORTED_OWNER_KINDS:
|
|
991
|
+
recycle_names_by_kind[kind].add(owner["metadata"]["name"])
|
|
992
|
+
|
|
993
|
+
for kind, names in recycle_names_by_kind.items():
|
|
994
|
+
for name in names:
|
|
995
|
+
self.recycle(
|
|
996
|
+
dry_run=dry_run,
|
|
997
|
+
namespace=namespace,
|
|
998
|
+
kind=kind,
|
|
999
|
+
name=name,
|
|
1000
|
+
)
|
|
1001
|
+
|
|
999
1002
|
def recycle(
|
|
1000
|
-
self,
|
|
1003
|
+
self,
|
|
1004
|
+
dry_run: bool,
|
|
1005
|
+
namespace: str,
|
|
1006
|
+
kind: str,
|
|
1007
|
+
name: str,
|
|
1001
1008
|
) -> None:
|
|
1002
|
-
"""
|
|
1009
|
+
"""
|
|
1010
|
+
Recycles an object using oc rollout restart, which will add an annotation
|
|
1011
|
+
kubectl.kubernetes.io/restartedAt with the current timestamp to the pod
|
|
1012
|
+
template, triggering a rolling restart.
|
|
1003
1013
|
|
|
1004
|
-
:
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1014
|
+
Args:
|
|
1015
|
+
dry_run (bool): if True, will only log the recycle action without executing it
|
|
1016
|
+
namespace (str): namespace of the object to recycle
|
|
1017
|
+
kind (str): kind of the object to recycle
|
|
1018
|
+
name (str): name of the object to recycle
|
|
1008
1019
|
"""
|
|
1009
|
-
name = obj["metadata"]["name"]
|
|
1010
1020
|
logging.info([f"recycle_{kind.lower()}", self.cluster_name, namespace, name])
|
|
1011
1021
|
if not dry_run:
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
obj = self.get(namespace, kind, name)
|
|
1017
|
-
# honor update strategy by setting annotations to force
|
|
1018
|
-
# a new rollout
|
|
1019
|
-
a = obj["spec"]["template"]["metadata"].get("annotations", {})
|
|
1020
|
-
a["recycle.time"] = recycle_time
|
|
1021
|
-
obj["spec"]["template"]["metadata"]["annotations"] = a
|
|
1022
|
-
cmd = ["apply", "-n", namespace, "-f", "-"]
|
|
1023
|
-
stdin = json.dumps(obj, sort_keys=True)
|
|
1024
|
-
self._run(cmd, stdin=stdin, apply=True)
|
|
1022
|
+
self._run(
|
|
1023
|
+
["rollout", "restart", f"{kind}/{name}", "-n", namespace],
|
|
1024
|
+
apply=True,
|
|
1025
|
+
)
|
|
1025
1026
|
|
|
1026
1027
|
def get_obj_root_owner(
|
|
1027
1028
|
self,
|
|
@@ -1063,12 +1064,24 @@ class OCCli:
|
|
|
1063
1064
|
)
|
|
1064
1065
|
return obj
|
|
1065
1066
|
|
|
1066
|
-
def
|
|
1067
|
-
|
|
1068
|
-
|
|
1067
|
+
def is_resource_used_in_pod(
|
|
1068
|
+
self,
|
|
1069
|
+
name: str,
|
|
1070
|
+
kind: str,
|
|
1071
|
+
pod: Mapping[str, Any],
|
|
1072
|
+
) -> bool:
|
|
1073
|
+
"""
|
|
1074
|
+
Check if a resource (Secret or ConfigMap) is used in a Pod.
|
|
1075
|
+
|
|
1076
|
+
Args:
|
|
1077
|
+
name: Name of the resource
|
|
1078
|
+
kind: "Secret" or "ConfigMap"
|
|
1079
|
+
pod: Pod object
|
|
1069
1080
|
|
|
1070
|
-
|
|
1071
|
-
|
|
1081
|
+
Returns:
|
|
1082
|
+
True if the resource is used in the Pod, False otherwise.
|
|
1083
|
+
"""
|
|
1084
|
+
used_resources = self.get_resources_used_in_pod_spec(pod["spec"], kind)
|
|
1072
1085
|
return name in used_resources
|
|
1073
1086
|
|
|
1074
1087
|
@staticmethod
|
|
@@ -1077,25 +1090,39 @@ class OCCli:
|
|
|
1077
1090
|
kind: str,
|
|
1078
1091
|
include_optional: bool = True,
|
|
1079
1092
|
) -> dict[str, set[str]]:
|
|
1080
|
-
|
|
1081
|
-
|
|
1093
|
+
"""
|
|
1094
|
+
Get resources (Secrets or ConfigMaps) used in a Pod spec.
|
|
1095
|
+
Returns a dictionary where keys are resource names and values are sets of keys used from that resource.
|
|
1096
|
+
|
|
1097
|
+
Args:
|
|
1098
|
+
spec: Pod spec
|
|
1099
|
+
kind: "Secret" or "ConfigMap"
|
|
1100
|
+
include_optional: Whether to include optional resources
|
|
1101
|
+
|
|
1102
|
+
Returns:
|
|
1103
|
+
A dictionary mapping resource names to sets of keys used.
|
|
1104
|
+
"""
|
|
1105
|
+
match kind:
|
|
1106
|
+
case "Secret":
|
|
1107
|
+
volume_kind, volume_kind_ref, env_from_kind, env_kind, env_ref = (
|
|
1108
|
+
"secret",
|
|
1109
|
+
"secretName",
|
|
1110
|
+
"secretRef",
|
|
1111
|
+
"secretKeyRef",
|
|
1112
|
+
"name",
|
|
1113
|
+
)
|
|
1114
|
+
case "ConfigMap":
|
|
1115
|
+
volume_kind, volume_kind_ref, env_from_kind, env_kind, env_ref = (
|
|
1116
|
+
"configMap",
|
|
1117
|
+
"name",
|
|
1118
|
+
"configMapRef",
|
|
1119
|
+
"configMapKeyRef",
|
|
1120
|
+
"name",
|
|
1121
|
+
)
|
|
1122
|
+
case _:
|
|
1123
|
+
raise KeyError(f"unsupported resource kind: {kind}")
|
|
1124
|
+
|
|
1082
1125
|
optional = "optional"
|
|
1083
|
-
if kind == "Secret":
|
|
1084
|
-
volume_kind, volume_kind_ref, env_from_kind, env_kind, env_ref = (
|
|
1085
|
-
"secret",
|
|
1086
|
-
"secretName",
|
|
1087
|
-
"secretRef",
|
|
1088
|
-
"secretKeyRef",
|
|
1089
|
-
"name",
|
|
1090
|
-
)
|
|
1091
|
-
elif kind == "ConfigMap":
|
|
1092
|
-
volume_kind, volume_kind_ref, env_from_kind, env_kind, env_ref = (
|
|
1093
|
-
"configMap",
|
|
1094
|
-
"name",
|
|
1095
|
-
"configMapRef",
|
|
1096
|
-
"configMapKeyRef",
|
|
1097
|
-
"name",
|
|
1098
|
-
)
|
|
1099
1126
|
|
|
1100
1127
|
resources: dict[str, set[str]] = {}
|
|
1101
1128
|
for v in spec.get("volumes") or []:
|
|
@@ -1124,8 +1151,8 @@ class OCCli:
|
|
|
1124
1151
|
continue
|
|
1125
1152
|
resource_name = resource_ref[env_ref]
|
|
1126
1153
|
resources.setdefault(resource_name, set())
|
|
1127
|
-
|
|
1128
|
-
resources[resource_name].add(
|
|
1154
|
+
key = resource_ref["key"]
|
|
1155
|
+
resources[resource_name].add(key)
|
|
1129
1156
|
except (KeyError, TypeError):
|
|
1130
1157
|
continue
|
|
1131
1158
|
|
|
@@ -1186,7 +1213,7 @@ class OCCli:
|
|
|
1186
1213
|
def _run_json(
|
|
1187
1214
|
self, cmd: list[str], allow_not_found: bool = False
|
|
1188
1215
|
) -> dict[str, Any]:
|
|
1189
|
-
out = self._run(cmd, allow_not_found=allow_not_found)
|
|
1216
|
+
out = self._run(cmd, allow_not_found=allow_not_found).decode("utf-8")
|
|
1190
1217
|
|
|
1191
1218
|
try:
|
|
1192
1219
|
out_json = json.loads(out)
|
|
@@ -1195,76 +1222,90 @@ class OCCli:
|
|
|
1195
1222
|
|
|
1196
1223
|
return out_json
|
|
1197
1224
|
|
|
1198
|
-
def
|
|
1199
|
-
|
|
1200
|
-
# the api resources initialization.
|
|
1201
|
-
if not self.api_resources:
|
|
1202
|
-
self.get_api_resources()
|
|
1225
|
+
def parse_kind(self, kind: str) -> tuple[str, str, str]:
|
|
1226
|
+
"""Parse a Kubernetes kind string into its components.
|
|
1203
1227
|
|
|
1204
|
-
|
|
1205
|
-
kind
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1228
|
+
Supports three formats:
|
|
1229
|
+
- kind
|
|
1230
|
+
- kind.group.whatever
|
|
1231
|
+
- kind.group.whatever/version
|
|
1232
|
+
|
|
1233
|
+
Args:
|
|
1234
|
+
kind: A Kubernetes kind string in one of the supported formats
|
|
1235
|
+
|
|
1236
|
+
Returns:
|
|
1237
|
+
Tuple of (kind, group, version) where missing parts are empty strings
|
|
1238
|
+
|
|
1239
|
+
Raises:
|
|
1240
|
+
ValueError: If the kind string format is invalid
|
|
1241
|
+
|
|
1242
|
+
Examples:
|
|
1243
|
+
>>> parse_kind_string("Deployment")
|
|
1244
|
+
('Deployment', '', '')
|
|
1245
|
+
>>> parse_kind_string("ClusterRoleBinding.rbac.authorization.k8s.io")
|
|
1246
|
+
('ClusterRoleBinding', 'rbac.authorization.k8s.io', '')
|
|
1247
|
+
>>> parse_kind_string("CustomResource.mygroup.example.com/v1")
|
|
1248
|
+
('CustomResource', 'mygroup.example.com', 'v1')
|
|
1249
|
+
"""
|
|
1250
|
+
pattern = r"^(?P<kind>[^./]+)(?:\.(?P<group>[^/]+))?(?:/(?P<version>.+))?$"
|
|
1251
|
+
match = re.match(pattern, kind)
|
|
1252
|
+
if not match:
|
|
1253
|
+
raise ValueError(f"Invalid kind string: {kind}")
|
|
1254
|
+
|
|
1255
|
+
kind = match.group("kind") or ""
|
|
1256
|
+
group = match.group("group") or DEFAULT_GROUP
|
|
1257
|
+
version = match.group("version") or ""
|
|
1258
|
+
|
|
1259
|
+
return kind, group, version
|
|
1231
1260
|
|
|
1232
1261
|
def is_kind_supported(self, kind: str) -> bool:
|
|
1233
|
-
|
|
1234
|
-
# the api resources initialization.
|
|
1235
|
-
if not self.api_resources:
|
|
1236
|
-
self.get_api_resources()
|
|
1262
|
+
"""Returns True if the given kind is supported by the cluster, False otherwise.
|
|
1237
1263
|
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
else:
|
|
1245
|
-
return kind in self.api_resources
|
|
1264
|
+
Kind can be either kind, kind.group or kind.group/version."""
|
|
1265
|
+
try:
|
|
1266
|
+
self.get_api_resource(kind)
|
|
1267
|
+
return True
|
|
1268
|
+
except KindNotFoundError:
|
|
1269
|
+
return False
|
|
1246
1270
|
|
|
1247
1271
|
def is_kind_namespaced(self, kind: str) -> bool:
|
|
1248
|
-
|
|
1249
|
-
|
|
1272
|
+
"""Returns True if the given kind is namespaced, False if it's cluster scoped.
|
|
1273
|
+
|
|
1274
|
+
Kind can be either kind, kind.group or kind.group/version."""
|
|
1275
|
+
return self.get_api_resource(kind).namespaced
|
|
1276
|
+
|
|
1277
|
+
def get_api_resource(self, kind: str) -> OCCliApiResource:
|
|
1278
|
+
"""Return the OCCliApiResource for the given resource type.
|
|
1279
|
+
|
|
1280
|
+
Resource type can be either kind, kind.group or kind.group/version.
|
|
1281
|
+
If kind is not unique, group must be specified."""
|
|
1282
|
+
|
|
1250
1283
|
if not self.api_resources:
|
|
1251
|
-
|
|
1284
|
+
raise RuntimeError("API resources not initialized")
|
|
1252
1285
|
|
|
1253
|
-
|
|
1254
|
-
kind = kg[0]
|
|
1286
|
+
kind, group, _ = self.parse_kind(kind)
|
|
1255
1287
|
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
raise StatusCodeError(f"Kind {kind} does not exist in the ApiServer")
|
|
1288
|
+
if not (resources := self.api_resources.get(kind)):
|
|
1289
|
+
# the kind not found at all
|
|
1290
|
+
raise KindNotFoundError(f"Unsupported resource type: {kind}")
|
|
1260
1291
|
|
|
1261
|
-
if len(
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1292
|
+
if len(resources) == 1 and group == DEFAULT_GROUP:
|
|
1293
|
+
return resources[0]
|
|
1294
|
+
|
|
1295
|
+
# get the resource with the specified group
|
|
1296
|
+
if resource := next((r for r in resources if r.group == group), None):
|
|
1297
|
+
return resource
|
|
1298
|
+
|
|
1299
|
+
# no resource with the specified group found
|
|
1300
|
+
if group == DEFAULT_GROUP:
|
|
1301
|
+
message = (
|
|
1302
|
+
f"Ambiguous resource type: {kind}. "
|
|
1303
|
+
"Please fully qualify it with its API group. E.g., ClusterRoleBinding -> ClusterRoleBinding.rbac.authorization.k8s.io"
|
|
1304
|
+
)
|
|
1305
|
+
raise AmbiguousResourceTypeError(message)
|
|
1306
|
+
|
|
1307
|
+
# group was specified but no matching resource found
|
|
1308
|
+
raise KindNotFoundError(f"Unsupported resource type: {kind}")
|
|
1268
1309
|
|
|
1269
1310
|
|
|
1270
1311
|
REQUEST_TIMEOUT = 60
|
|
@@ -1304,20 +1345,19 @@ class OCNative(OCCli):
|
|
|
1304
1345
|
|
|
1305
1346
|
server = connection_parameters.server_url
|
|
1306
1347
|
|
|
1307
|
-
if server:
|
|
1308
|
-
|
|
1309
|
-
self.api_resources = self.get_api_resources()
|
|
1348
|
+
if not server:
|
|
1349
|
+
raise Exception("Server name is required!")
|
|
1310
1350
|
|
|
1311
|
-
|
|
1312
|
-
raise Exception("
|
|
1351
|
+
if not token:
|
|
1352
|
+
raise Exception("Token is required!")
|
|
1353
|
+
|
|
1354
|
+
self.client = self._get_client(server, token)
|
|
1355
|
+
self.api_resources = self.get_api_resources()
|
|
1313
1356
|
|
|
1314
1357
|
self.projects = set()
|
|
1315
1358
|
self.init_projects = init_projects
|
|
1316
1359
|
if self.init_projects:
|
|
1317
|
-
if self.is_kind_supported("
|
|
1318
|
-
kind = "Project.project.openshift.io"
|
|
1319
|
-
else:
|
|
1320
|
-
kind = "Namespace"
|
|
1360
|
+
kind = PROJECT_KIND if self.is_kind_supported(PROJECT_KIND) else "Namespace"
|
|
1321
1361
|
self.projects = {p["metadata"]["name"] for p in self.get_all(kind)["items"]}
|
|
1322
1362
|
|
|
1323
1363
|
def __enter__(self) -> OCNative:
|
|
@@ -1367,8 +1407,10 @@ class OCNative(OCCli):
|
|
|
1367
1407
|
|
|
1368
1408
|
@retry(max_attempts=5, exceptions=(ServerTimeoutError))
|
|
1369
1409
|
def get_items(self, kind: str, **kwargs: Any) -> list[dict[str, Any]]:
|
|
1370
|
-
|
|
1371
|
-
obj_client = self._get_obj_client(
|
|
1410
|
+
resource = self.get_api_resource(kind)
|
|
1411
|
+
obj_client = self._get_obj_client(
|
|
1412
|
+
group_version=resource.group_version, kind=resource.kind
|
|
1413
|
+
)
|
|
1372
1414
|
|
|
1373
1415
|
namespace = ""
|
|
1374
1416
|
if "namespace" in kwargs:
|
|
@@ -1420,8 +1462,10 @@ class OCNative(OCCli):
|
|
|
1420
1462
|
name: str | None = None,
|
|
1421
1463
|
allow_not_found: bool = False,
|
|
1422
1464
|
) -> dict[str, Any]:
|
|
1423
|
-
|
|
1424
|
-
obj_client = self._get_obj_client(
|
|
1465
|
+
resource = self.get_api_resource(kind)
|
|
1466
|
+
obj_client = self._get_obj_client(
|
|
1467
|
+
group_version=resource.group_version, kind=resource.kind
|
|
1468
|
+
)
|
|
1425
1469
|
try:
|
|
1426
1470
|
obj = obj_client.get(
|
|
1427
1471
|
name=name,
|
|
@@ -1435,8 +1479,10 @@ class OCNative(OCCli):
|
|
|
1435
1479
|
raise StatusCodeError(f"[{self.server}]: {e}") from None
|
|
1436
1480
|
|
|
1437
1481
|
def get_all(self, kind: str, all_namespaces: bool = False) -> dict[str, Any]:
|
|
1438
|
-
|
|
1439
|
-
obj_client = self._get_obj_client(
|
|
1482
|
+
resource = self.get_api_resource(kind)
|
|
1483
|
+
obj_client = self._get_obj_client(
|
|
1484
|
+
group_version=resource.group_version, kind=resource.kind
|
|
1485
|
+
)
|
|
1440
1486
|
try:
|
|
1441
1487
|
return obj_client.get(_request_timeout=REQUEST_TIMEOUT).to_dict()
|
|
1442
1488
|
except NotFoundError as e:
|