qontract-reconcile 0.9.1rc298__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.9.1rc298.dist-info → qontract_reconcile-0.10.1.dev1203.dist-info}/WHEEL +1 -2
- {qontract_reconcile-0.9.1rc298.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.9.1rc298.dist-info/METADATA +0 -63
- qontract_reconcile-0.9.1rc298.dist-info/RECORD +0 -585
- qontract_reconcile-0.9.1rc298.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
@@ -0,0 +1,157 @@
|
|
1
|
+
import base64
|
2
|
+
import logging
|
3
|
+
import re
|
4
|
+
from collections.abc import Mapping
|
5
|
+
from typing import Any
|
6
|
+
|
7
|
+
import jsonpatch # type: ignore
|
8
|
+
from jsonpointer import resolve_pointer # type: ignore
|
9
|
+
|
10
|
+
from reconcile.utils.openshift_resource import QONTRACT_ANNOTATIONS
|
11
|
+
from reconcile.utils.openshift_resource import OpenshiftResource as OR
|
12
|
+
|
13
|
+
NORMALIZE_COMPARE_EXCLUDED_ATTRS = {
|
14
|
+
"creationTimestamp",
|
15
|
+
"resourceVersion",
|
16
|
+
"generation",
|
17
|
+
"selfLink",
|
18
|
+
"uid",
|
19
|
+
"fieldRef",
|
20
|
+
"managedFields",
|
21
|
+
"namespace",
|
22
|
+
}
|
23
|
+
K8S_ANNOTATION_LAC = "kubectl.kubernetes.io/last-applied-configuration"
|
24
|
+
NORMALIZE_IGNORE_ANNOTATIONS = QONTRACT_ANNOTATIONS | {K8S_ANNOTATION_LAC}
|
25
|
+
|
26
|
+
|
27
|
+
def _normalize_secret(secret: OR) -> None:
|
28
|
+
body = secret.body
|
29
|
+
string_data = body.get("stringData")
|
30
|
+
|
31
|
+
if string_data:
|
32
|
+
data = body.get("data") or {}
|
33
|
+
body["data"] = data | {
|
34
|
+
k: base64.b64encode(str(v).encode()).decode("utf-8")
|
35
|
+
for k, v in string_data.items()
|
36
|
+
}
|
37
|
+
secret.body = {k: v for k, v in body.items() if k != "stringData"}
|
38
|
+
|
39
|
+
|
40
|
+
def _normalize_noop(item: OR) -> None:
|
41
|
+
pass
|
42
|
+
|
43
|
+
|
44
|
+
NORMALIZERS = {"Secret": _normalize_secret}
|
45
|
+
|
46
|
+
|
47
|
+
def normalize_object(item: OR) -> OR:
|
48
|
+
# Remove K8s managed attributes not needed to compare objects
|
49
|
+
metadata = {
|
50
|
+
k: v
|
51
|
+
for k, v in item.body["metadata"].items()
|
52
|
+
if k not in NORMALIZE_COMPARE_EXCLUDED_ATTRS
|
53
|
+
}
|
54
|
+
|
55
|
+
n = OR(
|
56
|
+
body=item.body | {"metadata": metadata},
|
57
|
+
integration=item.integration,
|
58
|
+
integration_version=item.integration_version,
|
59
|
+
error_details=item.error_details,
|
60
|
+
caller_name=item.caller_name,
|
61
|
+
validate_k8s_object=False,
|
62
|
+
)
|
63
|
+
|
64
|
+
annotations = n.body.get("metadata").get("annotations") or {}
|
65
|
+
metadata["annotations"] = {
|
66
|
+
k: v for k, v in annotations.items() if k not in NORMALIZE_IGNORE_ANNOTATIONS
|
67
|
+
}
|
68
|
+
|
69
|
+
# Run normalizers on Kinds with special needs
|
70
|
+
NORMALIZERS.get(n.body["kind"], _normalize_noop)(n)
|
71
|
+
return n
|
72
|
+
|
73
|
+
|
74
|
+
CPU_REGEX = re.compile(r"/.*/(requests|limits)/cpu$")
|
75
|
+
EMPTY_ENV_VALUE = re.compile(r"/.*/env/[0-9]+/value$")
|
76
|
+
|
77
|
+
|
78
|
+
def is_cpu_mutation(current: OR, desired: OR, patch: Mapping[str, Any]) -> bool:
|
79
|
+
pointer = patch["path"]
|
80
|
+
if re.match(CPU_REGEX, pointer):
|
81
|
+
current_value = resolve_pointer(current.body, pointer)
|
82
|
+
desired_value = patch["value"]
|
83
|
+
return OR.cpu_equal(current_value, desired_value)
|
84
|
+
|
85
|
+
return False
|
86
|
+
|
87
|
+
|
88
|
+
def is_empty_env_value(current: OR, desired: OR, patch: Mapping[str, Any]) -> bool:
|
89
|
+
"""Check if the patch is an empty env value. Empty values are removed in the
|
90
|
+
current object
|
91
|
+
|
92
|
+
:param current: Current object
|
93
|
+
:param desired: Desired object
|
94
|
+
:param patch: JSON patch
|
95
|
+
:return: True if the change is not needed, False otherwise
|
96
|
+
"""
|
97
|
+
pointer = patch["path"]
|
98
|
+
return bool(
|
99
|
+
patch["op"] == "add"
|
100
|
+
and not patch["value"]
|
101
|
+
and re.match(EMPTY_ENV_VALUE, pointer)
|
102
|
+
)
|
103
|
+
|
104
|
+
|
105
|
+
def is_valid_change(current: OR, desired: OR, patch: Mapping[str, Any]) -> bool:
|
106
|
+
# Only consider added or replaced values on the Desired object
|
107
|
+
if patch["op"] not in {"add", "replace"}:
|
108
|
+
return False
|
109
|
+
|
110
|
+
# Check known mutations. Replaced values can happen if values have been
|
111
|
+
# mutated by the API server
|
112
|
+
if is_cpu_mutation(current, desired, patch):
|
113
|
+
return False
|
114
|
+
|
115
|
+
# Other cases
|
116
|
+
return not is_empty_env_value(current, desired, patch)
|
117
|
+
|
118
|
+
|
119
|
+
def three_way_diff_using_hash(c_item: OR, d_item: OR) -> bool:
|
120
|
+
c_item_sha256 = ""
|
121
|
+
try:
|
122
|
+
annotations = c_item.body["metadata"]["annotations"]
|
123
|
+
c_item_sha256 = annotations["qontract.sha256sum"]
|
124
|
+
except KeyError:
|
125
|
+
logging.debug("Current object QR hash is missing -> Apply")
|
126
|
+
return False
|
127
|
+
|
128
|
+
if (
|
129
|
+
c_item_integration := annotations["qontract.integration"]
|
130
|
+
) != d_item.integration:
|
131
|
+
logging.info(
|
132
|
+
f"resource switching integration from {c_item_integration} to {d_item.integration}"
|
133
|
+
)
|
134
|
+
return False
|
135
|
+
|
136
|
+
# Original object does not match Desired -> Apply
|
137
|
+
# Current object hash is not recalculated!
|
138
|
+
if c_item_sha256 != d_item.sha256sum():
|
139
|
+
logging.debug("Original and Desired objects hash differs -> Apply")
|
140
|
+
return False
|
141
|
+
|
142
|
+
# The patch only detects changes with attributes defined in the desired state.
|
143
|
+
# Values in the current state added by operators or other actors are not taken
|
144
|
+
# into account
|
145
|
+
|
146
|
+
current = normalize_object(c_item)
|
147
|
+
desired = normalize_object(d_item)
|
148
|
+
|
149
|
+
patch = jsonpatch.JsonPatch.from_diff(current.body, desired.body)
|
150
|
+
valid_changes = [
|
151
|
+
item for item in patch.patch if is_valid_change(current, desired, item)
|
152
|
+
]
|
153
|
+
if len(valid_changes) > 0:
|
154
|
+
logging.debug("Desired and Current objects differ -> Apply: %s", valid_changes)
|
155
|
+
return False
|
156
|
+
|
157
|
+
return True
|
@@ -2,10 +2,7 @@ import logging
|
|
2
2
|
import os
|
3
3
|
import threading
|
4
4
|
from collections.abc import Mapping
|
5
|
-
from typing import
|
6
|
-
Any,
|
7
|
-
Optional,
|
8
|
-
)
|
5
|
+
from typing import Any
|
9
6
|
|
10
7
|
from UnleashClient import (
|
11
8
|
BaseCache,
|
@@ -13,84 +10,93 @@ from UnleashClient import (
|
|
13
10
|
)
|
14
11
|
from UnleashClient.strategies import Strategy
|
15
12
|
|
16
|
-
client:
|
13
|
+
client: UnleashClient | None = None
|
17
14
|
client_lock = threading.Lock()
|
18
15
|
|
19
16
|
|
20
17
|
class CacheDict(BaseCache):
|
21
|
-
def __init__(self):
|
22
|
-
self.cache = {}
|
18
|
+
def __init__(self) -> None:
|
19
|
+
self.cache: dict = {}
|
23
20
|
|
24
|
-
def set(self, key: str, value: Any):
|
21
|
+
def set(self, key: str, value: Any) -> None:
|
25
22
|
self.cache[key] = value
|
26
23
|
|
27
|
-
def mset(self, data: dict):
|
24
|
+
def mset(self, data: dict) -> None:
|
28
25
|
self.cache.update(data)
|
29
26
|
|
30
|
-
def get(self, key: str, default:
|
27
|
+
def get(self, key: str, default: Any | None = None) -> Any:
|
31
28
|
return self.cache.get(key, default)
|
32
29
|
|
33
|
-
def exists(self, key: str):
|
30
|
+
def exists(self, key: str) -> bool:
|
34
31
|
return key in self.cache
|
35
32
|
|
36
|
-
def destroy(self):
|
33
|
+
def destroy(self) -> None:
|
37
34
|
self.cache = {}
|
38
35
|
|
39
36
|
|
40
|
-
class
|
37
|
+
class ClusterStrategy(Strategy):
|
41
38
|
def load_provisioning(self) -> list:
|
42
39
|
return [x.strip() for x in self.parameters["cluster_name"].split(",")]
|
43
40
|
|
44
|
-
|
41
|
+
|
42
|
+
class DisableClusterStrategy(ClusterStrategy):
|
43
|
+
def apply(self, context: dict | None = None) -> bool:
|
45
44
|
enable = True
|
46
45
|
|
47
|
-
if context and "cluster_name" in context
|
46
|
+
if context and "cluster_name" in context:
|
48
47
|
# if cluster in context is in clusters sent from server, disable
|
49
48
|
enable = context["cluster_name"] not in self.parsed_provisioning
|
50
49
|
|
51
50
|
return enable
|
52
51
|
|
53
52
|
|
53
|
+
class EnableClusterStrategy(ClusterStrategy):
|
54
|
+
def apply(self, context: dict | None = None) -> bool:
|
55
|
+
enable = False
|
56
|
+
|
57
|
+
if context and "cluster_name" in context:
|
58
|
+
# if cluster in context is in clusters sent from server, enable
|
59
|
+
enable = context["cluster_name"] in self.parsed_provisioning
|
60
|
+
|
61
|
+
return enable
|
62
|
+
|
63
|
+
|
54
64
|
def _get_unleash_api_client(api_url: str, auth_head: str) -> UnleashClient:
|
55
|
-
global client
|
65
|
+
global client # noqa: PLW0603
|
56
66
|
with client_lock:
|
57
67
|
if client is None:
|
58
68
|
logging.getLogger("apscheduler").setLevel(logging.ERROR)
|
59
69
|
logging.getLogger("UnleashClient").setLevel(logging.ERROR)
|
60
|
-
headers = {"Authorization":
|
70
|
+
headers = {"Authorization": auth_head}
|
61
71
|
client = UnleashClient(
|
62
72
|
url=api_url,
|
63
73
|
app_name="qontract-reconcile",
|
64
74
|
custom_headers=headers,
|
65
75
|
cache=CacheDict(),
|
66
|
-
custom_strategies={
|
76
|
+
custom_strategies={
|
77
|
+
"enableCluster": EnableClusterStrategy,
|
78
|
+
"disableCluster": DisableClusterStrategy,
|
79
|
+
},
|
67
80
|
)
|
68
81
|
client.initialize_client()
|
69
82
|
return client
|
70
83
|
|
71
84
|
|
72
|
-
def
|
73
|
-
# Intended for test usage only
|
74
|
-
with client_lock:
|
75
|
-
if client:
|
76
|
-
client.destroy()
|
77
|
-
|
78
|
-
|
79
|
-
def get_feature_toggle_default(feature_name, context):
|
85
|
+
def get_feature_toggle_default(feature_name: str, context: dict) -> bool:
|
80
86
|
return True
|
81
87
|
|
82
88
|
|
83
|
-
def get_feature_toggle_default_false(feature_name, context):
|
89
|
+
def get_feature_toggle_default_false(feature_name: str, context: dict) -> bool:
|
84
90
|
return False
|
85
91
|
|
86
92
|
|
87
93
|
def get_feature_toggle_state(
|
88
|
-
integration_name: str, context:
|
94
|
+
integration_name: str, context: dict | None = None, default: bool = True
|
89
95
|
) -> bool:
|
90
96
|
api_url = os.environ.get("UNLEASH_API_URL")
|
91
97
|
client_access_token = os.environ.get("UNLEASH_CLIENT_ACCESS_TOKEN")
|
92
98
|
if not (api_url and client_access_token):
|
93
|
-
return get_feature_toggle_default(
|
99
|
+
return get_feature_toggle_default("", {})
|
94
100
|
|
95
101
|
c = _get_unleash_api_client(
|
96
102
|
api_url,
|
@@ -0,0 +1,145 @@
|
|
1
|
+
from enum import StrEnum
|
2
|
+
|
3
|
+
import requests
|
4
|
+
from pydantic import BaseModel, Field
|
5
|
+
|
6
|
+
from reconcile.utils.rest_api_base import ApiBase, BearerTokenAuth
|
7
|
+
|
8
|
+
|
9
|
+
class FeatureToggleType(StrEnum):
|
10
|
+
experiment = "experiment"
|
11
|
+
kill_switch = "kill-switch"
|
12
|
+
release = "release"
|
13
|
+
operational = "operational"
|
14
|
+
permission = "permission"
|
15
|
+
|
16
|
+
|
17
|
+
class Environment(BaseModel):
|
18
|
+
name: str
|
19
|
+
enabled: bool
|
20
|
+
|
21
|
+
def __eq__(self, other: object) -> bool:
|
22
|
+
if isinstance(other, Environment):
|
23
|
+
return self.name == other.name
|
24
|
+
return self.name == other
|
25
|
+
|
26
|
+
|
27
|
+
class FeatureToggle(BaseModel):
|
28
|
+
name: str
|
29
|
+
type: FeatureToggleType = FeatureToggleType.release
|
30
|
+
description: str | None = None
|
31
|
+
impression_data: bool = Field(False, alias="impressionData")
|
32
|
+
environments: list[Environment]
|
33
|
+
|
34
|
+
class Config:
|
35
|
+
allow_population_by_field_name = True
|
36
|
+
|
37
|
+
def __eq__(self, other: object) -> bool:
|
38
|
+
if isinstance(other, FeatureToggle):
|
39
|
+
return self.name == other.name
|
40
|
+
return self.name == other
|
41
|
+
|
42
|
+
|
43
|
+
class Project(BaseModel):
|
44
|
+
pk: str = Field(alias="id")
|
45
|
+
name: str
|
46
|
+
feature_toggles: list[FeatureToggle] = []
|
47
|
+
|
48
|
+
class Config:
|
49
|
+
allow_population_by_field_name = True
|
50
|
+
|
51
|
+
|
52
|
+
class TokenAuth(BearerTokenAuth):
|
53
|
+
def __call__(self, r: requests.PreparedRequest) -> requests.PreparedRequest:
|
54
|
+
r.headers["Authorization"] = self.token
|
55
|
+
return r
|
56
|
+
|
57
|
+
|
58
|
+
class UnleashServer(ApiBase):
|
59
|
+
"""Unleash server API client."""
|
60
|
+
|
61
|
+
def projects(self, include_feature_toggles: bool = False) -> list[Project]:
|
62
|
+
"""List all projects."""
|
63
|
+
projects = []
|
64
|
+
for project_data in self._list("/api/admin/projects", attribute="projects"):
|
65
|
+
project = Project(**project_data)
|
66
|
+
if include_feature_toggles:
|
67
|
+
project.feature_toggles = self.feature_toggles(project.pk)
|
68
|
+
projects.append(project)
|
69
|
+
return projects
|
70
|
+
|
71
|
+
def feature_toggles(self, project_id: str) -> list[FeatureToggle]:
|
72
|
+
"""List all feature toggles for a project."""
|
73
|
+
return [
|
74
|
+
FeatureToggle(**i)
|
75
|
+
for i in self._list(
|
76
|
+
f"/api/admin/projects/{project_id}/features", attribute="features"
|
77
|
+
)
|
78
|
+
]
|
79
|
+
|
80
|
+
def environments(self, project_id: str) -> list[Environment]:
|
81
|
+
"""Gets the environments that are available for this project. An environment is available for a project if enabled in the project configuration."""
|
82
|
+
return [
|
83
|
+
Environment(**i)
|
84
|
+
for i in self._list(
|
85
|
+
f"/api/admin/environments/project/{project_id}",
|
86
|
+
attribute="environments",
|
87
|
+
)
|
88
|
+
]
|
89
|
+
|
90
|
+
def create_feature_toggle(
|
91
|
+
self,
|
92
|
+
project_id: str,
|
93
|
+
name: str,
|
94
|
+
description: str,
|
95
|
+
type: FeatureToggleType,
|
96
|
+
impression_data: bool,
|
97
|
+
) -> None:
|
98
|
+
"""Create a feature toggle."""
|
99
|
+
self._post(
|
100
|
+
f"/api/admin/projects/{project_id}/features",
|
101
|
+
data={
|
102
|
+
"name": name,
|
103
|
+
"description": description,
|
104
|
+
"type": type.value,
|
105
|
+
"impressionData": impression_data,
|
106
|
+
},
|
107
|
+
)
|
108
|
+
|
109
|
+
def update_feature_toggle(
|
110
|
+
self,
|
111
|
+
project_id: str,
|
112
|
+
name: str,
|
113
|
+
description: str,
|
114
|
+
type: FeatureToggleType,
|
115
|
+
impression_data: bool,
|
116
|
+
) -> None:
|
117
|
+
"""Create a feature toggle."""
|
118
|
+
self._put(
|
119
|
+
f"/api/admin/projects/{project_id}/features/{name}",
|
120
|
+
data={
|
121
|
+
"description": description,
|
122
|
+
"type": type.value,
|
123
|
+
"impressionData": impression_data,
|
124
|
+
},
|
125
|
+
)
|
126
|
+
|
127
|
+
def delete_feature_toggle(self, project_id: str, name: str) -> None:
|
128
|
+
"""Delete a feature toggle."""
|
129
|
+
# First archive the feature toggle
|
130
|
+
self._delete(f"/api/admin/projects/{project_id}/features/{name}")
|
131
|
+
# Then delete it
|
132
|
+
self._post(
|
133
|
+
f"/api/admin/projects/{project_id}/delete",
|
134
|
+
data={"features": [name]},
|
135
|
+
)
|
136
|
+
|
137
|
+
def set_feature_toggle_state(
|
138
|
+
self, project_id: str, name: str, environment: str, enabled: bool
|
139
|
+
) -> None:
|
140
|
+
"""Set the state of a feature toggle."""
|
141
|
+
base_url = f"/api/admin/projects/{project_id}/features/{name}/environments/{environment}"
|
142
|
+
if enabled:
|
143
|
+
self._post(f"{base_url}/on")
|
144
|
+
else:
|
145
|
+
self._post(f"{base_url}/off")
|
reconcile/utils/vault.py
CHANGED
@@ -5,7 +5,6 @@ import threading
|
|
5
5
|
import time
|
6
6
|
from collections.abc import Mapping
|
7
7
|
from functools import lru_cache
|
8
|
-
from typing import Optional
|
9
8
|
|
10
9
|
import hvac
|
11
10
|
import requests
|
@@ -60,11 +59,11 @@ class _VaultClient:
|
|
60
59
|
|
61
60
|
def __init__(
|
62
61
|
self,
|
63
|
-
server:
|
64
|
-
role_id:
|
65
|
-
secret_id:
|
66
|
-
kube_auth_role:
|
67
|
-
kube_auth_mount:
|
62
|
+
server: str | None = None,
|
63
|
+
role_id: str | None = None,
|
64
|
+
secret_id: str | None = None,
|
65
|
+
kube_auth_role: str | None = None,
|
66
|
+
kube_auth_mount: str | None = None,
|
68
67
|
auto_refresh: bool = True,
|
69
68
|
):
|
70
69
|
config = get_config()
|
@@ -96,12 +95,9 @@ class _VaultClient:
|
|
96
95
|
self._get_mount_version = lru_cache(maxsize=128)(self.__get_mount_version)
|
97
96
|
self._read_all_v2 = lru_cache(maxsize=2048)(self.__read_all_v2)
|
98
97
|
|
99
|
-
# This is a threaded world. Let's define a big
|
100
|
-
# connections pool to live in that world
|
101
|
-
# (this avoids the warning "Connection pool is
|
102
|
-
# full, discarding connection: vault.devshift.net")
|
103
98
|
session = requests.Session()
|
104
|
-
|
99
|
+
# There are at most 10 working threads in reconcile, plus 1 daemon thread for auto refresh
|
100
|
+
adapter = HTTPAdapter(pool_maxsize=11)
|
105
101
|
session.mount("https://", adapter)
|
106
102
|
self._client = hvac.Client(url=server, session=session)
|
107
103
|
self._close_lock = threading.Lock()
|
@@ -113,7 +109,10 @@ class _VaultClient:
|
|
113
109
|
self._refresh_client_auth()
|
114
110
|
authenticated = self._client.is_authenticated()
|
115
111
|
break
|
116
|
-
except
|
112
|
+
except (
|
113
|
+
requests.exceptions.ConnectionError,
|
114
|
+
requests.exceptions.TooManyRedirects,
|
115
|
+
):
|
117
116
|
time.sleep(1)
|
118
117
|
|
119
118
|
if not authenticated:
|
@@ -154,7 +153,7 @@ class _VaultClient:
|
|
154
153
|
def _refresh_client_auth(self):
|
155
154
|
if self.kube_auth_enabled:
|
156
155
|
# must read each time to account for sa token refresh
|
157
|
-
with open(self.kube_sa_token_path) as f:
|
156
|
+
with open(self.kube_sa_token_path, encoding="locale") as f:
|
158
157
|
try:
|
159
158
|
self._client.auth_kubernetes(
|
160
159
|
role=self.kube_auth_role,
|
@@ -167,7 +166,7 @@ class _VaultClient:
|
|
167
166
|
self._client.auth_approle(self.role_id, self.secret_id)
|
168
167
|
|
169
168
|
@retry()
|
170
|
-
def read_all_with_version(self, secret: Mapping) -> tuple[Mapping,
|
169
|
+
def read_all_with_version(self, secret: Mapping) -> tuple[Mapping, str | None]:
|
171
170
|
"""Returns a dictionary of keys and values in a Vault secret and the
|
172
171
|
version of the secret, for V1 secrets, version will be None.
|
173
172
|
|
@@ -194,7 +193,6 @@ class _VaultClient:
|
|
194
193
|
|
195
194
|
return data, version
|
196
195
|
|
197
|
-
@retry()
|
198
196
|
def read_all(self, secret: Mapping) -> dict:
|
199
197
|
"""Returns a dictionary of keys and values in a Vault secret.
|
200
198
|
|
@@ -219,9 +217,7 @@ class _VaultClient:
|
|
219
217
|
|
220
218
|
return version
|
221
219
|
|
222
|
-
def __read_all_v2(
|
223
|
-
self, path: str, version: Optional[str]
|
224
|
-
) -> tuple[dict, Optional[str]]:
|
220
|
+
def __read_all_v2(self, path: str, version: str | None) -> tuple[dict, str | None]:
|
225
221
|
path_split = path.split("/")
|
226
222
|
mount_point = path_split[0]
|
227
223
|
read_path = "/".join(path_split[1:])
|
@@ -241,10 +237,10 @@ class _VaultClient:
|
|
241
237
|
)
|
242
238
|
except InvalidPath:
|
243
239
|
msg = f"version '{version}' not found " f"for secret with path '{path}'."
|
244
|
-
raise SecretVersionNotFound(msg)
|
240
|
+
raise SecretVersionNotFound(msg) from None
|
245
241
|
except hvac.exceptions.Forbidden:
|
246
242
|
msg = f"permission denied accessing secret '{path}'"
|
247
|
-
raise SecretAccessForbidden(msg)
|
243
|
+
raise SecretAccessForbidden(msg) from None
|
248
244
|
if secret is None or "data" not in secret or "data" not in secret["data"]:
|
249
245
|
raise SecretNotFound(path)
|
250
246
|
|
@@ -257,7 +253,7 @@ class _VaultClient:
|
|
257
253
|
secret = self._client.read(path)
|
258
254
|
except hvac.exceptions.Forbidden:
|
259
255
|
msg = f"permission denied accessing secret '{path}'"
|
260
|
-
raise SecretAccessForbidden(msg)
|
256
|
+
raise SecretAccessForbidden(msg) from None
|
261
257
|
|
262
258
|
if secret is None or "data" not in secret:
|
263
259
|
raise SecretNotFound(path)
|
@@ -302,7 +298,7 @@ class _VaultClient:
|
|
302
298
|
try:
|
303
299
|
secret_field = data[field]
|
304
300
|
except KeyError:
|
305
|
-
raise SecretFieldNotFound(f"{path}/{field} ({version})")
|
301
|
+
raise SecretFieldNotFound(f"{path}/{field} ({version})") from None
|
306
302
|
return secret_field
|
307
303
|
|
308
304
|
def _read_v1(self, path, field):
|
@@ -310,7 +306,7 @@ class _VaultClient:
|
|
310
306
|
try:
|
311
307
|
secret_field = data[field]
|
312
308
|
except KeyError:
|
313
|
-
raise SecretFieldNotFound("{}/{}"
|
309
|
+
raise SecretFieldNotFound(f"{path}/{field}") from None
|
314
310
|
return secret_field
|
315
311
|
|
316
312
|
@retry()
|
@@ -362,14 +358,14 @@ class _VaultClient:
|
|
362
358
|
self._read_all_v2.cache_clear()
|
363
359
|
except hvac.exceptions.Forbidden:
|
364
360
|
msg = f"permission denied accessing secret '{path}'"
|
365
|
-
raise SecretAccessForbidden(msg)
|
361
|
+
raise SecretAccessForbidden(msg) from None
|
366
362
|
|
367
363
|
def _write_v1(self, path, data):
|
368
364
|
try:
|
369
365
|
self._client.write(path, **data)
|
370
366
|
except hvac.exceptions.Forbidden:
|
371
367
|
msg = f"permission denied accessing secret '{path}'"
|
372
|
-
raise SecretAccessForbidden(msg)
|
368
|
+
raise SecretAccessForbidden(msg) from None
|
373
369
|
|
374
370
|
def _list_kv2(self, path: str) -> dict:
|
375
371
|
try:
|
@@ -380,23 +376,23 @@ class _VaultClient:
|
|
380
376
|
return response
|
381
377
|
except hvac.exceptions.Forbidden:
|
382
378
|
msg = f"permission denied accessing path '{path}'"
|
383
|
-
raise PathAccessForbidden(msg)
|
379
|
+
raise PathAccessForbidden(msg) from None
|
384
380
|
|
385
381
|
def _list(self, path: str) -> dict:
|
386
382
|
try:
|
387
383
|
return self._client.list(path)
|
388
384
|
except hvac.exceptions.Forbidden:
|
389
385
|
msg = f"permission denied accessing path '{path}'"
|
390
|
-
raise PathAccessForbidden(msg)
|
386
|
+
raise PathAccessForbidden(msg) from None
|
391
387
|
|
392
388
|
def list(self, path: str) -> list[str]:
|
393
389
|
"""Returns a list of secrets in a given path."""
|
394
390
|
kv_version = self._get_mount_version_by_secret_path(path)
|
395
|
-
if kv_version == 2
|
396
|
-
path_list = self._list_kv2(path)
|
397
|
-
else:
|
398
|
-
path_list = self._list(path)
|
391
|
+
path_list = self._list_kv2(path) if kv_version == 2 else self._list(path)
|
399
392
|
|
393
|
+
if not path_list:
|
394
|
+
# path list can be None if the path does not exist
|
395
|
+
return []
|
400
396
|
return path_list["data"]["keys"] or []
|
401
397
|
|
402
398
|
def list_all(self, path):
|
@@ -411,9 +407,23 @@ class _VaultClient:
|
|
411
407
|
secrets.append(secret_path)
|
412
408
|
return secrets
|
413
409
|
|
410
|
+
@retry()
|
411
|
+
def delete(self, path: str) -> None:
|
412
|
+
"""Deletes a V1 secret from Vault."""
|
413
|
+
kv_version = self._get_mount_version_by_secret_path(path)
|
414
|
+
if kv_version == 2:
|
415
|
+
raise ValueError("deleting V2 secrets is not supported yet")
|
416
|
+
self._delete_v1(path)
|
417
|
+
|
418
|
+
def _delete_v1(self, path):
|
419
|
+
try:
|
420
|
+
self._client.delete(path)
|
421
|
+
except hvac.exceptions.Forbidden:
|
422
|
+
msg = f"permission denied accessing secret '{path}'"
|
423
|
+
raise SecretAccessForbidden(msg) from None
|
414
424
|
|
415
|
-
class VaultClient:
|
416
425
|
|
426
|
+
class VaultClient:
|
417
427
|
_instance_lock = threading.Lock()
|
418
428
|
_instance = None
|
419
429
|
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
2
|
from typing import (
|
3
|
-
Optional,
|
4
3
|
cast,
|
5
4
|
)
|
6
5
|
|
@@ -12,13 +11,12 @@ from reconcile.utils.vault import (
|
|
12
11
|
|
13
12
|
@dataclass
|
14
13
|
class VaultSecretRef:
|
15
|
-
|
16
14
|
_ALL_FIELDS = "all"
|
17
15
|
|
18
16
|
path: str
|
19
17
|
field: str
|
20
|
-
format:
|
21
|
-
version:
|
18
|
+
format: str | None = None
|
19
|
+
version: int | None = None
|
22
20
|
|
23
21
|
def get(self, field=None, default=None):
|
24
22
|
secret_content = self._resolve_secret()
|