qontract-reconcile 0.10.0__py3-none-any.whl → 0.10.1.dev1203__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.1.dev1203.dist-info/METADATA +500 -0
- qontract_reconcile-0.10.1.dev1203.dist-info/RECORD +771 -0
- {qontract_reconcile-0.10.0.dist-info → qontract_reconcile-0.10.1.dev1203.dist-info}/WHEEL +1 -2
- {qontract_reconcile-0.10.0.dist-info → qontract_reconcile-0.10.1.dev1203.dist-info}/entry_points.txt +4 -2
- reconcile/acs_notifiers.py +126 -0
- reconcile/acs_policies.py +243 -0
- reconcile/acs_rbac.py +596 -0
- reconcile/aus/advanced_upgrade_service.py +621 -8
- reconcile/aus/aus_label_source.py +115 -0
- reconcile/aus/base.py +1053 -353
- reconcile/{utils → aus}/cluster_version_data.py +27 -12
- reconcile/aus/healthchecks.py +77 -0
- reconcile/aus/metrics.py +158 -0
- reconcile/aus/models.py +245 -5
- reconcile/aus/node_pool_spec.py +35 -0
- reconcile/aus/ocm_addons_upgrade_scheduler_org.py +225 -110
- reconcile/aus/ocm_upgrade_scheduler.py +76 -71
- reconcile/aus/ocm_upgrade_scheduler_org.py +81 -23
- reconcile/aus/version_gate_approver.py +204 -0
- reconcile/aus/version_gates/__init__.py +12 -0
- reconcile/aus/version_gates/handler.py +33 -0
- reconcile/aus/version_gates/ingress_gate_handler.py +32 -0
- reconcile/aus/version_gates/ocp_gate_handler.py +26 -0
- reconcile/aus/version_gates/sts_version_gate_handler.py +100 -0
- reconcile/aws_account_manager/README.md +5 -0
- reconcile/aws_account_manager/integration.py +373 -0
- reconcile/aws_account_manager/merge_request_manager.py +114 -0
- reconcile/aws_account_manager/metrics.py +39 -0
- reconcile/aws_account_manager/reconciler.py +403 -0
- reconcile/aws_account_manager/utils.py +41 -0
- reconcile/aws_ami_cleanup/integration.py +273 -0
- reconcile/aws_ami_share.py +18 -14
- reconcile/aws_cloudwatch_log_retention/integration.py +253 -0
- reconcile/aws_iam_keys.py +1 -1
- reconcile/aws_iam_password_reset.py +56 -20
- reconcile/aws_saml_idp/integration.py +204 -0
- reconcile/aws_saml_roles/integration.py +322 -0
- reconcile/aws_support_cases_sos.py +2 -2
- reconcile/aws_version_sync/integration.py +430 -0
- reconcile/aws_version_sync/merge_request_manager/merge_request.py +156 -0
- reconcile/aws_version_sync/merge_request_manager/merge_request_manager.py +160 -0
- reconcile/aws_version_sync/utils.py +64 -0
- reconcile/blackbox_exporter_endpoint_monitoring.py +2 -5
- reconcile/change_owners/README.md +34 -0
- reconcile/change_owners/approver.py +7 -9
- reconcile/change_owners/bundle.py +134 -9
- reconcile/change_owners/change_log_tracking.py +236 -0
- reconcile/change_owners/change_owners.py +204 -194
- reconcile/change_owners/change_types.py +183 -265
- reconcile/change_owners/changes.py +488 -0
- reconcile/change_owners/decision.py +120 -41
- reconcile/change_owners/diff.py +63 -92
- reconcile/change_owners/implicit_ownership.py +19 -16
- reconcile/change_owners/self_service_roles.py +158 -35
- reconcile/change_owners/tester.py +20 -18
- reconcile/checkpoint.py +4 -6
- reconcile/cli.py +1523 -242
- reconcile/closedbox_endpoint_monitoring_base.py +10 -17
- reconcile/cluster_auth_rhidp/integration.py +257 -0
- reconcile/cluster_deployment_mapper.py +2 -5
- reconcile/cna/assets/asset.py +4 -7
- reconcile/cna/assets/null.py +2 -5
- reconcile/cna/integration.py +2 -3
- reconcile/cna/state.py +6 -9
- reconcile/dashdotdb_base.py +31 -10
- reconcile/dashdotdb_cso.py +3 -6
- reconcile/dashdotdb_dora.py +530 -0
- reconcile/dashdotdb_dvo.py +10 -13
- reconcile/dashdotdb_slo.py +75 -19
- reconcile/database_access_manager.py +753 -0
- reconcile/deadmanssnitch.py +207 -0
- reconcile/dynatrace_token_provider/dependencies.py +69 -0
- reconcile/dynatrace_token_provider/integration.py +656 -0
- reconcile/dynatrace_token_provider/metrics.py +62 -0
- reconcile/dynatrace_token_provider/model.py +14 -0
- reconcile/dynatrace_token_provider/ocm.py +140 -0
- reconcile/dynatrace_token_provider/validate.py +48 -0
- reconcile/endpoints_discovery/integration.py +348 -0
- reconcile/endpoints_discovery/merge_request.py +96 -0
- reconcile/endpoints_discovery/merge_request_manager.py +178 -0
- reconcile/external_resources/aws.py +204 -0
- reconcile/external_resources/factories.py +163 -0
- reconcile/external_resources/integration.py +194 -0
- reconcile/external_resources/integration_secrets_sync.py +47 -0
- reconcile/external_resources/manager.py +405 -0
- reconcile/external_resources/meta.py +17 -0
- reconcile/external_resources/metrics.py +95 -0
- reconcile/external_resources/model.py +350 -0
- reconcile/external_resources/reconciler.py +265 -0
- reconcile/external_resources/secrets_sync.py +465 -0
- reconcile/external_resources/state.py +258 -0
- reconcile/gabi_authorized_users.py +19 -11
- reconcile/gcr_mirror.py +43 -34
- reconcile/github_org.py +4 -6
- reconcile/github_owners.py +1 -1
- reconcile/github_repo_invites.py +2 -5
- reconcile/gitlab_fork_compliance.py +14 -13
- reconcile/gitlab_housekeeping.py +185 -91
- reconcile/gitlab_labeler.py +15 -14
- reconcile/gitlab_members.py +126 -120
- reconcile/gitlab_owners.py +53 -66
- reconcile/gitlab_permissions.py +167 -6
- reconcile/glitchtip/README.md +150 -0
- reconcile/glitchtip/integration.py +99 -51
- reconcile/glitchtip/reconciler.py +99 -70
- reconcile/glitchtip_project_alerts/__init__.py +0 -0
- reconcile/glitchtip_project_alerts/integration.py +333 -0
- reconcile/glitchtip_project_dsn/integration.py +43 -43
- reconcile/gql_definitions/acs/__init__.py +0 -0
- reconcile/gql_definitions/acs/acs_instances.py +83 -0
- reconcile/gql_definitions/acs/acs_policies.py +239 -0
- reconcile/gql_definitions/acs/acs_rbac.py +111 -0
- reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +46 -8
- reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +38 -8
- reconcile/gql_definitions/app_interface_metrics_exporter/__init__.py +0 -0
- reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +61 -0
- reconcile/gql_definitions/aws_account_manager/__init__.py +0 -0
- reconcile/gql_definitions/aws_account_manager/aws_accounts.py +177 -0
- reconcile/gql_definitions/aws_ami_cleanup/__init__.py +0 -0
- reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +161 -0
- reconcile/gql_definitions/aws_saml_idp/__init__.py +0 -0
- reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +117 -0
- reconcile/gql_definitions/aws_saml_roles/__init__.py +0 -0
- reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +117 -0
- reconcile/gql_definitions/aws_saml_roles/roles.py +97 -0
- reconcile/gql_definitions/aws_version_sync/__init__.py +0 -0
- reconcile/gql_definitions/aws_version_sync/clusters.py +83 -0
- reconcile/gql_definitions/aws_version_sync/namespaces.py +143 -0
- reconcile/gql_definitions/change_owners/queries/change_types.py +16 -29
- reconcile/gql_definitions/change_owners/queries/self_service_roles.py +45 -11
- reconcile/gql_definitions/cluster_auth_rhidp/__init__.py +0 -0
- reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +128 -0
- reconcile/gql_definitions/cna/queries/cna_provisioners.py +6 -8
- reconcile/gql_definitions/cna/queries/cna_resources.py +3 -5
- reconcile/gql_definitions/common/alerting_services_settings.py +2 -2
- reconcile/gql_definitions/common/app_code_component_repos.py +9 -5
- reconcile/gql_definitions/{glitchtip/glitchtip_settings.py → common/app_interface_custom_messages.py} +14 -16
- reconcile/gql_definitions/common/app_interface_dms_settings.py +86 -0
- reconcile/gql_definitions/common/app_interface_repo_settings.py +2 -2
- reconcile/gql_definitions/common/app_interface_state_settings.py +3 -5
- reconcile/gql_definitions/common/app_interface_vault_settings.py +3 -5
- reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +120 -0
- reconcile/gql_definitions/common/apps.py +72 -0
- reconcile/gql_definitions/common/aws_vpc_requests.py +109 -0
- reconcile/gql_definitions/common/aws_vpcs.py +84 -0
- reconcile/gql_definitions/common/clusters.py +120 -254
- reconcile/gql_definitions/common/clusters_minimal.py +11 -35
- reconcile/gql_definitions/common/clusters_with_dms.py +72 -0
- reconcile/gql_definitions/common/clusters_with_peering.py +70 -98
- reconcile/gql_definitions/common/github_orgs.py +2 -2
- reconcile/gql_definitions/common/jira_settings.py +68 -0
- reconcile/gql_definitions/common/jiralert_settings.py +68 -0
- reconcile/gql_definitions/common/namespaces.py +74 -32
- reconcile/gql_definitions/common/namespaces_minimal.py +4 -10
- reconcile/gql_definitions/common/ocm_env_telemeter.py +95 -0
- reconcile/gql_definitions/common/ocm_environments.py +4 -2
- reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
- reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -11
- reconcile/gql_definitions/common/pipeline_providers.py +45 -90
- reconcile/gql_definitions/common/quay_instances.py +64 -0
- reconcile/gql_definitions/common/quay_orgs.py +68 -0
- reconcile/gql_definitions/common/reserved_networks.py +94 -0
- reconcile/gql_definitions/common/saas_files.py +133 -95
- reconcile/gql_definitions/common/saas_target_namespaces.py +41 -26
- reconcile/gql_definitions/common/saasherder_settings.py +2 -2
- reconcile/gql_definitions/common/slack_workspaces.py +62 -0
- reconcile/gql_definitions/common/smtp_client_settings.py +2 -2
- reconcile/gql_definitions/common/state_aws_account.py +77 -0
- reconcile/gql_definitions/common/users.py +3 -2
- reconcile/gql_definitions/cost_report/__init__.py +0 -0
- reconcile/gql_definitions/cost_report/app_names.py +68 -0
- reconcile/gql_definitions/cost_report/cost_namespaces.py +86 -0
- reconcile/gql_definitions/cost_report/settings.py +77 -0
- reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +42 -12
- reconcile/gql_definitions/dynatrace_token_provider/__init__.py +0 -0
- reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +79 -0
- reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +84 -0
- reconcile/gql_definitions/endpoints_discovery/__init__.py +0 -0
- reconcile/gql_definitions/endpoints_discovery/namespaces.py +127 -0
- reconcile/gql_definitions/external_resources/__init__.py +0 -0
- reconcile/gql_definitions/external_resources/aws_accounts.py +73 -0
- reconcile/gql_definitions/external_resources/external_resources_modules.py +78 -0
- reconcile/gql_definitions/external_resources/external_resources_namespaces.py +1111 -0
- reconcile/gql_definitions/external_resources/external_resources_settings.py +98 -0
- reconcile/gql_definitions/fragments/aus_organization.py +34 -39
- reconcile/gql_definitions/fragments/aws_account_common.py +62 -0
- reconcile/gql_definitions/fragments/aws_account_managed.py +57 -0
- reconcile/gql_definitions/fragments/aws_account_sso.py +35 -0
- reconcile/gql_definitions/fragments/aws_infra_management_account.py +2 -2
- reconcile/gql_definitions/fragments/aws_vpc.py +47 -0
- reconcile/gql_definitions/fragments/aws_vpc_request.py +65 -0
- reconcile/gql_definitions/fragments/aws_vpc_request_subnet.py +29 -0
- reconcile/gql_definitions/fragments/deplopy_resources.py +7 -7
- reconcile/gql_definitions/fragments/disable.py +28 -0
- reconcile/gql_definitions/fragments/jumphost_common_fields.py +2 -2
- reconcile/gql_definitions/fragments/membership_source.py +47 -0
- reconcile/gql_definitions/fragments/minimal_ocm_organization.py +29 -0
- reconcile/gql_definitions/fragments/oc_connection_cluster.py +4 -9
- reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
- reconcile/gql_definitions/fragments/pipeline_provider_retention.py +30 -0
- reconcile/gql_definitions/fragments/prometheus_instance.py +48 -0
- reconcile/gql_definitions/fragments/resource_limits_requirements.py +29 -0
- reconcile/gql_definitions/fragments/{resource_requirements.py → resource_requests_requirements.py} +3 -3
- reconcile/gql_definitions/fragments/resource_values.py +2 -2
- reconcile/gql_definitions/fragments/saas_target_namespace.py +55 -12
- reconcile/gql_definitions/fragments/serviceaccount_token.py +38 -0
- reconcile/gql_definitions/fragments/terraform_state.py +36 -0
- reconcile/gql_definitions/fragments/upgrade_policy.py +5 -3
- reconcile/gql_definitions/fragments/user.py +3 -2
- reconcile/gql_definitions/fragments/vault_secret.py +2 -2
- reconcile/gql_definitions/gitlab_members/gitlab_instances.py +6 -2
- reconcile/gql_definitions/gitlab_members/permissions.py +3 -5
- reconcile/gql_definitions/glitchtip/glitchtip_instance.py +16 -2
- reconcile/gql_definitions/glitchtip/glitchtip_project.py +22 -23
- reconcile/gql_definitions/glitchtip_project_alerts/__init__.py +0 -0
- reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +173 -0
- reconcile/gql_definitions/integrations/integrations.py +62 -45
- reconcile/gql_definitions/introspection.json +51176 -0
- reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +13 -5
- reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +79 -0
- reconcile/gql_definitions/jira/__init__.py +0 -0
- reconcile/gql_definitions/jira/jira_servers.py +80 -0
- reconcile/gql_definitions/jira_permissions_validator/__init__.py +0 -0
- reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +131 -0
- reconcile/gql_definitions/jumphosts/jumphosts.py +3 -5
- reconcile/gql_definitions/ldap_groups/__init__.py +0 -0
- reconcile/gql_definitions/ldap_groups/roles.py +111 -0
- reconcile/gql_definitions/ldap_groups/settings.py +79 -0
- reconcile/gql_definitions/maintenance/__init__.py +0 -0
- reconcile/gql_definitions/maintenance/maintenances.py +101 -0
- reconcile/gql_definitions/membershipsources/__init__.py +0 -0
- reconcile/gql_definitions/membershipsources/roles.py +112 -0
- reconcile/gql_definitions/ocm_labels/__init__.py +0 -0
- reconcile/gql_definitions/ocm_labels/clusters.py +112 -0
- reconcile/gql_definitions/ocm_labels/organizations.py +78 -0
- reconcile/gql_definitions/ocm_subscription_labels/__init__.py +0 -0
- reconcile/gql_definitions/openshift_cluster_bots/__init__.py +0 -0
- reconcile/gql_definitions/openshift_cluster_bots/clusters.py +126 -0
- reconcile/gql_definitions/openshift_groups/managed_groups.py +2 -2
- reconcile/gql_definitions/openshift_groups/managed_roles.py +3 -2
- reconcile/gql_definitions/openshift_serviceaccount_tokens/__init__.py +0 -0
- reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +132 -0
- reconcile/gql_definitions/quay_membership/quay_membership.py +3 -5
- reconcile/gql_definitions/rhidp/__init__.py +0 -0
- reconcile/gql_definitions/rhidp/organizations.py +96 -0
- reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +2 -2
- reconcile/gql_definitions/service_dependencies/service_dependencies.py +9 -31
- reconcile/gql_definitions/sharding/aws_accounts.py +2 -2
- reconcile/gql_definitions/sharding/ocm_organization.py +63 -0
- reconcile/gql_definitions/skupper_network/site_controller_template.py +2 -2
- reconcile/gql_definitions/skupper_network/skupper_networks.py +12 -38
- reconcile/gql_definitions/slack_usergroups/clusters.py +2 -2
- reconcile/gql_definitions/slack_usergroups/permissions.py +8 -15
- reconcile/gql_definitions/slack_usergroups/users.py +3 -2
- reconcile/gql_definitions/slo_documents/__init__.py +0 -0
- reconcile/gql_definitions/slo_documents/slo_documents.py +142 -0
- reconcile/gql_definitions/status_board/__init__.py +0 -0
- reconcile/gql_definitions/status_board/status_board.py +163 -0
- reconcile/gql_definitions/statuspage/statuspages.py +56 -7
- reconcile/gql_definitions/templating/__init__.py +0 -0
- reconcile/gql_definitions/templating/template_collection.py +130 -0
- reconcile/gql_definitions/templating/templates.py +108 -0
- reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +4 -8
- reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +8 -8
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +6 -8
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +45 -56
- reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +4 -8
- reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +4 -8
- reconcile/gql_definitions/terraform_init/__init__.py +0 -0
- reconcile/gql_definitions/terraform_init/aws_accounts.py +93 -0
- reconcile/gql_definitions/terraform_repo/__init__.py +0 -0
- reconcile/gql_definitions/terraform_repo/terraform_repo.py +141 -0
- reconcile/gql_definitions/terraform_resources/database_access_manager.py +158 -0
- reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +153 -162
- reconcile/gql_definitions/terraform_tgw_attachments/__init__.py +0 -0
- reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +119 -0
- reconcile/gql_definitions/unleash_feature_toggles/__init__.py +0 -0
- reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +113 -0
- reconcile/gql_definitions/vault_instances/vault_instances.py +17 -50
- reconcile/gql_definitions/vault_policies/vault_policies.py +2 -2
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +49 -12
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +7 -2
- reconcile/integrations_manager.py +25 -13
- reconcile/jenkins/types.py +5 -1
- reconcile/jenkins_base.py +36 -0
- reconcile/jenkins_job_builder.py +10 -48
- reconcile/jenkins_job_builds_cleaner.py +40 -25
- reconcile/jenkins_job_cleaner.py +1 -3
- reconcile/jenkins_roles.py +22 -26
- reconcile/jenkins_webhooks.py +9 -6
- reconcile/jenkins_worker_fleets.py +11 -6
- reconcile/jira_permissions_validator.py +340 -0
- reconcile/jira_watcher.py +3 -5
- reconcile/ldap_groups/__init__.py +0 -0
- reconcile/ldap_groups/integration.py +279 -0
- reconcile/ldap_users.py +3 -0
- reconcile/ocm/types.py +39 -59
- reconcile/ocm_additional_routers.py +0 -1
- reconcile/ocm_addons_upgrade_tests_trigger.py +10 -15
- reconcile/ocm_aws_infrastructure_access.py +30 -32
- reconcile/ocm_clusters.py +217 -130
- reconcile/ocm_external_configuration_labels.py +15 -0
- reconcile/ocm_github_idp.py +1 -1
- reconcile/ocm_groups.py +25 -5
- reconcile/ocm_internal_notifications/__init__.py +0 -0
- reconcile/ocm_internal_notifications/integration.py +119 -0
- reconcile/ocm_labels/__init__.py +0 -0
- reconcile/ocm_labels/integration.py +409 -0
- reconcile/ocm_machine_pools.py +517 -108
- reconcile/ocm_upgrade_scheduler_org_updater.py +15 -11
- reconcile/openshift_base.py +609 -207
- reconcile/openshift_cluster_bots.py +344 -0
- reconcile/openshift_clusterrolebindings.py +15 -15
- reconcile/openshift_groups.py +42 -45
- reconcile/openshift_limitranges.py +1 -0
- reconcile/openshift_namespace_labels.py +22 -28
- reconcile/openshift_namespaces.py +22 -22
- reconcile/openshift_network_policies.py +4 -8
- reconcile/openshift_prometheus_rules.py +43 -0
- reconcile/openshift_resourcequotas.py +2 -16
- reconcile/openshift_resources.py +12 -10
- reconcile/openshift_resources_base.py +304 -328
- reconcile/openshift_rolebindings.py +18 -20
- reconcile/openshift_saas_deploy.py +105 -21
- reconcile/openshift_saas_deploy_change_tester.py +30 -35
- reconcile/openshift_saas_deploy_trigger_base.py +39 -36
- reconcile/openshift_saas_deploy_trigger_cleaner.py +41 -27
- reconcile/openshift_saas_deploy_trigger_configs.py +1 -2
- reconcile/openshift_saas_deploy_trigger_images.py +1 -2
- reconcile/openshift_saas_deploy_trigger_moving_commits.py +1 -2
- reconcile/openshift_saas_deploy_trigger_upstream_jobs.py +1 -2
- reconcile/openshift_serviceaccount_tokens.py +138 -74
- reconcile/openshift_tekton_resources.py +89 -24
- reconcile/openshift_upgrade_watcher.py +110 -62
- reconcile/openshift_users.py +16 -15
- reconcile/openshift_vault_secrets.py +11 -6
- reconcile/oum/__init__.py +0 -0
- reconcile/oum/base.py +387 -0
- reconcile/oum/labelset.py +55 -0
- reconcile/oum/metrics.py +71 -0
- reconcile/oum/models.py +69 -0
- reconcile/oum/providers.py +59 -0
- reconcile/oum/standalone.py +196 -0
- reconcile/prometheus_rules_tester/integration.py +31 -23
- reconcile/quay_base.py +4 -1
- reconcile/quay_membership.py +1 -2
- reconcile/quay_mirror.py +111 -61
- reconcile/quay_mirror_org.py +34 -21
- reconcile/quay_permissions.py +7 -3
- reconcile/quay_repos.py +24 -32
- reconcile/queries.py +263 -198
- reconcile/query_validator.py +3 -5
- reconcile/resource_scraper.py +3 -4
- reconcile/{template_tester.py → resource_template_tester.py} +3 -3
- reconcile/rhidp/__init__.py +0 -0
- reconcile/rhidp/common.py +214 -0
- reconcile/rhidp/metrics.py +20 -0
- reconcile/rhidp/ocm_oidc_idp/__init__.py +0 -0
- reconcile/rhidp/ocm_oidc_idp/base.py +221 -0
- reconcile/rhidp/ocm_oidc_idp/integration.py +56 -0
- reconcile/rhidp/ocm_oidc_idp/metrics.py +22 -0
- reconcile/rhidp/sso_client/__init__.py +0 -0
- reconcile/rhidp/sso_client/base.py +266 -0
- reconcile/rhidp/sso_client/integration.py +60 -0
- reconcile/rhidp/sso_client/metrics.py +39 -0
- reconcile/run_integration.py +293 -0
- reconcile/saas_auto_promotions_manager/integration.py +69 -24
- reconcile/saas_auto_promotions_manager/merge_request_manager/batcher.py +208 -0
- reconcile/saas_auto_promotions_manager/merge_request_manager/desired_state.py +28 -0
- reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request.py +3 -4
- reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request_manager_v2.py +172 -0
- reconcile/saas_auto_promotions_manager/merge_request_manager/metrics.py +42 -0
- reconcile/saas_auto_promotions_manager/merge_request_manager/mr_parser.py +226 -0
- reconcile/saas_auto_promotions_manager/merge_request_manager/open_merge_requests.py +23 -0
- reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +108 -32
- reconcile/saas_auto_promotions_manager/meta.py +4 -0
- reconcile/saas_auto_promotions_manager/publisher.py +32 -4
- reconcile/saas_auto_promotions_manager/s3_exporter.py +77 -0
- reconcile/saas_auto_promotions_manager/subscriber.py +110 -23
- reconcile/saas_auto_promotions_manager/utils/saas_files_inventory.py +48 -41
- reconcile/saas_file_validator.py +16 -6
- reconcile/sendgrid_teammates.py +27 -12
- reconcile/service_dependencies.py +0 -3
- reconcile/signalfx_endpoint_monitoring.py +2 -5
- reconcile/skupper_network/integration.py +10 -11
- reconcile/skupper_network/models.py +3 -5
- reconcile/skupper_network/reconciler.py +28 -35
- reconcile/skupper_network/site_controller.py +8 -8
- reconcile/slack_base.py +4 -7
- reconcile/slack_usergroups.py +249 -171
- reconcile/sql_query.py +324 -171
- reconcile/status.py +0 -1
- reconcile/status_board.py +275 -0
- reconcile/statuspage/__init__.py +0 -5
- reconcile/statuspage/atlassian.py +219 -80
- reconcile/statuspage/integration.py +9 -97
- reconcile/statuspage/integrations/__init__.py +0 -0
- reconcile/statuspage/integrations/components.py +77 -0
- reconcile/statuspage/integrations/maintenances.py +111 -0
- reconcile/statuspage/page.py +107 -72
- reconcile/statuspage/state.py +6 -11
- reconcile/statuspage/status.py +8 -12
- reconcile/templates/rosa-classic-cluster-creation.sh.j2 +60 -0
- reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +61 -0
- reconcile/templating/__init__.py +0 -0
- reconcile/templating/lib/__init__.py +0 -0
- reconcile/templating/lib/merge_request_manager.py +180 -0
- reconcile/templating/lib/model.py +20 -0
- reconcile/templating/lib/rendering.py +191 -0
- reconcile/templating/renderer.py +410 -0
- reconcile/templating/validator.py +153 -0
- reconcile/terraform_aws_route53.py +13 -10
- reconcile/terraform_cloudflare_dns.py +92 -122
- reconcile/terraform_cloudflare_resources.py +15 -13
- reconcile/terraform_cloudflare_users.py +27 -27
- reconcile/terraform_init/__init__.py +0 -0
- reconcile/terraform_init/integration.py +165 -0
- reconcile/terraform_init/merge_request.py +57 -0
- reconcile/terraform_init/merge_request_manager.py +102 -0
- reconcile/terraform_repo.py +403 -0
- reconcile/terraform_resources.py +266 -168
- reconcile/terraform_tgw_attachments.py +417 -167
- reconcile/terraform_users.py +40 -17
- reconcile/terraform_vpc_peerings.py +310 -142
- reconcile/terraform_vpc_resources/__init__.py +0 -0
- reconcile/terraform_vpc_resources/integration.py +220 -0
- reconcile/terraform_vpc_resources/merge_request.py +57 -0
- reconcile/terraform_vpc_resources/merge_request_manager.py +107 -0
- reconcile/typed_queries/alerting_services_settings.py +1 -2
- reconcile/typed_queries/app_interface_custom_messages.py +24 -0
- reconcile/typed_queries/app_interface_deadmanssnitch_settings.py +17 -0
- reconcile/typed_queries/app_interface_metrics_exporter/__init__.py +0 -0
- reconcile/typed_queries/app_interface_metrics_exporter/onboarding_status.py +13 -0
- reconcile/typed_queries/app_interface_repo_url.py +1 -2
- reconcile/typed_queries/app_interface_state_settings.py +1 -3
- reconcile/typed_queries/app_interface_vault_settings.py +1 -2
- reconcile/typed_queries/app_quay_repos_escalation_policies.py +14 -0
- reconcile/typed_queries/apps.py +11 -0
- reconcile/typed_queries/aws_vpc_requests.py +9 -0
- reconcile/typed_queries/aws_vpcs.py +12 -0
- reconcile/typed_queries/cloudflare.py +10 -0
- reconcile/typed_queries/clusters.py +7 -5
- reconcile/typed_queries/clusters_minimal.py +6 -5
- reconcile/typed_queries/clusters_with_dms.py +16 -0
- reconcile/typed_queries/cost_report/__init__.py +0 -0
- reconcile/typed_queries/cost_report/app_names.py +22 -0
- reconcile/typed_queries/cost_report/cost_namespaces.py +43 -0
- reconcile/typed_queries/cost_report/settings.py +15 -0
- reconcile/typed_queries/dynatrace.py +10 -0
- reconcile/typed_queries/dynatrace_environments.py +14 -0
- reconcile/typed_queries/dynatrace_token_provider_token_specs.py +14 -0
- reconcile/typed_queries/external_resources.py +46 -0
- reconcile/typed_queries/get_state_aws_account.py +20 -0
- reconcile/typed_queries/glitchtip.py +10 -0
- reconcile/typed_queries/jenkins.py +25 -0
- reconcile/typed_queries/jira.py +7 -0
- reconcile/typed_queries/jira_settings.py +16 -0
- reconcile/typed_queries/jiralert_settings.py +22 -0
- reconcile/typed_queries/ocm.py +8 -0
- reconcile/typed_queries/pagerduty_instances.py +2 -7
- reconcile/typed_queries/quay.py +23 -0
- reconcile/typed_queries/repos.py +20 -8
- reconcile/typed_queries/reserved_networks.py +12 -0
- reconcile/typed_queries/saas_files.py +221 -167
- reconcile/typed_queries/slack.py +7 -0
- reconcile/typed_queries/slo_documents.py +12 -0
- reconcile/typed_queries/status_board.py +58 -0
- reconcile/typed_queries/tekton_pipeline_providers.py +1 -2
- reconcile/typed_queries/terraform_namespaces.py +1 -2
- reconcile/typed_queries/terraform_tgw_attachments/__init__.py +0 -0
- reconcile/typed_queries/terraform_tgw_attachments/aws_accounts.py +16 -0
- reconcile/typed_queries/unleash.py +10 -0
- reconcile/typed_queries/users.py +11 -0
- reconcile/typed_queries/vault.py +10 -0
- reconcile/unleash_feature_toggles/__init__.py +0 -0
- reconcile/unleash_feature_toggles/integration.py +287 -0
- reconcile/utils/acs/__init__.py +0 -0
- reconcile/utils/acs/base.py +81 -0
- reconcile/utils/acs/notifiers.py +143 -0
- reconcile/utils/acs/policies.py +163 -0
- reconcile/utils/acs/rbac.py +277 -0
- reconcile/utils/aggregated_list.py +11 -9
- reconcile/utils/amtool.py +6 -4
- reconcile/utils/aws_api.py +279 -66
- reconcile/utils/aws_api_typed/__init__.py +0 -0
- reconcile/utils/aws_api_typed/account.py +23 -0
- reconcile/utils/aws_api_typed/api.py +273 -0
- reconcile/utils/aws_api_typed/dynamodb.py +16 -0
- reconcile/utils/aws_api_typed/iam.py +67 -0
- reconcile/utils/aws_api_typed/organization.py +152 -0
- reconcile/utils/aws_api_typed/s3.py +26 -0
- reconcile/utils/aws_api_typed/service_quotas.py +79 -0
- reconcile/utils/aws_api_typed/sts.py +36 -0
- reconcile/utils/aws_api_typed/support.py +79 -0
- reconcile/utils/aws_helper.py +42 -3
- reconcile/utils/batches.py +11 -0
- reconcile/utils/binary.py +7 -9
- reconcile/utils/cloud_resource_best_practice/__init__.py +0 -0
- reconcile/utils/cloud_resource_best_practice/aws_rds.py +66 -0
- reconcile/utils/clusterhealth/__init__.py +0 -0
- reconcile/utils/clusterhealth/providerbase.py +39 -0
- reconcile/utils/clusterhealth/telemeter.py +39 -0
- reconcile/utils/config.py +3 -4
- reconcile/utils/deadmanssnitch_api.py +86 -0
- reconcile/utils/differ.py +205 -0
- reconcile/utils/disabled_integrations.py +4 -6
- reconcile/utils/dynatrace/__init__.py +0 -0
- reconcile/utils/dynatrace/client.py +93 -0
- reconcile/utils/early_exit_cache.py +289 -0
- reconcile/utils/elasticsearch_exceptions.py +5 -0
- reconcile/utils/environ.py +2 -2
- reconcile/utils/exceptions.py +4 -0
- reconcile/utils/expiration.py +4 -8
- reconcile/utils/extended_early_exit.py +210 -0
- reconcile/utils/external_resource_spec.py +34 -12
- reconcile/utils/external_resources.py +48 -20
- reconcile/utils/filtering.py +16 -0
- reconcile/utils/git.py +49 -16
- reconcile/utils/github_api.py +10 -9
- reconcile/utils/gitlab_api.py +333 -190
- reconcile/utils/glitchtip/client.py +97 -100
- reconcile/utils/glitchtip/models.py +89 -11
- reconcile/utils/gql.py +157 -58
- reconcile/utils/grouping.py +17 -0
- reconcile/utils/helm.py +89 -18
- reconcile/utils/helpers.py +51 -0
- reconcile/utils/imap_client.py +5 -6
- reconcile/utils/internal_groups/__init__.py +0 -0
- reconcile/utils/internal_groups/client.py +160 -0
- reconcile/utils/internal_groups/models.py +71 -0
- reconcile/utils/jenkins_api.py +10 -34
- reconcile/utils/jinja2/__init__.py +0 -0
- reconcile/utils/{jinja2_ext.py → jinja2/extensions.py} +6 -4
- reconcile/utils/jinja2/filters.py +142 -0
- reconcile/utils/jinja2/utils.py +278 -0
- reconcile/utils/jira_client.py +165 -8
- reconcile/utils/jjb_client.py +47 -35
- reconcile/utils/jobcontroller/__init__.py +0 -0
- reconcile/utils/jobcontroller/controller.py +413 -0
- reconcile/utils/jobcontroller/models.py +195 -0
- reconcile/utils/jsonpath.py +4 -5
- reconcile/utils/jump_host.py +13 -12
- reconcile/utils/keycloak.py +106 -0
- reconcile/utils/ldap_client.py +35 -6
- reconcile/utils/lean_terraform_client.py +115 -6
- reconcile/utils/membershipsources/__init__.py +0 -0
- reconcile/utils/membershipsources/app_interface_resolver.py +60 -0
- reconcile/utils/membershipsources/models.py +91 -0
- reconcile/utils/membershipsources/resolver.py +110 -0
- reconcile/utils/merge_request_manager/__init__.py +0 -0
- reconcile/utils/merge_request_manager/merge_request_manager.py +99 -0
- reconcile/utils/merge_request_manager/parser.py +67 -0
- reconcile/utils/metrics.py +511 -1
- reconcile/utils/models.py +123 -0
- reconcile/utils/mr/README.md +198 -0
- reconcile/utils/mr/__init__.py +14 -10
- reconcile/utils/mr/app_interface_reporter.py +2 -2
- reconcile/utils/mr/aws_access.py +4 -4
- reconcile/utils/mr/base.py +51 -31
- reconcile/utils/mr/clusters_updates.py +10 -7
- reconcile/utils/mr/glitchtip_access_reporter.py +2 -4
- reconcile/utils/mr/labels.py +14 -1
- reconcile/utils/mr/notificator.py +1 -3
- reconcile/utils/mr/ocm_update_recommended_version.py +1 -2
- reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py +7 -3
- reconcile/utils/mr/promote_qontract.py +203 -0
- reconcile/utils/mr/user_maintenance.py +24 -4
- reconcile/utils/oauth2_backend_application_session.py +132 -0
- reconcile/utils/oc.py +194 -170
- reconcile/utils/oc_connection_parameters.py +40 -51
- reconcile/utils/oc_filters.py +11 -13
- reconcile/utils/oc_map.py +14 -35
- reconcile/utils/ocm/__init__.py +30 -1
- reconcile/utils/ocm/addons.py +228 -0
- reconcile/utils/ocm/base.py +618 -5
- reconcile/utils/ocm/cluster_groups.py +5 -56
- reconcile/utils/ocm/clusters.py +111 -99
- reconcile/utils/ocm/identity_providers.py +66 -0
- reconcile/utils/ocm/label_sources.py +75 -0
- reconcile/utils/ocm/labels.py +139 -54
- reconcile/utils/ocm/manifests.py +39 -0
- reconcile/utils/ocm/ocm.py +182 -928
- reconcile/utils/ocm/products.py +758 -0
- reconcile/utils/ocm/search_filters.py +20 -28
- reconcile/utils/ocm/service_log.py +32 -79
- reconcile/utils/ocm/sre_capability_labels.py +51 -0
- reconcile/utils/ocm/status_board.py +66 -0
- reconcile/utils/ocm/subscriptions.py +49 -59
- reconcile/utils/ocm/syncsets.py +39 -0
- reconcile/utils/ocm/upgrades.py +181 -0
- reconcile/utils/ocm_base_client.py +71 -36
- reconcile/utils/openshift_resource.py +113 -67
- reconcile/utils/output.py +18 -11
- reconcile/utils/pagerduty_api.py +16 -10
- reconcile/utils/parse_dhms_duration.py +13 -1
- reconcile/utils/prometheus.py +123 -0
- reconcile/utils/promotion_state.py +56 -19
- reconcile/utils/promtool.py +5 -8
- reconcile/utils/quay_api.py +13 -25
- reconcile/utils/raw_github_api.py +3 -5
- reconcile/utils/repo_owners.py +2 -8
- reconcile/utils/rest_api_base.py +126 -0
- reconcile/utils/rosa/__init__.py +0 -0
- reconcile/utils/rosa/rosa_cli.py +310 -0
- reconcile/utils/rosa/session.py +201 -0
- reconcile/utils/ruamel.py +16 -0
- reconcile/utils/runtime/__init__.py +0 -1
- reconcile/utils/runtime/desired_state_diff.py +9 -20
- reconcile/utils/runtime/environment.py +33 -8
- reconcile/utils/runtime/integration.py +28 -12
- reconcile/utils/runtime/meta.py +1 -3
- reconcile/utils/runtime/runner.py +8 -11
- reconcile/utils/runtime/sharding.py +93 -36
- reconcile/utils/saasherder/__init__.py +1 -1
- reconcile/utils/saasherder/interfaces.py +143 -138
- reconcile/utils/saasherder/models.py +201 -43
- reconcile/utils/saasherder/saasherder.py +508 -378
- reconcile/utils/secret_reader.py +22 -27
- reconcile/utils/semver_helper.py +15 -1
- reconcile/utils/slack_api.py +124 -36
- reconcile/utils/smtp_client.py +1 -2
- reconcile/utils/sqs_gateway.py +10 -6
- reconcile/utils/state.py +276 -127
- reconcile/utils/terraform/config_client.py +6 -7
- reconcile/utils/terraform_client.py +284 -125
- reconcile/utils/terrascript/cloudflare_client.py +38 -17
- reconcile/utils/terrascript/cloudflare_resources.py +67 -18
- reconcile/utils/terrascript/models.py +2 -3
- reconcile/utils/terrascript/resources.py +1 -2
- reconcile/utils/terrascript_aws_client.py +1292 -540
- reconcile/utils/three_way_diff_strategy.py +157 -0
- reconcile/utils/unleash/__init__.py +11 -0
- reconcile/utils/{unleash.py → unleash/client.py} +35 -29
- reconcile/utils/unleash/server.py +145 -0
- reconcile/utils/vault.py +42 -32
- reconcile/utils/vaultsecretref.py +2 -4
- reconcile/utils/vcs.py +250 -0
- reconcile/vault_replication.py +38 -31
- reconcile/vpc_peerings_validator.py +82 -13
- tools/app_interface_metrics_exporter.py +70 -0
- tools/app_interface_reporter.py +44 -157
- tools/cli_commands/container_images_report.py +154 -0
- tools/cli_commands/cost_report/__init__.py +0 -0
- tools/cli_commands/cost_report/aws.py +137 -0
- tools/cli_commands/cost_report/cost_management_api.py +155 -0
- tools/cli_commands/cost_report/model.py +49 -0
- tools/cli_commands/cost_report/openshift.py +166 -0
- tools/cli_commands/cost_report/openshift_cost_optimization.py +187 -0
- tools/cli_commands/cost_report/response.py +124 -0
- tools/cli_commands/cost_report/util.py +72 -0
- tools/cli_commands/cost_report/view.py +524 -0
- tools/cli_commands/erv2.py +620 -0
- tools/cli_commands/gpg_encrypt.py +5 -8
- tools/cli_commands/systems_and_tools.py +489 -0
- tools/glitchtip_access_revalidation.py +1 -1
- tools/qontract_cli.py +2301 -673
- tools/saas_metrics_exporter/__init__.py +0 -0
- tools/saas_metrics_exporter/commit_distance/__init__.py +0 -0
- tools/saas_metrics_exporter/commit_distance/channel.py +63 -0
- tools/saas_metrics_exporter/commit_distance/commit_distance.py +103 -0
- tools/saas_metrics_exporter/commit_distance/metrics.py +19 -0
- tools/saas_metrics_exporter/main.py +99 -0
- tools/saas_promotion_state/__init__.py +0 -0
- tools/saas_promotion_state/saas_promotion_state.py +105 -0
- tools/sd_app_sre_alert_report.py +145 -0
- tools/template_validation.py +107 -0
- e2e_tests/cli.py +0 -83
- e2e_tests/create_namespace.py +0 -43
- e2e_tests/dedicated_admin_rolebindings.py +0 -44
- e2e_tests/dedicated_admin_test_base.py +0 -39
- e2e_tests/default_network_policies.py +0 -47
- e2e_tests/default_project_labels.py +0 -52
- e2e_tests/network_policy_test_base.py +0 -17
- e2e_tests/test_base.py +0 -56
- qontract_reconcile-0.10.0.dist-info/LICENSE +0 -201
- qontract_reconcile-0.10.0.dist-info/METADATA +0 -63
- qontract_reconcile-0.10.0.dist-info/RECORD +0 -586
- qontract_reconcile-0.10.0.dist-info/top_level.txt +0 -4
- reconcile/ecr_mirror.py +0 -152
- reconcile/github_scanner.py +0 -74
- reconcile/gitlab_integrations.py +0 -63
- reconcile/gql_definitions/ocm_oidc_idp/clusters.py +0 -195
- reconcile/gql_definitions/ocp_release_mirror/ocp_release_mirror.py +0 -287
- reconcile/integrations_validator.py +0 -18
- reconcile/jenkins_plugins.py +0 -129
- reconcile/kafka_clusters.py +0 -208
- reconcile/ocm_cluster_admin.py +0 -42
- reconcile/ocm_oidc_idp.py +0 -198
- reconcile/ocp_release_mirror.py +0 -373
- reconcile/prometheus_rules_tester_old.py +0 -436
- reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request_manager.py +0 -279
- reconcile/saas_auto_promotions_manager/utils/vcs.py +0 -141
- reconcile/sentry_config.py +0 -613
- reconcile/sentry_helper.py +0 -69
- reconcile/test/conftest.py +0 -187
- reconcile/test/fixtures.py +0 -24
- reconcile/test/saas_auto_promotions_manager/conftest.py +0 -69
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/conftest.py +0 -110
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/data_keys.py +0 -10
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_housekeeping.py +0 -200
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_merge_request_manager.py +0 -151
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/conftest.py +0 -63
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/data_keys.py +0 -4
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_multiple_namespaces.py +0 -46
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_single_namespace.py +0 -94
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_single_target.py +0 -44
- reconcile/test/saas_auto_promotions_manager/subscriber/conftest.py +0 -74
- reconcile/test/saas_auto_promotions_manager/subscriber/data_keys.py +0 -11
- reconcile/test/saas_auto_promotions_manager/subscriber/test_content_hash.py +0 -155
- reconcile/test/saas_auto_promotions_manager/subscriber/test_diff.py +0 -173
- reconcile/test/saas_auto_promotions_manager/subscriber/test_multiple_channels_config_hash.py +0 -226
- reconcile/test/saas_auto_promotions_manager/subscriber/test_multiple_channels_moving_ref.py +0 -224
- reconcile/test/saas_auto_promotions_manager/subscriber/test_single_channel_with_single_publisher.py +0 -350
- reconcile/test/saas_auto_promotions_manager/test_integration_test.py +0 -129
- reconcile/test/saas_auto_promotions_manager/utils/saas_files_inventory/test_multiple_publishers_for_single_channel.py +0 -70
- reconcile/test/saas_auto_promotions_manager/utils/saas_files_inventory/test_saas_files_use_target_config_hash.py +0 -63
- reconcile/test/saas_auto_promotions_manager/utils/saas_files_inventory/test_saas_files_with_auto_promote.py +0 -74
- reconcile/test/saas_auto_promotions_manager/utils/saas_files_inventory/test_saas_files_without_auto_promote.py +0 -65
- reconcile/test/test_aggregated_list.py +0 -237
- reconcile/test/test_amtool.py +0 -37
- reconcile/test/test_auto_promoter.py +0 -295
- reconcile/test/test_aws_ami_share.py +0 -68
- reconcile/test/test_aws_iam_keys.py +0 -70
- reconcile/test/test_aws_iam_password_reset.py +0 -35
- reconcile/test/test_aws_support_cases_sos.py +0 -23
- reconcile/test/test_checkpoint.py +0 -178
- reconcile/test/test_cli.py +0 -41
- reconcile/test/test_closedbox_endpoint_monitoring.py +0 -207
- reconcile/test/test_gabi_authorized_users.py +0 -72
- reconcile/test/test_github_org.py +0 -154
- reconcile/test/test_github_repo_invites.py +0 -123
- reconcile/test/test_gitlab_housekeeping.py +0 -88
- reconcile/test/test_gitlab_labeler.py +0 -129
- reconcile/test/test_gitlab_members.py +0 -283
- reconcile/test/test_instrumented_wrappers.py +0 -18
- reconcile/test/test_integrations_manager.py +0 -995
- reconcile/test/test_jenkins_worker_fleets.py +0 -55
- reconcile/test/test_jump_host.py +0 -117
- reconcile/test/test_ldap_users.py +0 -123
- reconcile/test/test_make.py +0 -28
- reconcile/test/test_ocm_additional_routers.py +0 -134
- reconcile/test/test_ocm_addons_upgrade_scheduler_org.py +0 -149
- reconcile/test/test_ocm_clusters.py +0 -598
- reconcile/test/test_ocm_clusters_manifest_updates.py +0 -89
- reconcile/test/test_ocm_oidc_idp.py +0 -315
- reconcile/test/test_ocm_update_recommended_version.py +0 -145
- reconcile/test/test_ocm_upgrade_scheduler.py +0 -614
- reconcile/test/test_ocm_upgrade_scheduler_org_updater.py +0 -129
- reconcile/test/test_openshift_base.py +0 -730
- reconcile/test/test_openshift_namespace_labels.py +0 -345
- reconcile/test/test_openshift_namespaces.py +0 -256
- reconcile/test/test_openshift_resource.py +0 -415
- reconcile/test/test_openshift_resources_base.py +0 -440
- reconcile/test/test_openshift_saas_deploy_change_tester.py +0 -310
- reconcile/test/test_openshift_tekton_resources.py +0 -253
- reconcile/test/test_openshift_upgrade_watcher.py +0 -146
- reconcile/test/test_prometheus_rules_tester.py +0 -151
- reconcile/test/test_prometheus_rules_tester_old.py +0 -77
- reconcile/test/test_quay_membership.py +0 -86
- reconcile/test/test_quay_mirror.py +0 -109
- reconcile/test/test_quay_mirror_org.py +0 -70
- reconcile/test/test_quay_repos.py +0 -59
- reconcile/test/test_queries.py +0 -53
- reconcile/test/test_repo_owners.py +0 -47
- reconcile/test/test_requests_sender.py +0 -139
- reconcile/test/test_saasherder.py +0 -1074
- reconcile/test/test_saasherder_allowed_secret_paths.py +0 -127
- reconcile/test/test_secret_reader.py +0 -153
- reconcile/test/test_slack_base.py +0 -185
- reconcile/test/test_slack_usergroups.py +0 -744
- reconcile/test/test_sql_query.py +0 -19
- reconcile/test/test_terraform_cloudflare_dns.py +0 -117
- reconcile/test/test_terraform_cloudflare_resources.py +0 -106
- reconcile/test/test_terraform_cloudflare_users.py +0 -749
- reconcile/test/test_terraform_resources.py +0 -257
- reconcile/test/test_terraform_tgw_attachments.py +0 -631
- reconcile/test/test_terraform_users.py +0 -57
- reconcile/test/test_terraform_vpc_peerings.py +0 -499
- reconcile/test/test_terraform_vpc_peerings_build_desired_state.py +0 -1061
- reconcile/test/test_unleash.py +0 -138
- reconcile/test/test_utils_aws_api.py +0 -240
- reconcile/test/test_utils_aws_helper.py +0 -80
- reconcile/test/test_utils_cluster_version_data.py +0 -177
- reconcile/test/test_utils_data_structures.py +0 -13
- reconcile/test/test_utils_disabled_integrations.py +0 -86
- reconcile/test/test_utils_expiration.py +0 -109
- reconcile/test/test_utils_external_resource_spec.py +0 -383
- reconcile/test/test_utils_external_resources.py +0 -247
- reconcile/test/test_utils_github_api.py +0 -73
- reconcile/test/test_utils_gitlab_api.py +0 -20
- reconcile/test/test_utils_gpg.py +0 -69
- reconcile/test/test_utils_gql.py +0 -81
- reconcile/test/test_utils_helm.py +0 -306
- reconcile/test/test_utils_helpers.py +0 -55
- reconcile/test/test_utils_imap_client.py +0 -65
- reconcile/test/test_utils_jjb_client.py +0 -52
- reconcile/test/test_utils_jsonpath.py +0 -286
- reconcile/test/test_utils_ldap_client.py +0 -51
- reconcile/test/test_utils_mr.py +0 -226
- reconcile/test/test_utils_mr_clusters_updates.py +0 -77
- reconcile/test/test_utils_oc.py +0 -984
- reconcile/test/test_utils_ocm.py +0 -110
- reconcile/test/test_utils_pagerduty_api.py +0 -251
- reconcile/test/test_utils_parse_dhms_duration.py +0 -34
- reconcile/test/test_utils_password_validator.py +0 -155
- reconcile/test/test_utils_quay_api.py +0 -86
- reconcile/test/test_utils_semver_helper.py +0 -19
- reconcile/test/test_utils_sharding.py +0 -56
- reconcile/test/test_utils_slack_api.py +0 -439
- reconcile/test/test_utils_smtp_client.py +0 -73
- reconcile/test/test_utils_state.py +0 -256
- reconcile/test/test_utils_terraform.py +0 -13
- reconcile/test/test_utils_terraform_client.py +0 -585
- reconcile/test/test_utils_terraform_config_client.py +0 -219
- reconcile/test/test_utils_terrascript_aws_client.py +0 -277
- reconcile/test/test_utils_terrascript_cloudflare_client.py +0 -597
- reconcile/test/test_utils_terrascript_cloudflare_resources.py +0 -26
- reconcile/test/test_vault_replication.py +0 -515
- reconcile/test/test_vault_utils.py +0 -47
- reconcile/test/test_version_bump.py +0 -18
- reconcile/test/test_vpc_peerings_validator.py +0 -103
- reconcile/test/test_wrong_region.py +0 -78
- reconcile/typed_queries/glitchtip_settings.py +0 -18
- reconcile/typed_queries/ocp_release_mirror.py +0 -11
- reconcile/unleash_watcher.py +0 -120
- reconcile/utils/git_secrets.py +0 -63
- reconcile/utils/mr/auto_promoter.py +0 -218
- reconcile/utils/sentry_client.py +0 -383
- release/test_version.py +0 -50
- release/version.py +0 -100
- tools/test/test_qontract_cli.py +0 -60
- tools/test/test_sre_checkpoints.py +0 -79
- /e2e_tests/__init__.py → /reconcile/aus/upgrades.py +0 -0
- /reconcile/{gql_definitions/ocp_release_mirror → aws_account_manager}/__init__.py +0 -0
- /reconcile/{test → aws_ami_cleanup}/__init__.py +0 -0
- /reconcile/{test/saas_auto_promotions_manager → aws_cloudwatch_log_retention}/__init__.py +0 -0
- /reconcile/{test/saas_auto_promotions_manager/merge_request_manager → aws_saml_idp}/__init__.py +0 -0
- /reconcile/{test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager → aws_saml_roles}/__init__.py +0 -0
- /reconcile/{test/saas_auto_promotions_manager/merge_request_manager/renderer → aws_version_sync}/__init__.py +0 -0
- /reconcile/{test/saas_auto_promotions_manager/subscriber → aws_version_sync/merge_request_manager}/__init__.py +0 -0
- /reconcile/{test/saas_auto_promotions_manager/utils → cluster_auth_rhidp}/__init__.py +0 -0
- /reconcile/{test/saas_auto_promotions_manager/utils/saas_files_inventory → dynatrace_token_provider}/__init__.py +0 -0
- {release → reconcile/endpoints_discovery}/__init__.py +0 -0
- {tools/test → reconcile/external_resources}/__init__.py +0 -0
@@ -3,37 +3,38 @@ import hashlib
|
|
3
3
|
import itertools
|
4
4
|
import json
|
5
5
|
import logging
|
6
|
+
import re
|
6
7
|
import sys
|
8
|
+
from collections import defaultdict
|
7
9
|
from collections.abc import (
|
10
|
+
Callable,
|
11
|
+
Generator,
|
8
12
|
Iterable,
|
9
13
|
Mapping,
|
14
|
+
MutableMapping,
|
15
|
+
Sequence,
|
10
16
|
)
|
11
17
|
from contextlib import contextmanager
|
12
18
|
from dataclasses import dataclass
|
13
|
-
from functools import cache
|
14
19
|
from textwrap import indent
|
15
20
|
from threading import Lock
|
16
21
|
from typing import (
|
17
22
|
Any,
|
18
|
-
Optional,
|
19
23
|
Protocol,
|
20
|
-
Tuple,
|
21
24
|
)
|
22
|
-
from
|
25
|
+
from unittest.mock import DEFAULT, patch
|
23
26
|
|
24
27
|
import anymarkup
|
25
|
-
import
|
26
|
-
import jinja2.sandbox
|
28
|
+
from deepdiff import DeepHash
|
27
29
|
from sretoolbox.utils import (
|
28
|
-
retry,
|
29
30
|
threaded,
|
30
31
|
)
|
31
32
|
|
32
33
|
import reconcile.openshift_base as ob
|
34
|
+
import reconcile.utils.jinja2.utils as jinja2_utils
|
33
35
|
from reconcile import queries
|
34
36
|
from reconcile.change_owners.diff import IDENTIFIER_FIELD_NAME
|
35
|
-
from reconcile.
|
36
|
-
from reconcile.github_users import init_github
|
37
|
+
from reconcile.external_resources.meta import SECRET_UPDATED_AT
|
37
38
|
from reconcile.utils import (
|
38
39
|
amtool,
|
39
40
|
gql,
|
@@ -41,9 +42,10 @@ from reconcile.utils import (
|
|
41
42
|
)
|
42
43
|
from reconcile.utils.defer import defer
|
43
44
|
from reconcile.utils.exceptions import FetchResourceError
|
44
|
-
from reconcile.utils.
|
45
|
-
|
46
|
-
|
45
|
+
from reconcile.utils.jinja2.utils import (
|
46
|
+
FetchSecretError,
|
47
|
+
process_extracurlyjinja2_template,
|
48
|
+
process_jinja2_template,
|
47
49
|
)
|
48
50
|
from reconcile.utils.oc import (
|
49
51
|
OC_Map,
|
@@ -51,12 +53,14 @@ from reconcile.utils.oc import (
|
|
51
53
|
OCLogMsg,
|
52
54
|
StatusCodeError,
|
53
55
|
)
|
54
|
-
from reconcile.utils.openshift_resource import ConstructResourceError
|
55
|
-
from reconcile.utils.openshift_resource import OpenshiftResource as OR
|
56
56
|
from reconcile.utils.openshift_resource import (
|
57
|
+
ConstructResourceError,
|
57
58
|
ResourceInventory,
|
58
59
|
ResourceKeyExistsError,
|
60
|
+
ResourceNotManagedError,
|
61
|
+
base64_encode_secret_field_value,
|
59
62
|
)
|
63
|
+
from reconcile.utils.openshift_resource import OpenshiftResource as OR
|
60
64
|
from reconcile.utils.runtime.integration import DesiredStateShardConfig
|
61
65
|
from reconcile.utils.secret_reader import SecretReader
|
62
66
|
from reconcile.utils.semver_helper import make_semver
|
@@ -142,6 +146,8 @@ NAMESPACES_QUERY = """
|
|
142
146
|
{
|
143
147
|
namespaces: namespaces_v1 {
|
144
148
|
name
|
149
|
+
path
|
150
|
+
labels
|
145
151
|
delete
|
146
152
|
clusterAdmin
|
147
153
|
managedResourceTypes
|
@@ -161,8 +167,13 @@ NAMESPACES_QUERY = """
|
|
161
167
|
openshiftResources {
|
162
168
|
%s
|
163
169
|
}
|
170
|
+
environment {
|
171
|
+
name
|
172
|
+
parameters
|
173
|
+
}
|
164
174
|
cluster {
|
165
175
|
name
|
176
|
+
labels
|
166
177
|
serverUrl
|
167
178
|
auth {
|
168
179
|
service
|
@@ -181,8 +192,14 @@ NAMESPACES_QUERY = """
|
|
181
192
|
%s
|
182
193
|
}
|
183
194
|
spec {
|
195
|
+
... on ClusterSpecROSA_v1 {
|
196
|
+
account {
|
197
|
+
uid
|
198
|
+
}
|
199
|
+
}
|
184
200
|
version
|
185
201
|
region
|
202
|
+
hypershift
|
186
203
|
}
|
187
204
|
network {
|
188
205
|
pod
|
@@ -215,203 +232,58 @@ NAMESPACES_QUERY = """
|
|
215
232
|
QONTRACT_INTEGRATION = "openshift_resources_base"
|
216
233
|
QONTRACT_INTEGRATION_VERSION = make_semver(1, 9, 2)
|
217
234
|
QONTRACT_BASE64_SUFFIX = "_qb64"
|
218
|
-
|
235
|
+
KUBERNETES_SECRET_DATA_KEY_RE = "^[-._a-zA-Z0-9]+$"
|
219
236
|
|
237
|
+
# Keys in vault secrets that do not need to land
|
238
|
+
# into K8S secrets.
|
239
|
+
VAULT_SECRETS_EXCLUDED_KEYS = {SECRET_UPDATED_AT}
|
220
240
|
_log_lock = Lock()
|
221
241
|
|
222
242
|
|
223
|
-
def _locked_info_log(msg: str):
|
243
|
+
def _locked_info_log(msg: str) -> None:
|
224
244
|
with _log_lock:
|
225
245
|
logging.info(msg)
|
226
246
|
|
227
247
|
|
228
|
-
def _locked_debug_log(msg: str):
|
248
|
+
def _locked_debug_log(msg: str) -> None:
|
229
249
|
with _log_lock:
|
230
250
|
logging.debug(msg)
|
231
251
|
|
232
252
|
|
233
|
-
def _locked_error_log(msg: str):
|
253
|
+
def _locked_error_log(msg: str) -> None:
|
234
254
|
with _log_lock:
|
235
255
|
logging.error(msg)
|
236
256
|
|
237
257
|
|
238
|
-
class FetchSecretError(Exception):
|
239
|
-
def __init__(self, msg):
|
240
|
-
super().__init__("error fetching secret: " + str(msg))
|
241
|
-
|
242
|
-
|
243
258
|
class FetchRouteError(Exception):
|
244
|
-
def __init__(self, msg):
|
259
|
+
def __init__(self, msg: Any):
|
245
260
|
super().__init__("error fetching route: " + str(msg))
|
246
261
|
|
247
262
|
|
248
|
-
class
|
249
|
-
|
250
|
-
super().__init__("error processing jinja2 template: " + str(msg))
|
263
|
+
class ResourceTemplateRenderError(Exception):
|
264
|
+
pass
|
251
265
|
|
252
266
|
|
253
|
-
class
|
267
|
+
class SecretKeyFormatError(Exception):
|
254
268
|
pass
|
255
269
|
|
256
270
|
|
257
271
|
class UnknownProviderError(Exception):
|
258
|
-
def __init__(self, msg):
|
272
|
+
def __init__(self, msg: Any):
|
259
273
|
super().__init__("unknown provider error: " + str(msg))
|
260
274
|
|
261
275
|
|
262
276
|
class UnknownTemplateTypeError(Exception):
|
263
|
-
def __init__(self, msg):
|
277
|
+
def __init__(self, msg: Any):
|
264
278
|
super().__init__("unknown template type error: " + str(msg))
|
265
279
|
|
266
280
|
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
version = process_jinja2_template(
|
274
|
-
body=version, vars=tvars, settings=settings
|
275
|
-
)
|
276
|
-
secret = {"path": path, "field": key, "version": version}
|
277
|
-
try:
|
278
|
-
secret_reader = SecretReader(settings)
|
279
|
-
return secret_reader.read(secret)
|
280
|
-
except Exception as e:
|
281
|
-
raise FetchSecretError(e)
|
282
|
-
|
283
|
-
|
284
|
-
def lookup_github_file_content(repo, path, ref, tvars=None, settings=None):
|
285
|
-
if tvars is not None:
|
286
|
-
repo = process_jinja2_template(body=repo, vars=tvars, settings=settings)
|
287
|
-
path = process_jinja2_template(body=path, vars=tvars, settings=settings)
|
288
|
-
ref = process_jinja2_template(body=ref, vars=tvars, settings=settings)
|
289
|
-
|
290
|
-
gh = init_github()
|
291
|
-
c = gh.get_repo(repo).get_contents(path, ref).decoded_content
|
292
|
-
return c.decode("utf-8")
|
293
|
-
|
294
|
-
|
295
|
-
def lookup_graphql_query_results(query: str, **kwargs) -> list[Any]:
|
296
|
-
gqlapi = gql.get_api()
|
297
|
-
resource = gqlapi.get_resource(query)["content"]
|
298
|
-
rendered_resource = jinja2.Template(resource).render(**kwargs)
|
299
|
-
results = list(gqlapi.query(rendered_resource).values())[0]
|
300
|
-
return results
|
301
|
-
|
302
|
-
|
303
|
-
def json_to_dict(input):
|
304
|
-
"""Jinja2 filter to parse JSON strings into dictionaries.
|
305
|
-
This becomes useful to access Graphql queries data (labels)
|
306
|
-
:param input: json string
|
307
|
-
:return: dict with the parsed inputs contents
|
308
|
-
"""
|
309
|
-
data = json.loads(input)
|
310
|
-
return data
|
311
|
-
|
312
|
-
|
313
|
-
def urlescape(
|
314
|
-
string: str,
|
315
|
-
safe: str = "/",
|
316
|
-
encoding: Optional[str] = None,
|
317
|
-
) -> str:
|
318
|
-
"""Jinja2 filter that is a simple wrapper around urllib's URL quoting
|
319
|
-
functions that takes a string value and makes it safe for use as URL
|
320
|
-
components escaping any reserved characters using URL encoding. See:
|
321
|
-
urllib.parse.quote() and urllib.parse.quote_plus() for reference.
|
322
|
-
|
323
|
-
:param str string: String value to escape.
|
324
|
-
:param str safe: Optional characters that should not be escaped.
|
325
|
-
:param encoding: Encoding to apply to the string to be escaped. Defaults
|
326
|
-
to UTF-8. Unsupported characters raise a UnicodeEncodeError error.
|
327
|
-
:type encoding: typing.Optional[str]
|
328
|
-
:returns: A string with reserved characters escaped.
|
329
|
-
:rtype: str
|
330
|
-
"""
|
331
|
-
return parse.quote(string, safe=safe, encoding=encoding)
|
332
|
-
|
333
|
-
|
334
|
-
def urlunescape(string: str, encoding: Optional[str] = None) -> str:
|
335
|
-
"""Jinja2 filter that is a simple wrapper around urllib's URL unquoting
|
336
|
-
functions that takes an URL-encoded string value and unescapes it
|
337
|
-
replacing any URL-encoded values with their character equivalent. See:
|
338
|
-
urllib.parse.unquote() and urllib.parse.unquote_plus() for reference.
|
339
|
-
|
340
|
-
:param str string: String value to unescape.
|
341
|
-
:param encoding: Encoding to apply to the string to be unescaped. Defaults
|
342
|
-
to UTF-8. Unsupported characters are replaced by placeholder values.
|
343
|
-
:type encoding: typing.Optional[str]
|
344
|
-
:returns: A string with URL-encoded sequences unescaped.
|
345
|
-
:rtype: str
|
346
|
-
"""
|
347
|
-
if encoding is None:
|
348
|
-
encoding = "utf-8"
|
349
|
-
return parse.unquote(string, encoding=encoding)
|
350
|
-
|
351
|
-
|
352
|
-
@cache
|
353
|
-
def compile_jinja2_template(body, extra_curly: bool = False):
|
354
|
-
env: dict = {}
|
355
|
-
if extra_curly:
|
356
|
-
env = {
|
357
|
-
"block_start_string": "{{%",
|
358
|
-
"block_end_string": "%}}",
|
359
|
-
"variable_start_string": "{{{",
|
360
|
-
"variable_end_string": "}}}",
|
361
|
-
"comment_start_string": "{{#",
|
362
|
-
"comment_end_string": "#}}",
|
363
|
-
}
|
364
|
-
|
365
|
-
jinja_env = jinja2.sandbox.SandboxedEnvironment(
|
366
|
-
extensions=[B64EncodeExtension, RaiseErrorExtension],
|
367
|
-
undefined=jinja2.StrictUndefined,
|
368
|
-
**env,
|
369
|
-
)
|
370
|
-
jinja_env.filters.update(
|
371
|
-
{
|
372
|
-
"json_to_dict": json_to_dict,
|
373
|
-
"urlescape": urlescape,
|
374
|
-
"urlunescape": urlunescape,
|
375
|
-
}
|
376
|
-
)
|
377
|
-
|
378
|
-
return jinja_env.from_string(body)
|
379
|
-
|
380
|
-
|
381
|
-
def process_jinja2_template(body, vars=None, extra_curly: bool = False, settings=None):
|
382
|
-
if vars is None:
|
383
|
-
vars = {}
|
384
|
-
vars.update(
|
385
|
-
{
|
386
|
-
"vault": lambda p, k, v=None: lookup_secret(
|
387
|
-
path=p, key=k, version=v, tvars=vars, settings=settings
|
388
|
-
),
|
389
|
-
"github": lambda u, p, r, v=None: lookup_github_file_content(
|
390
|
-
repo=u, path=p, ref=r, tvars=vars, settings=settings
|
391
|
-
),
|
392
|
-
"urlescape": lambda u, s="/", e=None: urlescape(
|
393
|
-
string=u, safe=s, encoding=e
|
394
|
-
),
|
395
|
-
"urlunescape": lambda u, e=None: urlunescape(string=u, encoding=e),
|
396
|
-
"query": lookup_graphql_query_results,
|
397
|
-
"url": url_makes_sense,
|
398
|
-
}
|
399
|
-
)
|
400
|
-
try:
|
401
|
-
template = compile_jinja2_template(body, extra_curly)
|
402
|
-
r = template.render(vars)
|
403
|
-
except Exception as e:
|
404
|
-
raise Jinja2TemplateError(e)
|
405
|
-
return r
|
406
|
-
|
407
|
-
|
408
|
-
def process_extracurlyjinja2_template(body, vars=None, env=None, settings=None):
|
409
|
-
if vars is None:
|
410
|
-
vars = {}
|
411
|
-
return process_jinja2_template(body, vars=vars, extra_curly=True, settings=settings)
|
412
|
-
|
413
|
-
|
414
|
-
def check_alertmanager_config(data, path, alertmanager_config_key, decode_base64=False):
|
281
|
+
def check_alertmanager_config(
|
282
|
+
data: Mapping[str, Any],
|
283
|
+
path: str,
|
284
|
+
alertmanager_config_key: str,
|
285
|
+
decode_base64: bool = False,
|
286
|
+
) -> None:
|
415
287
|
try:
|
416
288
|
config = data[alertmanager_config_key]
|
417
289
|
except KeyError:
|
@@ -419,7 +291,7 @@ def check_alertmanager_config(data, path, alertmanager_config_key, decode_base64
|
|
419
291
|
f"error validating alertmanager config in {path}: "
|
420
292
|
f"missing key {alertmanager_config_key}"
|
421
293
|
)
|
422
|
-
raise FetchResourceError(e_msg)
|
294
|
+
raise FetchResourceError(e_msg) from None
|
423
295
|
|
424
296
|
if decode_base64:
|
425
297
|
config = base64.b64decode(config).decode("utf-8")
|
@@ -431,15 +303,15 @@ def check_alertmanager_config(data, path, alertmanager_config_key, decode_base64
|
|
431
303
|
|
432
304
|
|
433
305
|
def fetch_provider_resource(
|
434
|
-
resource:
|
435
|
-
tfunc=None,
|
436
|
-
tvars=None,
|
437
|
-
validate_json=False,
|
438
|
-
validate_alertmanager_config=False,
|
439
|
-
alertmanager_config_key="alertmanager.yaml",
|
440
|
-
add_path_to_prom_rules=True,
|
441
|
-
skip_validation=False,
|
442
|
-
settings=None,
|
306
|
+
resource: Mapping,
|
307
|
+
tfunc: Callable | None = None,
|
308
|
+
tvars: Mapping[str, Any] | None = None,
|
309
|
+
validate_json: bool = False,
|
310
|
+
validate_alertmanager_config: bool = False,
|
311
|
+
alertmanager_config_key: str = "alertmanager.yaml",
|
312
|
+
add_path_to_prom_rules: bool = True,
|
313
|
+
skip_validation: bool = False,
|
314
|
+
settings: Mapping[str, Any] | None = None,
|
443
315
|
) -> OR:
|
444
316
|
path = resource["path"]
|
445
317
|
content = resource["content"]
|
@@ -461,10 +333,10 @@ def fetch_provider_resource(
|
|
461
333
|
except TypeError:
|
462
334
|
body_type = type(body).__name__
|
463
335
|
e_msg = f"invalid resource type {body_type} found in path: {path}"
|
464
|
-
raise FetchResourceError(e_msg)
|
336
|
+
raise FetchResourceError(e_msg) from None
|
465
337
|
except anymarkup.AnyMarkupError:
|
466
338
|
e_msg = f"Could not parse data. Skipping resource: {path}"
|
467
|
-
raise FetchResourceError(e_msg)
|
339
|
+
raise FetchResourceError(e_msg) from None
|
468
340
|
|
469
341
|
if validate_json:
|
470
342
|
files = body["data"]
|
@@ -473,7 +345,7 @@ def fetch_provider_resource(
|
|
473
345
|
json.loads(file_content)
|
474
346
|
except ValueError:
|
475
347
|
e_msg = f"invalid json in {path} under {file_name}"
|
476
|
-
raise FetchResourceError(e_msg)
|
348
|
+
raise FetchResourceError(e_msg) from None
|
477
349
|
|
478
350
|
if validate_alertmanager_config:
|
479
351
|
if body["kind"] == "Secret":
|
@@ -491,6 +363,9 @@ def fetch_provider_resource(
|
|
491
363
|
|
492
364
|
if add_path_to_prom_rules:
|
493
365
|
if body["kind"] == "PrometheusRule":
|
366
|
+
app_int_base_url = "https://gitlab.cee.redhat.com/service/app-interface"
|
367
|
+
if settings and "repoUrl" in settings:
|
368
|
+
app_int_base_url = settings["repoUrl"]
|
494
369
|
try:
|
495
370
|
groups = body["spec"]["groups"]
|
496
371
|
for group in groups:
|
@@ -499,10 +374,9 @@ def fetch_provider_resource(
|
|
499
374
|
annotations = rule.get("annotations")
|
500
375
|
if not annotations:
|
501
376
|
continue
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
] = f"{APP_INT_BASE_URL}/blob/master/resources{path}"
|
377
|
+
rule["annotations"]["html_url"] = (
|
378
|
+
f"{app_int_base_url}/blob/master/resources{path}"
|
379
|
+
)
|
506
380
|
except Exception:
|
507
381
|
logging.warning(
|
508
382
|
"could not add html_url annotation to" + body["metadata"]["name"]
|
@@ -513,31 +387,35 @@ def fetch_provider_resource(
|
|
513
387
|
body, QONTRACT_INTEGRATION, QONTRACT_INTEGRATION_VERSION, error_details=path
|
514
388
|
)
|
515
389
|
except ConstructResourceError as e:
|
516
|
-
raise FetchResourceError(str(e))
|
390
|
+
raise FetchResourceError(str(e)) from None
|
517
391
|
|
518
392
|
|
519
393
|
def fetch_provider_vault_secret(
|
520
|
-
path,
|
521
|
-
version,
|
522
|
-
name,
|
523
|
-
labels,
|
524
|
-
annotations,
|
525
|
-
type,
|
526
|
-
integration,
|
527
|
-
integration_version,
|
528
|
-
validate_alertmanager_config=False,
|
529
|
-
alertmanager_config_key="alertmanager.yaml",
|
530
|
-
settings=None,
|
394
|
+
path: str,
|
395
|
+
version: str,
|
396
|
+
name: str,
|
397
|
+
labels: Mapping[str, str] | None,
|
398
|
+
annotations: Mapping[str, str],
|
399
|
+
type: str,
|
400
|
+
integration: str,
|
401
|
+
integration_version: str,
|
402
|
+
validate_alertmanager_config: bool = False,
|
403
|
+
alertmanager_config_key: str = "alertmanager.yaml",
|
404
|
+
settings: Mapping[str, Any] | None = None,
|
531
405
|
) -> OR:
|
532
406
|
# get the fields from vault
|
533
407
|
secret_reader = SecretReader(settings)
|
534
|
-
raw_data =
|
408
|
+
raw_data = {
|
409
|
+
k: v
|
410
|
+
for k, v in secret_reader.read_all({"path": path, "version": version}).items()
|
411
|
+
if k not in VAULT_SECRETS_EXCLUDED_KEYS
|
412
|
+
}
|
535
413
|
|
536
414
|
if validate_alertmanager_config:
|
537
415
|
check_alertmanager_config(raw_data, path, alertmanager_config_key)
|
538
416
|
|
539
417
|
# construct oc resource
|
540
|
-
body = {
|
418
|
+
body: dict[str, Any] = {
|
541
419
|
"apiVersion": "v1",
|
542
420
|
"kind": "Secret",
|
543
421
|
"type": type,
|
@@ -546,24 +424,42 @@ def fetch_provider_vault_secret(
|
|
546
424
|
if labels:
|
547
425
|
body["metadata"]["labels"] = labels
|
548
426
|
|
427
|
+
assert_valid_secret_keys(raw_data)
|
428
|
+
|
549
429
|
# populate data
|
550
430
|
for k, v in raw_data.items():
|
551
|
-
if v == "":
|
552
|
-
continue
|
553
431
|
if k.lower().endswith(QONTRACT_BASE64_SUFFIX):
|
554
432
|
k = k[: -len(QONTRACT_BASE64_SUFFIX)]
|
555
433
|
v = v.replace("\n", "")
|
556
|
-
|
557
|
-
v =
|
434
|
+
else:
|
435
|
+
v = base64_encode_secret_field_value(v)
|
558
436
|
body.setdefault("data", {})[k] = v
|
559
437
|
|
560
438
|
try:
|
561
439
|
return OR(body, integration, integration_version, error_details=path)
|
562
440
|
except ConstructResourceError as e:
|
563
|
-
raise FetchResourceError(str(e))
|
441
|
+
raise FetchResourceError(str(e)) from None
|
442
|
+
|
443
|
+
|
444
|
+
# check to ensure that all of the keys are valid by looking to see if there are
|
445
|
+
# any white space issues. If any issues are uncovered, an exception will be
|
446
|
+
# raised.
|
447
|
+
# we're receiving the full key: value information, not simply a list of keys.
|
448
|
+
def assert_valid_secret_keys(secrets_data: dict[str, str]) -> None:
|
449
|
+
for k in secrets_data:
|
450
|
+
matches = re.search(KUBERNETES_SECRET_DATA_KEY_RE, k)
|
451
|
+
if not matches:
|
452
|
+
raise SecretKeyFormatError(
|
453
|
+
f"'{k}' is not valid key name for a Secret. a valid Secret key must consist of alphanumeric characters, '-', '_' or '.' (e.g. 'key.name', or 'KEY_NAME', or 'key-name', regex used for validation is '^[-._a-zA-Z0-9]+$')"
|
454
|
+
)
|
564
455
|
|
565
456
|
|
566
|
-
def fetch_provider_route(
|
457
|
+
def fetch_provider_route(
|
458
|
+
resource: Mapping,
|
459
|
+
tls_path: str | None,
|
460
|
+
tls_version: str | None,
|
461
|
+
settings: Mapping | None = None,
|
462
|
+
) -> OR:
|
567
463
|
path = resource["path"]
|
568
464
|
openshift_resource = fetch_provider_resource(resource)
|
569
465
|
|
@@ -589,9 +485,7 @@ def fetch_provider_route(resource: dict, tls_path, tls_version, settings=None) -
|
|
589
485
|
tls[k] = v
|
590
486
|
continue
|
591
487
|
|
592
|
-
msg = "Route secret '{}' key '{}' not in valid keys {}"
|
593
|
-
tls_path, k, valid_keys
|
594
|
-
)
|
488
|
+
msg = f"Route secret '{tls_path}' key '{k}' not in valid keys {valid_keys}"
|
595
489
|
_locked_info_log(msg)
|
596
490
|
|
597
491
|
host = openshift_resource.body["spec"].get("host")
|
@@ -606,12 +500,15 @@ def fetch_provider_route(resource: dict, tls_path, tls_version, settings=None) -
|
|
606
500
|
|
607
501
|
|
608
502
|
def fetch_openshift_resource(
|
609
|
-
resource
|
503
|
+
resource: Mapping,
|
504
|
+
parent: Mapping[str, Any],
|
505
|
+
settings: Mapping | None = None,
|
506
|
+
skip_validation: bool = False,
|
610
507
|
) -> OR:
|
611
508
|
provider = resource["provider"]
|
612
509
|
if provider == "resource":
|
613
510
|
path = resource["resource"]["path"]
|
614
|
-
_locked_debug_log("Processing {}: {}"
|
511
|
+
_locked_debug_log(f"Processing {provider}: {path}")
|
615
512
|
validate_json = resource.get("validate_json") or False
|
616
513
|
add_path_to_prom_rules = resource.get("add_path_to_prom_rules", True)
|
617
514
|
validate_alertmanager_config = (
|
@@ -631,7 +528,7 @@ def fetch_openshift_resource(
|
|
631
528
|
)
|
632
529
|
elif provider == "resource-template":
|
633
530
|
path = resource["resource"]["path"]
|
634
|
-
_locked_debug_log("Processing {}: {}"
|
531
|
+
_locked_debug_log(f"Processing {provider}: {path}")
|
635
532
|
add_path_to_prom_rules = resource.get("add_path_to_prom_rules", True)
|
636
533
|
validate_alertmanager_config = (
|
637
534
|
resource.get("validate_alertmanager_config") or False
|
@@ -664,12 +561,12 @@ def fetch_openshift_resource(
|
|
664
561
|
settings=settings,
|
665
562
|
)
|
666
563
|
except Exception as e:
|
667
|
-
msg = "could not render template at path {}\n{}"
|
668
|
-
raise ResourceTemplateRenderError(msg)
|
564
|
+
msg = f"could not render template at path {path}\n{e}"
|
565
|
+
raise ResourceTemplateRenderError(msg) from None
|
669
566
|
elif provider == "vault-secret":
|
670
567
|
path = resource["path"]
|
671
568
|
version = resource["version"]
|
672
|
-
_locked_debug_log("Processing {}: {} - {}"
|
569
|
+
_locked_debug_log(f"Processing {provider}: {path} - {version}")
|
673
570
|
rn = resource["name"]
|
674
571
|
name = path.split("/")[-1] if rn is None else rn
|
675
572
|
rl = resource["labels"]
|
@@ -699,10 +596,10 @@ def fetch_openshift_resource(
|
|
699
596
|
settings=settings,
|
700
597
|
)
|
701
598
|
except (SecretVersionNotFound, SecretVersionIsNone) as e:
|
702
|
-
raise FetchSecretError(e)
|
599
|
+
raise FetchSecretError(e) from None
|
703
600
|
elif provider == "route":
|
704
601
|
path = resource["resource"]["path"]
|
705
|
-
_locked_debug_log("Processing {}: {}"
|
602
|
+
_locked_debug_log(f"Processing {provider}: {path}")
|
706
603
|
tls_path = resource["vault_tls_secret_path"]
|
707
604
|
tls_version = resource["vault_tls_secret_version"]
|
708
605
|
openshift_resource = fetch_provider_route(
|
@@ -710,7 +607,7 @@ def fetch_openshift_resource(
|
|
710
607
|
)
|
711
608
|
elif provider == "prometheus-rule":
|
712
609
|
path = resource["resource"]["path"]
|
713
|
-
_locked_debug_log("Processing {}: {}"
|
610
|
+
_locked_debug_log(f"Processing {provider}: {path}")
|
714
611
|
add_path_to_prom_rules = resource.get("add_path_to_prom_rules", True)
|
715
612
|
tv = {}
|
716
613
|
if resource["variables"]:
|
@@ -738,8 +635,8 @@ def fetch_openshift_resource(
|
|
738
635
|
settings=settings,
|
739
636
|
)
|
740
637
|
except Exception as e:
|
741
|
-
msg = "could not render template at path {}\n{}"
|
742
|
-
raise ResourceTemplateRenderError(msg)
|
638
|
+
msg = f"could not render template at path {path}\n{e}"
|
639
|
+
raise ResourceTemplateRenderError(msg) from None
|
743
640
|
|
744
641
|
else:
|
745
642
|
raise UnknownProviderError(provider)
|
@@ -753,8 +650,8 @@ def fetch_current_state(
|
|
753
650
|
cluster: str,
|
754
651
|
namespace: str,
|
755
652
|
kind: str,
|
756
|
-
resource_names
|
757
|
-
):
|
653
|
+
resource_names: Iterable[str] | None,
|
654
|
+
) -> None:
|
758
655
|
_locked_debug_log(f"Fetching {kind} from {cluster}/{namespace}")
|
759
656
|
if not oc.is_kind_supported(kind):
|
760
657
|
logging.warning(f"[{cluster}] cluster has no API resource {kind}.")
|
@@ -780,8 +677,8 @@ def fetch_desired_state(
|
|
780
677
|
resource: Mapping[str, Any],
|
781
678
|
parent: Mapping[str, Any],
|
782
679
|
privileged: bool,
|
783
|
-
settings:
|
784
|
-
):
|
680
|
+
settings: Mapping[str, Any] | None = None,
|
681
|
+
) -> None:
|
785
682
|
try:
|
786
683
|
openshift_resource = fetch_openshift_resource(resource, parent, settings)
|
787
684
|
except (
|
@@ -791,7 +688,7 @@ def fetch_desired_state(
|
|
791
688
|
UnknownProviderError,
|
792
689
|
) as e:
|
793
690
|
ri.register_error()
|
794
|
-
msg = "[{}/{}] {}"
|
691
|
+
msg = f"[{cluster}/{namespace}] {e!s}"
|
795
692
|
_locked_error_log(msg)
|
796
693
|
return
|
797
694
|
|
@@ -809,9 +706,7 @@ def fetch_desired_state(
|
|
809
706
|
# combination was not initialized, meaning that it shouldn't be
|
810
707
|
# managed. But someone is trying to add it via app-interface
|
811
708
|
ri.register_error()
|
812
|
-
msg = "[{}/{}] unknown kind: {}. hint: is it missing from managedResourceTypes?"
|
813
|
-
cluster, namespace, openshift_resource.kind
|
814
|
-
)
|
709
|
+
msg = f"[{cluster}/{namespace}] unknown kind: {openshift_resource.kind}. hint: is it missing from managedResourceTypes?"
|
815
710
|
_locked_error_log(msg)
|
816
711
|
return
|
817
712
|
except ResourceKeyExistsError:
|
@@ -819,9 +714,14 @@ def fetch_desired_state(
|
|
819
714
|
# a desired resource with the same name and
|
820
715
|
# the same type was already added previously
|
821
716
|
ri.register_error()
|
822
|
-
msg =
|
823
|
-
|
824
|
-
|
717
|
+
msg = f"[{cluster}/{namespace}] desired item already exists: {openshift_resource.kind}/{openshift_resource.name}."
|
718
|
+
_locked_error_log(msg)
|
719
|
+
return
|
720
|
+
except ResourceNotManagedError:
|
721
|
+
# This is failing because the resource name is
|
722
|
+
# not in the list of resource names that are managed
|
723
|
+
ri.register_error()
|
724
|
+
msg = f"[{cluster}/{namespace}] desired item is not managed: {openshift_resource.kind}/{openshift_resource.name}."
|
825
725
|
_locked_error_log(msg)
|
826
726
|
return
|
827
727
|
|
@@ -829,7 +729,7 @@ def fetch_desired_state(
|
|
829
729
|
def fetch_states(
|
830
730
|
spec: ob.StateSpec,
|
831
731
|
ri: ResourceInventory,
|
832
|
-
settings:
|
732
|
+
settings: Mapping[str, Any] | None = None,
|
833
733
|
) -> None:
|
834
734
|
try:
|
835
735
|
if isinstance(spec, ob.CurrentStateSpec):
|
@@ -855,17 +755,17 @@ def fetch_states(
|
|
855
755
|
|
856
756
|
except StatusCodeError as e:
|
857
757
|
ri.register_error(cluster=spec.cluster)
|
858
|
-
logging.error(f"{spec} - exception: {
|
758
|
+
logging.error(f"{spec} - exception: {e!s}")
|
859
759
|
|
860
760
|
|
861
761
|
def fetch_data(
|
862
|
-
namespaces,
|
863
|
-
thread_pool_size,
|
864
|
-
internal,
|
865
|
-
use_jump_host,
|
866
|
-
init_api_resources=False,
|
867
|
-
overrides=None,
|
868
|
-
):
|
762
|
+
namespaces: Iterable[Mapping[str, Any]],
|
763
|
+
thread_pool_size: int,
|
764
|
+
internal: bool | None,
|
765
|
+
use_jump_host: bool,
|
766
|
+
init_api_resources: bool = False,
|
767
|
+
overrides: Iterable[str] | None = None,
|
768
|
+
) -> tuple[OC_Map, ResourceInventory]:
|
869
769
|
ri = ResourceInventory()
|
870
770
|
settings = queries.get_app_interface_settings()
|
871
771
|
logging.debug(f"Overriding keys {overrides}")
|
@@ -887,20 +787,29 @@ def fetch_data(
|
|
887
787
|
|
888
788
|
|
889
789
|
def filter_namespaces_by_cluster_and_namespace(
|
890
|
-
namespaces,
|
891
|
-
|
892
|
-
|
893
|
-
|
790
|
+
namespaces: Sequence[dict[str, Any]],
|
791
|
+
cluster_names: Iterable[str] | None,
|
792
|
+
exclude_clusters: Iterable[str] | None,
|
793
|
+
namespace_name: str | None,
|
794
|
+
) -> Sequence[dict[str, Any]]:
|
795
|
+
if cluster_names:
|
796
|
+
namespaces = [n for n in namespaces if n["cluster"]["name"] in cluster_names]
|
797
|
+
elif exclude_clusters:
|
798
|
+
namespaces = [
|
799
|
+
n for n in namespaces if n["cluster"]["name"] not in exclude_clusters
|
800
|
+
]
|
801
|
+
|
894
802
|
if namespace_name:
|
895
803
|
namespaces = [n for n in namespaces if n["name"] == namespace_name]
|
804
|
+
|
896
805
|
return namespaces
|
897
806
|
|
898
807
|
|
899
808
|
def canonicalize_namespaces(
|
900
809
|
namespaces: Iterable[dict[str, Any]],
|
901
|
-
providers:
|
902
|
-
resource_schema_filter:
|
903
|
-
) -> tuple[list[dict[str, Any]],
|
810
|
+
providers: Sequence[str],
|
811
|
+
resource_schema_filter: str | None = None,
|
812
|
+
) -> tuple[list[dict[str, Any]], list[str] | None]:
|
904
813
|
canonicalized_namespaces = []
|
905
814
|
override = None
|
906
815
|
logging.debug(f"Received providers {providers}")
|
@@ -924,6 +833,8 @@ def canonicalize_namespaces(
|
|
924
833
|
override = ["Secret"]
|
925
834
|
elif providers[0] == "route":
|
926
835
|
override = ["Route"]
|
836
|
+
elif providers[0] == "prometheus-rule":
|
837
|
+
override = ["PrometheusRule"]
|
927
838
|
|
928
839
|
namespace_info["openshiftResources"] = ors
|
929
840
|
canonicalized_namespaces.append(namespace_info)
|
@@ -932,16 +843,17 @@ def canonicalize_namespaces(
|
|
932
843
|
|
933
844
|
|
934
845
|
def get_namespaces(
|
935
|
-
providers:
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
846
|
+
providers: Sequence[str] | None = None,
|
847
|
+
cluster_names: Iterable[str] | None = None,
|
848
|
+
exclude_clusters: Iterable[str] | None = None,
|
849
|
+
namespace_name: str | None = None,
|
850
|
+
resource_schema_filter: str | None = None,
|
851
|
+
filter_by_shard: bool | None = True,
|
852
|
+
) -> tuple[list[dict[str, Any]], list[str] | None]:
|
941
853
|
if providers is None:
|
942
854
|
providers = []
|
943
855
|
gqlapi = gql.get_api()
|
944
|
-
namespaces = [
|
856
|
+
namespaces: list[dict[str, Any]] = [
|
945
857
|
namespace_info
|
946
858
|
for namespace_info in gqlapi.query(NAMESPACES_QUERY)["namespaces"]
|
947
859
|
if not ob.is_namespace_deleted(namespace_info)
|
@@ -952,31 +864,51 @@ def get_namespaces(
|
|
952
864
|
)
|
953
865
|
)
|
954
866
|
]
|
955
|
-
|
956
|
-
namespaces,
|
867
|
+
_namespaces = filter_namespaces_by_cluster_and_namespace(
|
868
|
+
namespaces, cluster_names, exclude_clusters, namespace_name
|
957
869
|
)
|
958
|
-
return canonicalize_namespaces(
|
870
|
+
return canonicalize_namespaces(_namespaces, providers, resource_schema_filter)
|
959
871
|
|
960
872
|
|
961
873
|
@defer
|
962
874
|
def run(
|
963
|
-
dry_run,
|
964
|
-
thread_pool_size=10,
|
965
|
-
internal=None,
|
966
|
-
use_jump_host=True,
|
967
|
-
providers=None,
|
968
|
-
cluster_name=None,
|
969
|
-
|
970
|
-
|
971
|
-
|
972
|
-
|
875
|
+
dry_run: bool,
|
876
|
+
thread_pool_size: int = 10,
|
877
|
+
internal: bool | None = None,
|
878
|
+
use_jump_host: bool = True,
|
879
|
+
providers: Sequence[str] | None = None,
|
880
|
+
cluster_name: Sequence[str] | None = None,
|
881
|
+
exclude_cluster: Sequence[str] | None = None,
|
882
|
+
namespace_name: str | None = None,
|
883
|
+
init_api_resources: bool = False,
|
884
|
+
defer: Callable | None = None,
|
885
|
+
) -> ResourceInventory | None:
|
886
|
+
# https://click.palletsprojects.com/en/8.1.x/options/#multiple-options
|
887
|
+
cluster_names = cluster_name
|
888
|
+
exclude_clusters = exclude_cluster
|
889
|
+
|
890
|
+
if exclude_clusters and not dry_run:
|
891
|
+
raise RuntimeError("--exclude-cluster is only supported in dry-run mode")
|
892
|
+
|
893
|
+
if exclude_clusters and cluster_names:
|
894
|
+
raise RuntimeError(
|
895
|
+
"--cluster-name and --exclude-cluster can not be used together"
|
896
|
+
)
|
897
|
+
if cluster_names and len(cluster_names) > 1 and not dry_run:
|
898
|
+
raise RuntimeError(
|
899
|
+
"Running with multiple clusters is only supported in dry-run mode"
|
900
|
+
)
|
901
|
+
|
973
902
|
namespaces, overrides = get_namespaces(
|
974
|
-
providers=providers,
|
903
|
+
providers=providers,
|
904
|
+
cluster_names=cluster_names,
|
905
|
+
exclude_clusters=exclude_clusters,
|
906
|
+
namespace_name=namespace_name,
|
975
907
|
)
|
976
908
|
if not namespaces:
|
977
|
-
logging.
|
909
|
+
logging.debug(
|
978
910
|
"No namespaces found when filtering for "
|
979
|
-
f"cluster={
|
911
|
+
f"cluster={cluster_names}, namespace={namespace_name}. "
|
980
912
|
"Exiting."
|
981
913
|
)
|
982
914
|
return None
|
@@ -988,12 +920,14 @@ def run(
|
|
988
920
|
init_api_resources=init_api_resources,
|
989
921
|
overrides=overrides,
|
990
922
|
)
|
991
|
-
defer
|
923
|
+
if defer:
|
924
|
+
defer(oc_map.cleanup)
|
992
925
|
if dry_run and QONTRACT_INTEGRATION == "openshift-resources":
|
993
926
|
error = check_cluster_scoped_resources(oc_map, ri, namespaces, None)
|
994
927
|
if error:
|
995
928
|
sys.exit(1)
|
996
929
|
|
930
|
+
ob.publish_metrics(ri, QONTRACT_INTEGRATION)
|
997
931
|
ob.realize_data(dry_run, oc_map, ri, thread_pool_size)
|
998
932
|
|
999
933
|
if ri.has_error_registered():
|
@@ -1015,7 +949,7 @@ class CheckNamespaceResources(Protocol):
|
|
1015
949
|
class CheckClusterScopedResourceNames:
|
1016
950
|
oc_map: OC_Map
|
1017
951
|
ri: ResourceInventory
|
1018
|
-
namespaces:
|
952
|
+
namespaces: Iterable[Mapping[str, Any]]
|
1019
953
|
|
1020
954
|
def check(self) -> list[Exception]:
|
1021
955
|
errors: list[Exception] = []
|
@@ -1064,7 +998,7 @@ class CheckClusterScopedResourceNames:
|
|
1064
998
|
@dataclass
|
1065
999
|
class CheckClusterScopedResourceDuplicates:
|
1066
1000
|
oc_map: OC_Map
|
1067
|
-
all_namespaces:
|
1001
|
+
all_namespaces: Iterable[Mapping] | None = None
|
1068
1002
|
|
1069
1003
|
def check(self) -> list[Exception]:
|
1070
1004
|
errors: list[Exception] = []
|
@@ -1086,13 +1020,13 @@ class CheckClusterScopedResourceDuplicates:
|
|
1086
1020
|
|
1087
1021
|
def _find_resource_duplicates(
|
1088
1022
|
self, cluster_cs_resources: dict[str, dict[str, dict[str, list[str]]]]
|
1089
|
-
) -> list[
|
1023
|
+
) -> list[tuple[str, str, str, list[str]]]:
|
1090
1024
|
# ) -> dict[Tuple[str, str, str], list[str]]:
|
1091
1025
|
"""Finds cluster resource duplicates by kind/name.
|
1092
1026
|
:param cluster_cs_resources
|
1093
1027
|
:return: duplicates as [(cluster, kind, name, [namespaces])]
|
1094
1028
|
"""
|
1095
|
-
duplicates: list[
|
1029
|
+
duplicates: list[tuple[str, str, str, list[str]]] = []
|
1096
1030
|
|
1097
1031
|
for cluster, cluster_resources in cluster_cs_resources.items():
|
1098
1032
|
_kind_name: dict[str, dict[str, list[str]]] = {}
|
@@ -1111,10 +1045,9 @@ class CheckClusterScopedResourceDuplicates:
|
|
1111
1045
|
def check_cluster_scoped_resources(
|
1112
1046
|
oc_map: OC_Map,
|
1113
1047
|
ri: ResourceInventory,
|
1114
|
-
namespaces:
|
1115
|
-
all_namespaces:
|
1048
|
+
namespaces: Iterable[Mapping[str, Any]],
|
1049
|
+
all_namespaces: Iterable[Mapping[str, Any]] | None = None,
|
1116
1050
|
) -> bool:
|
1117
|
-
|
1118
1051
|
checks = [
|
1119
1052
|
CheckClusterScopedResourceNames(oc_map, ri, namespaces),
|
1120
1053
|
CheckClusterScopedResourceDuplicates(oc_map, all_namespaces),
|
@@ -1134,7 +1067,7 @@ def check_cluster_scoped_resources(
|
|
1134
1067
|
def get_cluster_scoped_resources(
|
1135
1068
|
oc_map: OC_Map,
|
1136
1069
|
clusters: Iterable[str],
|
1137
|
-
namespaces:
|
1070
|
+
namespaces: Iterable[Mapping[str, Any]] | None = None,
|
1138
1071
|
thread_pool_size: int = 10,
|
1139
1072
|
) -> dict[str, dict[str, dict[str, list[str]]]]:
|
1140
1073
|
"""Returns cluster scoped resources for a list of clusters
|
@@ -1171,7 +1104,7 @@ def get_cluster_scoped_resources(
|
|
1171
1104
|
def _get_namespace_cluster_scoped_resources(
|
1172
1105
|
namespace: Mapping,
|
1173
1106
|
oc_map: OC_Map,
|
1174
|
-
) ->
|
1107
|
+
) -> tuple[str, str, dict[str, dict[str, Any]]]:
|
1175
1108
|
"""Returns all non-namespaced resources defined in a namespace manifest.
|
1176
1109
|
|
1177
1110
|
:param namespace: the namespace dict
|
@@ -1190,7 +1123,7 @@ def _get_namespace_cluster_scoped_resources(
|
|
1190
1123
|
|
1191
1124
|
|
1192
1125
|
def early_exit_desired_state(
|
1193
|
-
providers: list[str], resource_schema_filter:
|
1126
|
+
providers: list[str], resource_schema_filter: str | None = None
|
1194
1127
|
) -> dict[str, Any]:
|
1195
1128
|
settings = queries.get_secret_reader_settings()
|
1196
1129
|
namespaces, _ = get_namespaces(
|
@@ -1212,20 +1145,28 @@ def early_exit_desired_state(
|
|
1212
1145
|
settings=settings,
|
1213
1146
|
)
|
1214
1147
|
|
1215
|
-
def post_process_ns(ns):
|
1216
|
-
ns[IDENTIFIER_FIELD_NAME] = f"{ns['cluster']['name']}/{ns['name']}"
|
1148
|
+
def post_process_ns(ns: MutableMapping) -> MutableMapping:
|
1217
1149
|
# the sharedResources have been aggreated into the openshiftResources
|
1218
1150
|
# and are no longer needed - speeds up diffing process
|
1219
1151
|
del ns["sharedResources"]
|
1220
1152
|
return ns
|
1221
1153
|
|
1154
|
+
# assemble all namespaces and resources file under a cluster
|
1155
|
+
state_for_clusters = defaultdict(list)
|
1156
|
+
for ns in namespaces:
|
1157
|
+
state_for_clusters[ns["cluster"]["name"]].append(post_process_ns(ns))
|
1158
|
+
for res in resources:
|
1159
|
+
state_for_clusters[res["cluster"]].append(res)
|
1160
|
+
|
1222
1161
|
return {
|
1223
|
-
"
|
1224
|
-
|
1162
|
+
"state": {
|
1163
|
+
cluster: {"shard": cluster, "hash": DeepHash(state).get(state)}
|
1164
|
+
for cluster, state in state_for_clusters.items()
|
1165
|
+
}
|
1225
1166
|
}
|
1226
1167
|
|
1227
1168
|
|
1228
|
-
def _early_exit_fetch_resource(spec, settings):
|
1169
|
+
def _early_exit_fetch_resource(spec: Sequence, settings: Mapping) -> dict[str, str]:
|
1229
1170
|
resource = spec[0]
|
1230
1171
|
ns_info = spec[1]
|
1231
1172
|
cluster_name = ns_info["cluster"]["name"]
|
@@ -1253,47 +1194,82 @@ def _early_exit_fetch_resource(spec, settings):
|
|
1253
1194
|
|
1254
1195
|
|
1255
1196
|
@contextmanager
|
1256
|
-
def early_exit_monkey_patch():
|
1197
|
+
def early_exit_monkey_patch() -> Generator:
|
1257
1198
|
"""Avoid looking outside of app-interface on early-exit pr-check."""
|
1258
|
-
|
1259
|
-
|
1260
|
-
|
1261
|
-
|
1262
|
-
|
1263
|
-
|
1264
|
-
|
1265
|
-
|
1266
|
-
|
1267
|
-
|
1268
|
-
lambda
|
1199
|
+
with patch.multiple(
|
1200
|
+
jinja2_utils,
|
1201
|
+
lookup_secret=DEFAULT,
|
1202
|
+
lookup_github_file_content=DEFAULT,
|
1203
|
+
url_makes_sense=DEFAULT,
|
1204
|
+
lookup_s3_object=DEFAULT,
|
1205
|
+
list_s3_objects=DEFAULT,
|
1206
|
+
) as mocks:
|
1207
|
+
# mock lookup_secret
|
1208
|
+
mocks["lookup_secret"].side_effect = (
|
1209
|
+
lambda path,
|
1210
|
+
key,
|
1211
|
+
version=None,
|
1212
|
+
tvars=None,
|
1213
|
+
allow_not_found=False,
|
1214
|
+
settings=None,
|
1215
|
+
secret_reader=None: f"vault({path}, {key}, {version}"
|
1269
1216
|
)
|
1270
|
-
|
1271
|
-
|
1272
|
-
|
1273
|
-
|
1274
|
-
|
1275
|
-
|
1217
|
+
# needed for jinja2 `is_safe_callable`
|
1218
|
+
mocks["lookup_secret"].unsafe_callable = False
|
1219
|
+
mocks["lookup_secret"].alters_data = False
|
1220
|
+
|
1221
|
+
# mock lookup_github_file_content
|
1222
|
+
mocks["lookup_github_file_content"].side_effect = (
|
1223
|
+
lambda repo,
|
1224
|
+
path,
|
1225
|
+
ref,
|
1226
|
+
tvars=None,
|
1227
|
+
settings=None,
|
1228
|
+
secret_reader=None: f"github({repo}, {path}, {ref})"
|
1276
1229
|
)
|
1230
|
+
# needed for jinja2 `is_safe_callable`
|
1231
|
+
mocks["lookup_github_file_content"].unsafe_callable = False
|
1232
|
+
mocks["lookup_github_file_content"].alters_data = False
|
1233
|
+
|
1234
|
+
# mock url_makes_sense
|
1235
|
+
mocks["url_makes_sense"].side_effect = lambda url: False
|
1236
|
+
# needed for jinja2 `is_safe_callable`
|
1237
|
+
mocks["url_makes_sense"].unsafe_callable = False
|
1238
|
+
mocks["url_makes_sense"].alters_data = False
|
1239
|
+
|
1240
|
+
# mock lookup_s3_object
|
1241
|
+
mocks["lookup_s3_object"].side_effect = (
|
1242
|
+
lambda account_name,
|
1243
|
+
bucket_name,
|
1244
|
+
path,
|
1245
|
+
region_name=None: f"lookup_s3_object({account_name}, {bucket_name}, {path}, {region_name})"
|
1246
|
+
)
|
1247
|
+
# needed for jinja2 `is_safe_callable`
|
1248
|
+
mocks["lookup_s3_object"].unsafe_callable = False
|
1249
|
+
mocks["lookup_s3_object"].alters_data = False
|
1250
|
+
|
1251
|
+
# mock list_s3_objects
|
1252
|
+
mocks["list_s3_objects"].side_effect = (
|
1253
|
+
lambda account_name,
|
1254
|
+
bucket_name,
|
1255
|
+
path,
|
1256
|
+
region_name=None: f"list_s3_objects({account_name}, {bucket_name}, {path}, {region_name})"
|
1257
|
+
)
|
1258
|
+
# needed for jinja2 `is_safe_callable`
|
1259
|
+
mocks["list_s3_objects"].unsafe_callable = False
|
1260
|
+
mocks["list_s3_objects"].alters_data = False
|
1277
1261
|
|
1278
|
-
|
1279
|
-
|
1280
|
-
|
1281
|
-
|
1282
|
-
|
1283
|
-
check_alertmanager_config,
|
1284
|
-
):
|
1285
|
-
sys.modules[__name__].lookup_secret = lookup_secret
|
1286
|
-
sys.modules[__name__].lookup_github_file_content = lookup_github_file_content
|
1287
|
-
sys.modules[__name__].url_makes_sense = url_makes_sense
|
1288
|
-
sys.modules[__name__].check_alertmanager_config = check_alertmanager_config
|
1262
|
+
with patch(
|
1263
|
+
"reconcile.openshift_resources_base.check_alertmanager_config",
|
1264
|
+
return_value=True,
|
1265
|
+
):
|
1266
|
+
yield
|
1289
1267
|
|
1290
1268
|
|
1291
1269
|
def desired_state_shard_config() -> DesiredStateShardConfig:
|
1292
1270
|
return DesiredStateShardConfig(
|
1293
1271
|
shard_arg_name="cluster_name",
|
1294
|
-
|
1295
|
-
|
1296
|
-
"resources[*].cluster",
|
1297
|
-
},
|
1272
|
+
shard_arg_is_collection=True,
|
1273
|
+
shard_path_selectors={"state.*.shard"},
|
1298
1274
|
sharded_run_review=lambda proposal: len(proposal.proposed_shards) <= 2,
|
1299
1275
|
)
|