qontract-reconcile 0.10.2.dev361__py3-none-any.whl → 0.10.2.dev430__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/METADATA +13 -12
- {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/RECORD +351 -345
- reconcile/acs_rbac.py +2 -2
- reconcile/aus/advanced_upgrade_service.py +18 -12
- reconcile/aus/base.py +134 -32
- reconcile/aus/cluster_version_data.py +15 -5
- reconcile/aus/models.py +3 -1
- reconcile/aus/ocm_addons_upgrade_scheduler_org.py +1 -0
- reconcile/aus/ocm_upgrade_scheduler.py +8 -1
- reconcile/aus/ocm_upgrade_scheduler_org.py +20 -5
- reconcile/aus/version_gates/sts_version_gate_handler.py +54 -1
- reconcile/automated_actions/config/integration.py +16 -4
- reconcile/aws_account_manager/integration.py +8 -8
- reconcile/aws_account_manager/reconciler.py +3 -3
- reconcile/aws_ami_cleanup/integration.py +8 -12
- reconcile/aws_ami_share.py +69 -62
- reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
- reconcile/aws_iam_keys.py +1 -0
- reconcile/aws_saml_idp/integration.py +12 -4
- reconcile/aws_saml_roles/integration.py +30 -23
- reconcile/aws_version_sync/integration.py +6 -12
- reconcile/change_owners/bundle.py +3 -3
- reconcile/change_owners/change_log_tracking.py +3 -2
- reconcile/change_owners/change_owners.py +1 -1
- reconcile/change_owners/diff.py +0 -2
- reconcile/checkpoint.py +11 -3
- reconcile/cli.py +93 -10
- reconcile/dashdotdb_dora.py +5 -12
- reconcile/dashdotdb_slo.py +1 -1
- reconcile/database_access_manager.py +123 -117
- reconcile/dynatrace_token_provider/integration.py +1 -1
- reconcile/endpoints_discovery/integration.py +4 -1
- reconcile/endpoints_discovery/merge_request.py +1 -1
- reconcile/endpoints_discovery/merge_request_manager.py +8 -8
- reconcile/external_resources/factories.py +4 -6
- reconcile/external_resources/integration.py +1 -1
- reconcile/external_resources/manager.py +8 -6
- reconcile/external_resources/meta.py +0 -1
- reconcile/external_resources/metrics.py +1 -1
- reconcile/external_resources/model.py +19 -15
- reconcile/external_resources/reconciler.py +7 -4
- reconcile/external_resources/secrets_sync.py +4 -7
- reconcile/external_resources/state.py +26 -16
- reconcile/fleet_labeler/integration.py +1 -1
- reconcile/gabi_authorized_users.py +5 -2
- reconcile/gcp_image_mirror.py +2 -2
- reconcile/github_org.py +1 -1
- reconcile/github_owners.py +4 -0
- reconcile/gitlab_housekeeping.py +13 -15
- reconcile/gitlab_members.py +6 -12
- reconcile/gitlab_owners.py +15 -11
- reconcile/gitlab_permissions.py +8 -12
- reconcile/glitchtip_project_alerts/integration.py +3 -1
- reconcile/gql_definitions/acs/acs_instances.py +5 -5
- reconcile/gql_definitions/acs/acs_policies.py +5 -5
- reconcile/gql_definitions/acs/acs_rbac.py +5 -5
- reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +5 -5
- reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +5 -5
- reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +5 -5
- reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
- reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
- reconcile/gql_definitions/automated_actions/instance.py +46 -7
- reconcile/gql_definitions/aws_account_manager/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +15 -5
- reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +27 -66
- reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +15 -5
- reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +15 -5
- reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
- reconcile/gql_definitions/aws_version_sync/clusters.py +5 -5
- reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
- reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
- reconcile/gql_definitions/change_owners/queries/self_service_roles.py +5 -5
- reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +5 -5
- reconcile/gql_definitions/common/alerting_services_settings.py +5 -5
- reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
- reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
- reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_roles.py +5 -5
- reconcile/gql_definitions/common/app_interface_state_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
- reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
- reconcile/gql_definitions/common/apps.py +5 -5
- reconcile/gql_definitions/common/aws_vpc_requests.py +15 -5
- reconcile/gql_definitions/common/aws_vpcs.py +5 -5
- reconcile/gql_definitions/common/clusters.py +5 -5
- reconcile/gql_definitions/common/clusters_minimal.py +5 -5
- reconcile/gql_definitions/common/clusters_with_dms.py +5 -5
- reconcile/gql_definitions/common/clusters_with_peering.py +5 -5
- reconcile/gql_definitions/common/github_orgs.py +5 -5
- reconcile/gql_definitions/common/jira_settings.py +5 -5
- reconcile/gql_definitions/common/jiralert_settings.py +5 -5
- reconcile/gql_definitions/common/ldap_settings.py +5 -5
- reconcile/gql_definitions/common/namespaces.py +5 -5
- reconcile/gql_definitions/common/namespaces_minimal.py +7 -5
- reconcile/gql_definitions/common/ocm_env_telemeter.py +5 -5
- reconcile/gql_definitions/common/ocm_environments.py +5 -5
- reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
- reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -5
- reconcile/gql_definitions/common/pipeline_providers.py +5 -5
- reconcile/gql_definitions/common/quay_instances.py +5 -5
- reconcile/gql_definitions/common/quay_orgs.py +5 -5
- reconcile/gql_definitions/common/reserved_networks.py +5 -5
- reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
- reconcile/gql_definitions/common/saas_files.py +5 -5
- reconcile/gql_definitions/common/saas_target_namespaces.py +5 -5
- reconcile/gql_definitions/common/saasherder_settings.py +5 -5
- reconcile/gql_definitions/common/slack_workspaces.py +5 -5
- reconcile/gql_definitions/common/smtp_client_settings.py +5 -5
- reconcile/gql_definitions/common/state_aws_account.py +5 -5
- reconcile/gql_definitions/common/users.py +5 -5
- reconcile/gql_definitions/common/users_with_paths.py +5 -5
- reconcile/gql_definitions/cost_report/app_names.py +5 -5
- reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
- reconcile/gql_definitions/cost_report/settings.py +5 -5
- reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +5 -5
- reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +5 -5
- reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
- reconcile/gql_definitions/email_sender/apps.py +5 -5
- reconcile/gql_definitions/email_sender/emails.py +5 -5
- reconcile/gql_definitions/email_sender/users.py +5 -5
- reconcile/gql_definitions/endpoints_discovery/apps.py +5 -5
- reconcile/gql_definitions/external_resources/aws_accounts.py +5 -5
- reconcile/gql_definitions/external_resources/external_resources_modules.py +5 -5
- reconcile/gql_definitions/external_resources/external_resources_namespaces.py +33 -6
- reconcile/gql_definitions/external_resources/external_resources_settings.py +5 -5
- reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
- reconcile/gql_definitions/fleet_labeler/fleet_labels.py +5 -5
- reconcile/gql_definitions/fragments/aus_organization.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_common.py +7 -5
- reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
- reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
- reconcile/gql_definitions/fragments/aws_organization.py +33 -0
- reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
- reconcile/gql_definitions/fragments/aws_vpc_request.py +7 -5
- reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
- reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
- reconcile/gql_definitions/fragments/disable.py +5 -5
- reconcile/gql_definitions/fragments/email_service.py +5 -5
- reconcile/gql_definitions/fragments/email_user.py +5 -5
- reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
- reconcile/gql_definitions/fragments/membership_source.py +5 -5
- reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
- reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
- reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
- reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
- reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
- reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
- reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
- reconcile/gql_definitions/fragments/resource_values.py +5 -5
- reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
- reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
- reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
- reconcile/gql_definitions/fragments/terraform_state.py +5 -5
- reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
- reconcile/gql_definitions/fragments/user.py +5 -5
- reconcile/gql_definitions/fragments/vault_secret.py +5 -5
- reconcile/gql_definitions/gcp/gcp_docker_repos.py +5 -5
- reconcile/gql_definitions/gcp/gcp_projects.py +5 -5
- reconcile/gql_definitions/gitlab_members/gitlab_instances.py +5 -5
- reconcile/gql_definitions/gitlab_members/permissions.py +5 -5
- reconcile/gql_definitions/glitchtip/glitchtip_instance.py +5 -5
- reconcile/gql_definitions/glitchtip/glitchtip_project.py +5 -5
- reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +5 -5
- reconcile/gql_definitions/integrations/integrations.py +5 -5
- reconcile/gql_definitions/introspection.json +724 -129
- reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +5 -5
- reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +5 -5
- reconcile/gql_definitions/jira/jira_servers.py +5 -5
- reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +9 -5
- reconcile/gql_definitions/jumphosts/jumphosts.py +5 -5
- reconcile/gql_definitions/ldap_groups/roles.py +5 -5
- reconcile/gql_definitions/ldap_groups/settings.py +5 -5
- reconcile/gql_definitions/maintenance/maintenances.py +5 -5
- reconcile/gql_definitions/membershipsources/roles.py +5 -5
- reconcile/gql_definitions/ocm_labels/clusters.py +5 -5
- reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
- reconcile/gql_definitions/openshift_cluster_bots/clusters.py +5 -5
- reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
- reconcile/gql_definitions/openshift_groups/managed_roles.py +5 -5
- reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +5 -5
- reconcile/gql_definitions/quay_membership/quay_membership.py +5 -5
- reconcile/gql_definitions/rhcs/certs.py +25 -79
- reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +43 -0
- reconcile/gql_definitions/rhidp/organizations.py +5 -5
- reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
- reconcile/gql_definitions/service_dependencies/service_dependencies.py +5 -5
- reconcile/gql_definitions/sharding/aws_accounts.py +5 -5
- reconcile/gql_definitions/sharding/ocm_organization.py +5 -5
- reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
- reconcile/gql_definitions/skupper_network/skupper_networks.py +5 -5
- reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
- reconcile/gql_definitions/slack_usergroups/permissions.py +5 -5
- reconcile/gql_definitions/slack_usergroups/users.py +5 -5
- reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
- reconcile/gql_definitions/status_board/status_board.py +5 -5
- reconcile/gql_definitions/statuspage/statuspages.py +5 -5
- reconcile/gql_definitions/templating/template_collection.py +5 -5
- reconcile/gql_definitions/templating/templates.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +5 -5
- reconcile/gql_definitions/terraform_init/aws_accounts.py +19 -5
- reconcile/gql_definitions/terraform_repo/terraform_repo.py +5 -5
- reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
- reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +30 -6
- reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +15 -5
- reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +5 -5
- reconcile/gql_definitions/vault_instances/vault_instances.py +5 -5
- reconcile/gql_definitions/vault_policies/vault_policies.py +5 -5
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +5 -5
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
- reconcile/integrations_manager.py +3 -3
- reconcile/jenkins_worker_fleets.py +10 -8
- reconcile/jira_permissions_validator.py +237 -122
- reconcile/ldap_groups/integration.py +1 -1
- reconcile/ocm/types.py +35 -57
- reconcile/ocm_aws_infrastructure_access.py +1 -1
- reconcile/ocm_clusters.py +4 -4
- reconcile/ocm_labels/integration.py +3 -2
- reconcile/ocm_machine_pools.py +33 -27
- reconcile/openshift_base.py +113 -4
- reconcile/openshift_cluster_bots.py +1 -1
- reconcile/openshift_namespace_labels.py +1 -1
- reconcile/openshift_namespaces.py +97 -101
- reconcile/openshift_resources_base.py +6 -2
- reconcile/openshift_rhcs_certs.py +74 -37
- reconcile/openshift_rolebindings.py +7 -11
- reconcile/openshift_saas_deploy.py +4 -5
- reconcile/openshift_saas_deploy_change_tester.py +9 -7
- reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
- reconcile/openshift_serviceaccount_tokens.py +2 -2
- reconcile/openshift_upgrade_watcher.py +4 -4
- reconcile/oum/labelset.py +5 -3
- reconcile/oum/models.py +1 -4
- reconcile/prometheus_rules_tester/integration.py +3 -3
- reconcile/quay_mirror.py +1 -1
- reconcile/queries.py +131 -0
- reconcile/rhidp/common.py +3 -5
- reconcile/rhidp/sso_client/base.py +16 -5
- reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
- reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
- reconcile/skupper_network/integration.py +2 -2
- reconcile/slack_usergroups.py +35 -14
- reconcile/sql_query.py +1 -0
- reconcile/status_board.py +6 -6
- reconcile/statuspage/atlassian.py +7 -7
- reconcile/statuspage/integrations/maintenances.py +4 -3
- reconcile/statuspage/page.py +4 -9
- reconcile/statuspage/status.py +5 -8
- reconcile/templating/lib/rendering.py +3 -3
- reconcile/templating/renderer.py +2 -2
- reconcile/terraform_aws_route53.py +7 -1
- reconcile/terraform_cloudflare_dns.py +3 -3
- reconcile/terraform_cloudflare_resources.py +5 -5
- reconcile/terraform_cloudflare_users.py +3 -2
- reconcile/terraform_init/integration.py +187 -23
- reconcile/terraform_repo.py +16 -12
- reconcile/terraform_resources.py +6 -6
- reconcile/terraform_tgw_attachments.py +27 -19
- reconcile/terraform_users.py +7 -0
- reconcile/terraform_vpc_peerings.py +14 -3
- reconcile/terraform_vpc_resources/integration.py +10 -1
- reconcile/typed_queries/aws_account_tags.py +41 -0
- reconcile/typed_queries/cost_report/app_names.py +1 -1
- reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
- reconcile/typed_queries/saas_files.py +11 -11
- reconcile/typed_queries/status_board.py +2 -2
- reconcile/unleash_feature_toggles/integration.py +4 -2
- reconcile/utils/acs/base.py +6 -3
- reconcile/utils/acs/policies.py +2 -2
- reconcile/utils/aws_api.py +51 -20
- reconcile/utils/aws_api_typed/api.py +38 -9
- reconcile/utils/aws_api_typed/cloudformation.py +149 -0
- reconcile/utils/aws_api_typed/logs.py +73 -0
- reconcile/utils/aws_api_typed/organization.py +4 -2
- reconcile/utils/binary.py +7 -12
- reconcile/utils/datetime_util.py +67 -0
- reconcile/utils/deadmanssnitch_api.py +1 -1
- reconcile/utils/differ.py +2 -3
- reconcile/utils/early_exit_cache.py +11 -12
- reconcile/utils/expiration.py +7 -3
- reconcile/utils/filtering.py +1 -1
- reconcile/utils/gitlab_api.py +7 -5
- reconcile/utils/glitchtip/client.py +6 -2
- reconcile/utils/glitchtip/models.py +25 -28
- reconcile/utils/gql.py +4 -7
- reconcile/utils/helpers.py +1 -1
- reconcile/utils/instrumented_wrappers.py +1 -1
- reconcile/utils/internal_groups/client.py +2 -2
- reconcile/utils/internal_groups/models.py +8 -17
- reconcile/utils/jinja2/utils.py +6 -101
- reconcile/utils/jira_client.py +82 -63
- reconcile/utils/jjb_client.py +7 -10
- reconcile/utils/jobcontroller/controller.py +2 -2
- reconcile/utils/jobcontroller/models.py +17 -1
- reconcile/utils/json.py +43 -1
- reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
- reconcile/utils/membershipsources/models.py +16 -23
- reconcile/utils/membershipsources/resolver.py +4 -2
- reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
- reconcile/utils/merge_request_manager/parser.py +6 -6
- reconcile/utils/metrics.py +5 -5
- reconcile/utils/models.py +304 -82
- reconcile/utils/mr/app_interface_reporter.py +2 -2
- reconcile/utils/mr/notificator.py +3 -3
- reconcile/utils/mr/update_access_report_base.py +3 -4
- reconcile/utils/mr/user_maintenance.py +3 -2
- reconcile/utils/oc.py +246 -201
- reconcile/utils/oc_filters.py +3 -3
- reconcile/utils/ocm/addons.py +0 -1
- reconcile/utils/ocm/base.py +17 -20
- reconcile/utils/ocm/cluster_groups.py +1 -1
- reconcile/utils/ocm/identity_providers.py +2 -2
- reconcile/utils/ocm/labels.py +1 -1
- reconcile/utils/ocm/products.py +8 -8
- reconcile/utils/ocm/search_filters.py +3 -6
- reconcile/utils/ocm/service_log.py +4 -6
- reconcile/utils/ocm/sre_capability_labels.py +20 -13
- reconcile/utils/openshift_resource.py +8 -3
- reconcile/utils/pagerduty_api.py +10 -7
- reconcile/utils/promotion_state.py +6 -11
- reconcile/utils/raw_github_api.py +1 -1
- reconcile/utils/rhcsv2_certs.py +86 -23
- reconcile/utils/rosa/session.py +16 -0
- reconcile/utils/runtime/integration.py +2 -3
- reconcile/utils/runtime/runner.py +2 -2
- reconcile/utils/saasherder/interfaces.py +13 -20
- reconcile/utils/saasherder/models.py +23 -20
- reconcile/utils/saasherder/saasherder.py +50 -27
- reconcile/utils/slack_api.py +2 -2
- reconcile/utils/sloth.py +171 -2
- reconcile/utils/structs.py +1 -1
- reconcile/utils/terraform_client.py +5 -4
- reconcile/utils/terrascript_aws_client.py +134 -74
- reconcile/utils/unleash/server.py +2 -8
- reconcile/utils/vault.py +5 -12
- reconcile/utils/vcs.py +8 -8
- reconcile/vault_replication.py +107 -42
- tools/app_interface_reporter.py +4 -4
- tools/cli_commands/cost_report/cost_management_api.py +3 -3
- tools/cli_commands/cost_report/view.py +7 -6
- tools/cli_commands/erv2.py +1 -1
- tools/qontract_cli.py +28 -17
- tools/template_validation.py +3 -1
- {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/entry_points.txt +0 -0
|
@@ -2,31 +2,41 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import re
|
|
5
|
-
import typing
|
|
6
5
|
from collections import defaultdict
|
|
7
6
|
from datetime import UTC, datetime, timedelta
|
|
8
|
-
from enum import Enum
|
|
9
7
|
from typing import TYPE_CHECKING
|
|
10
8
|
|
|
11
|
-
from botocore.exceptions import ClientError
|
|
12
9
|
from pydantic import BaseModel
|
|
13
10
|
|
|
14
|
-
from reconcile import queries
|
|
15
11
|
from reconcile.gql_definitions.aws_cloudwatch_log_retention.aws_accounts import (
|
|
16
12
|
AWSAccountCleanupOptionCloudWatchV1,
|
|
17
13
|
AWSAccountV1,
|
|
18
14
|
)
|
|
15
|
+
from reconcile.typed_queries.app_interface_vault_settings import (
|
|
16
|
+
get_app_interface_vault_settings,
|
|
17
|
+
)
|
|
18
|
+
from reconcile.typed_queries.aws_account_tags import get_aws_account_tags
|
|
19
19
|
from reconcile.typed_queries.aws_cloudwatch_log_retention.aws_accounts import (
|
|
20
20
|
get_aws_accounts,
|
|
21
21
|
)
|
|
22
|
+
from reconcile.typed_queries.external_resources import get_settings
|
|
22
23
|
from reconcile.utils import gql
|
|
23
|
-
from reconcile.utils.
|
|
24
|
+
from reconcile.utils.aws_api_typed.api import AWSApi, AWSStaticCredentials
|
|
25
|
+
from reconcile.utils.datetime_util import utc_now
|
|
26
|
+
from reconcile.utils.differ import diff_mappings
|
|
27
|
+
from reconcile.utils.secret_reader import create_secret_reader
|
|
28
|
+
from reconcile.utils.state import init_state
|
|
29
|
+
|
|
30
|
+
TAGS_KEY = "tags.json"
|
|
24
31
|
|
|
25
32
|
if TYPE_CHECKING:
|
|
26
33
|
from collections.abc import Iterable
|
|
27
34
|
|
|
28
35
|
from mypy_boto3_logs.type_defs import LogGroupTypeDef
|
|
29
36
|
|
|
37
|
+
from reconcile.utils.aws_api_typed.logs import AWSApiLogs
|
|
38
|
+
from reconcile.utils.gql import GqlApi
|
|
39
|
+
|
|
30
40
|
|
|
31
41
|
QONTRACT_INTEGRATION = "aws_cloudwatch_log_retention"
|
|
32
42
|
MANAGED_BY_INTEGRATION_KEY = "managed_by_integration"
|
|
@@ -35,7 +45,7 @@ DEFAULT_RETENTION_IN_DAYS = 90
|
|
|
35
45
|
|
|
36
46
|
|
|
37
47
|
class AWSCloudwatchCleanupOption(BaseModel):
|
|
38
|
-
regex:
|
|
48
|
+
regex: re.Pattern
|
|
39
49
|
retention_in_days: int
|
|
40
50
|
delete_empty_log_group: bool
|
|
41
51
|
|
|
@@ -67,16 +77,6 @@ def get_desired_cleanup_options_by_region(
|
|
|
67
77
|
return result
|
|
68
78
|
|
|
69
79
|
|
|
70
|
-
def create_awsapi_client(accounts: list[AWSAccountV1], thread_pool_size: int) -> AWSApi:
|
|
71
|
-
settings = queries.get_secret_reader_settings()
|
|
72
|
-
return AWSApi(
|
|
73
|
-
thread_pool_size,
|
|
74
|
-
[account.dict(by_alias=True) for account in accounts],
|
|
75
|
-
settings=settings,
|
|
76
|
-
init_users=False,
|
|
77
|
-
)
|
|
78
|
-
|
|
79
|
-
|
|
80
80
|
def is_empty(log_group: LogGroupTypeDef) -> bool:
|
|
81
81
|
return log_group["storedBytes"] == 0
|
|
82
82
|
|
|
@@ -85,47 +85,32 @@ def is_longer_than_retention(
|
|
|
85
85
|
log_group: LogGroupTypeDef,
|
|
86
86
|
desired_retention_days: int,
|
|
87
87
|
) -> bool:
|
|
88
|
-
return
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
class TagStatus(Enum):
|
|
94
|
-
NOT_SET = "NOT_SET"
|
|
95
|
-
MANAGED_BY_CURRENT_INTEGRATION = "MANAGED_BY_CURRENT_INTEGRATION"
|
|
96
|
-
MANAGED_BY_OTHER_INTEGRATION = "MANAGED_BY_OTHER_INTEGRATION"
|
|
88
|
+
return (
|
|
89
|
+
datetime.fromtimestamp(log_group["creationTime"] / 1000, tz=UTC)
|
|
90
|
+
+ timedelta(days=desired_retention_days)
|
|
91
|
+
< utc_now()
|
|
92
|
+
)
|
|
97
93
|
|
|
98
94
|
|
|
99
|
-
def
|
|
100
|
-
log_group: LogGroupTypeDef,
|
|
101
|
-
account_name: str,
|
|
102
|
-
region: str,
|
|
103
|
-
aws_api: AWSApi,
|
|
104
|
-
) -> TagStatus:
|
|
105
|
-
tags = aws_api.get_cloudwatch_log_group_tags(
|
|
106
|
-
account_name,
|
|
107
|
-
log_group["arn"],
|
|
108
|
-
region,
|
|
109
|
-
)
|
|
95
|
+
def _is_managed_by_other_integration(tags: dict[str, str]) -> bool:
|
|
110
96
|
managed_by_integration = tags.get(MANAGED_BY_INTEGRATION_KEY)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return TagStatus.MANAGED_BY_OTHER_INTEGRATION
|
|
97
|
+
return (
|
|
98
|
+
managed_by_integration is not None
|
|
99
|
+
and managed_by_integration != QONTRACT_INTEGRATION
|
|
100
|
+
)
|
|
116
101
|
|
|
117
102
|
|
|
118
103
|
def _reconcile_log_group(
|
|
119
104
|
dry_run: bool,
|
|
120
|
-
|
|
105
|
+
log_group: LogGroupTypeDef,
|
|
121
106
|
desired_cleanup_options: Iterable[AWSCloudwatchCleanupOption],
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
107
|
+
desired_tags: dict[str, str],
|
|
108
|
+
last_tags: dict[str, str],
|
|
109
|
+
aws_api_logs: AWSApiLogs,
|
|
125
110
|
) -> None:
|
|
126
|
-
current_retention_in_days =
|
|
127
|
-
log_group_name =
|
|
128
|
-
log_group_arn =
|
|
111
|
+
current_retention_in_days = log_group.get("retentionInDays")
|
|
112
|
+
log_group_name = log_group["logGroupName"]
|
|
113
|
+
log_group_arn = log_group["arn"]
|
|
129
114
|
|
|
130
115
|
desired_cleanup_option = _find_desired_cleanup_option(
|
|
131
116
|
log_group_name, desired_cleanup_options
|
|
@@ -133,54 +118,66 @@ def _reconcile_log_group(
|
|
|
133
118
|
|
|
134
119
|
if (
|
|
135
120
|
desired_cleanup_option.delete_empty_log_group
|
|
136
|
-
and is_empty(
|
|
121
|
+
and is_empty(log_group)
|
|
137
122
|
and is_longer_than_retention(
|
|
138
|
-
|
|
123
|
+
log_group, desired_cleanup_option.retention_in_days
|
|
139
124
|
)
|
|
140
125
|
):
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
!= TagStatus.MANAGED_BY_OTHER_INTEGRATION
|
|
144
|
-
):
|
|
126
|
+
tags = aws_api_logs.get_tags(log_group_arn)
|
|
127
|
+
if not _is_managed_by_other_integration(tags):
|
|
145
128
|
logging.info(
|
|
146
129
|
"Deleting empty log group %s",
|
|
147
130
|
log_group_arn,
|
|
148
131
|
)
|
|
149
132
|
if not dry_run:
|
|
150
|
-
|
|
133
|
+
aws_api_logs.delete_log_group(log_group_name)
|
|
151
134
|
return
|
|
152
135
|
|
|
153
|
-
if
|
|
136
|
+
if (
|
|
137
|
+
current_retention_in_days == desired_cleanup_option.retention_in_days
|
|
138
|
+
and last_tags == desired_tags
|
|
139
|
+
):
|
|
154
140
|
return
|
|
155
141
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
142
|
+
current_tags = aws_api_logs.get_tags(log_group_arn)
|
|
143
|
+
if _is_managed_by_other_integration(current_tags):
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
diff_result = diff_mappings(
|
|
147
|
+
current=current_tags,
|
|
148
|
+
desired=desired_tags,
|
|
149
|
+
)
|
|
150
|
+
if to_delete := diff_result.delete.keys() & last_tags.keys():
|
|
151
|
+
logging.info(
|
|
152
|
+
"Deleting tags %s for log group %s",
|
|
153
|
+
to_delete,
|
|
154
|
+
log_group_arn,
|
|
155
|
+
)
|
|
156
|
+
if not dry_run:
|
|
157
|
+
aws_api_logs.delete_tags(
|
|
165
158
|
log_group_arn,
|
|
159
|
+
to_delete,
|
|
166
160
|
)
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
161
|
+
if diff_result.add or diff_result.change:
|
|
162
|
+
logging.info(
|
|
163
|
+
"Setting tags %s for log group %s",
|
|
164
|
+
desired_tags,
|
|
165
|
+
log_group_arn,
|
|
166
|
+
)
|
|
167
|
+
if not dry_run:
|
|
168
|
+
aws_api_logs.set_tags(log_group_arn, desired_tags)
|
|
171
169
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
)
|
|
177
|
-
if not dry_run:
|
|
178
|
-
awsapi.set_cloudwatch_log_retention(
|
|
179
|
-
account_name,
|
|
180
|
-
log_group_name,
|
|
170
|
+
if current_retention_in_days != desired_cleanup_option.retention_in_days:
|
|
171
|
+
logging.info(
|
|
172
|
+
"Setting %s retention days to %d",
|
|
173
|
+
log_group_arn,
|
|
181
174
|
desired_cleanup_option.retention_in_days,
|
|
182
|
-
region,
|
|
183
175
|
)
|
|
176
|
+
if not dry_run:
|
|
177
|
+
aws_api_logs.put_retention_policy(
|
|
178
|
+
log_group_name,
|
|
179
|
+
desired_cleanup_option.retention_in_days,
|
|
180
|
+
)
|
|
184
181
|
|
|
185
182
|
|
|
186
183
|
def _find_desired_cleanup_option(
|
|
@@ -191,60 +188,63 @@ def _find_desired_cleanup_option(
|
|
|
191
188
|
Find the first cleanup option that regex matches the log group name.
|
|
192
189
|
If no match is found, return the default cleanup option.
|
|
193
190
|
|
|
194
|
-
:
|
|
195
|
-
|
|
196
|
-
|
|
191
|
+
Args:
|
|
192
|
+
log_group_name: The name of the log group.
|
|
193
|
+
desired_cleanup_options: A list of desired cleanup options.
|
|
194
|
+
Returns:
|
|
195
|
+
The matching cleanup option or the default one.
|
|
197
196
|
"""
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
197
|
+
for option in desired_cleanup_options:
|
|
198
|
+
if option.regex.match(log_group_name):
|
|
199
|
+
return option
|
|
200
|
+
return DEFAULT_AWS_CLOUDWATCH_CLEANUP_OPTION
|
|
202
201
|
|
|
203
202
|
|
|
204
203
|
def _reconcile_log_groups(
|
|
205
204
|
dry_run: bool,
|
|
206
205
|
aws_account: AWSAccountV1,
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
206
|
+
last_tags: dict[str, str],
|
|
207
|
+
default_tags: dict[str, str],
|
|
208
|
+
automation_token: dict[str, str],
|
|
209
|
+
) -> dict[str, str]:
|
|
210
|
+
desired_tags = (
|
|
211
|
+
default_tags | get_aws_account_tags(aws_account.organization) | MANAGED_TAG
|
|
212
212
|
)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
213
|
+
for (
|
|
214
|
+
region,
|
|
215
|
+
desired_cleanup_options,
|
|
216
|
+
) in get_desired_cleanup_options_by_region(aws_account).items():
|
|
217
|
+
aws_credentials = AWSStaticCredentials(
|
|
218
|
+
access_key_id=automation_token["aws_access_key_id"],
|
|
219
|
+
secret_access_key=automation_token["aws_secret_access_key"],
|
|
220
|
+
region=region,
|
|
221
|
+
)
|
|
222
|
+
with AWSApi(aws_credentials) as aws_api:
|
|
223
|
+
aws_api_logs = aws_api.logs
|
|
224
|
+
try:
|
|
225
|
+
for log_group in aws_api_logs.get_log_groups():
|
|
226
|
+
_reconcile_log_group(
|
|
227
|
+
dry_run=dry_run,
|
|
228
|
+
log_group=log_group,
|
|
229
|
+
desired_cleanup_options=desired_cleanup_options,
|
|
230
|
+
desired_tags=desired_tags,
|
|
231
|
+
last_tags=last_tags,
|
|
232
|
+
aws_api_logs=aws_api_logs,
|
|
233
|
+
)
|
|
234
|
+
except aws_api_logs.client.exceptions.ClientError as e:
|
|
235
|
+
logging.error(
|
|
236
|
+
"Error reconciling log groups for %s: %s",
|
|
237
|
+
aws_account.name,
|
|
238
|
+
e,
|
|
229
239
|
)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
logging.info(
|
|
233
|
-
"Access denied for aws account %s. Skipping...",
|
|
234
|
-
account_name,
|
|
235
|
-
)
|
|
236
|
-
else:
|
|
237
|
-
logging.error(
|
|
238
|
-
"Error reconciling log groups for %s: %s",
|
|
239
|
-
account_name,
|
|
240
|
-
e,
|
|
241
|
-
)
|
|
240
|
+
return last_tags
|
|
241
|
+
return desired_tags
|
|
242
242
|
|
|
243
243
|
|
|
244
|
-
def get_active_aws_accounts() -> list[AWSAccountV1]:
|
|
244
|
+
def get_active_aws_accounts(gql_api: GqlApi) -> list[AWSAccountV1]:
|
|
245
245
|
return [
|
|
246
246
|
account
|
|
247
|
-
for account in get_aws_accounts(
|
|
247
|
+
for account in get_aws_accounts(gql_api)
|
|
248
248
|
if not (
|
|
249
249
|
account.disable
|
|
250
250
|
and account.disable.integrations
|
|
@@ -253,8 +253,37 @@ def get_active_aws_accounts() -> list[AWSAccountV1]:
|
|
|
253
253
|
]
|
|
254
254
|
|
|
255
255
|
|
|
256
|
-
def
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
256
|
+
def get_default_tags(gql_api: GqlApi) -> dict[str, str]:
|
|
257
|
+
try:
|
|
258
|
+
return get_settings(gql_api.query).default_tags
|
|
259
|
+
except ValueError:
|
|
260
|
+
# no settings found
|
|
261
|
+
return {}
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def run(dry_run: bool) -> None:
|
|
265
|
+
gql_api = gql.get_api()
|
|
266
|
+
aws_accounts = get_active_aws_accounts(gql_api)
|
|
267
|
+
vault_settings = get_app_interface_vault_settings(query_func=gql_api.query)
|
|
268
|
+
secret_reader = create_secret_reader(use_vault=vault_settings.vault)
|
|
269
|
+
default_tags = get_default_tags(gql_api)
|
|
270
|
+
|
|
271
|
+
with init_state(
|
|
272
|
+
integration=QONTRACT_INTEGRATION,
|
|
273
|
+
secret_reader=secret_reader,
|
|
274
|
+
) as state:
|
|
275
|
+
last_tags = state.get(TAGS_KEY, {})
|
|
276
|
+
desired_tags = {
|
|
277
|
+
aws_account.name: _reconcile_log_groups(
|
|
278
|
+
dry_run=dry_run,
|
|
279
|
+
aws_account=aws_account,
|
|
280
|
+
last_tags=last_tags.get(aws_account.name, {}),
|
|
281
|
+
default_tags=default_tags,
|
|
282
|
+
automation_token=secret_reader.read_all_secret(
|
|
283
|
+
aws_account.automation_token
|
|
284
|
+
),
|
|
285
|
+
)
|
|
286
|
+
for aws_account in aws_accounts
|
|
287
|
+
}
|
|
288
|
+
if not dry_run and desired_tags != last_tags:
|
|
289
|
+
state.add(TAGS_KEY, desired_tags, force=True)
|
reconcile/aws_iam_keys.py
CHANGED
|
@@ -19,6 +19,7 @@ from reconcile.gql_definitions.aws_saml_idp.aws_accounts import (
|
|
|
19
19
|
query as aws_accounts_query,
|
|
20
20
|
)
|
|
21
21
|
from reconcile.status import ExitCodes
|
|
22
|
+
from reconcile.typed_queries.external_resources import get_settings
|
|
22
23
|
from reconcile.utils import gql
|
|
23
24
|
from reconcile.utils.aws_api import AWSApi
|
|
24
25
|
from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
|
|
@@ -81,7 +82,7 @@ class AwsSamlIdpIntegration(QontractReconcileIntegration[AwsSamlIdpIntegrationPa
|
|
|
81
82
|
if not query_func:
|
|
82
83
|
query_func = gql.get_api().query
|
|
83
84
|
return {
|
|
84
|
-
"accounts": [c.
|
|
85
|
+
"accounts": [c.model_dump() for c in self.get_aws_accounts(query_func)],
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
def get_aws_accounts(
|
|
@@ -124,20 +125,27 @@ class AwsSamlIdpIntegration(QontractReconcileIntegration[AwsSamlIdpIntegrationPa
|
|
|
124
125
|
aws_accounts = self.get_aws_accounts(
|
|
125
126
|
gql_api.query, account_name=self.params.account_name
|
|
126
127
|
)
|
|
127
|
-
aws_accounts_dict = [
|
|
128
|
-
|
|
128
|
+
aws_accounts_dict = [
|
|
129
|
+
account.model_dump(by_alias=True) for account in aws_accounts
|
|
130
|
+
]
|
|
131
|
+
try:
|
|
132
|
+
default_tags = get_settings().default_tags
|
|
133
|
+
except ValueError:
|
|
134
|
+
# no external resources settings found
|
|
135
|
+
default_tags = None
|
|
129
136
|
ts = TerrascriptClient(
|
|
130
137
|
self.name.replace("-", "_"),
|
|
131
138
|
"",
|
|
132
139
|
self.params.thread_pool_size,
|
|
133
140
|
aws_accounts_dict,
|
|
134
141
|
secret_reader=self.secret_reader,
|
|
142
|
+
default_tags=default_tags,
|
|
135
143
|
)
|
|
136
144
|
|
|
137
145
|
for saml_idp_config in self.build_saml_idp_config(
|
|
138
146
|
aws_accounts,
|
|
139
147
|
saml_idp_name=self.params.saml_idp_name,
|
|
140
|
-
saml_metadata=self.get_saml_metadata(self.params.saml_metadata_url),
|
|
148
|
+
saml_metadata=self.get_saml_metadata(str(self.params.saml_metadata_url)),
|
|
141
149
|
):
|
|
142
150
|
ts.populate_saml_idp(
|
|
143
151
|
account_name=saml_idp_config.account_name,
|
|
@@ -6,10 +6,11 @@ from collections.abc import (
|
|
|
6
6
|
)
|
|
7
7
|
from typing import (
|
|
8
8
|
Any,
|
|
9
|
+
Self,
|
|
9
10
|
TypedDict,
|
|
10
11
|
)
|
|
11
12
|
|
|
12
|
-
from pydantic import BaseModel,
|
|
13
|
+
from pydantic import BaseModel, field_validator, model_validator
|
|
13
14
|
|
|
14
15
|
from reconcile.gql_definitions.aws_saml_roles.aws_accounts import (
|
|
15
16
|
AWSAccountV1,
|
|
@@ -21,6 +22,7 @@ from reconcile.gql_definitions.aws_saml_roles.roles import (
|
|
|
21
22
|
query as roles_query,
|
|
22
23
|
)
|
|
23
24
|
from reconcile.status import ExitCodes
|
|
25
|
+
from reconcile.typed_queries.external_resources import get_settings
|
|
24
26
|
from reconcile.utils import gql
|
|
25
27
|
from reconcile.utils.aws_api import AWSApi
|
|
26
28
|
from reconcile.utils.aws_helper import unique_sso_aws_accounts
|
|
@@ -58,7 +60,8 @@ class AwsSamlRolesIntegrationParams(PydanticRunParams):
|
|
|
58
60
|
extended_early_exit_cache_ttl_seconds: int = 3600
|
|
59
61
|
log_cached_log_output: bool = False
|
|
60
62
|
|
|
61
|
-
@
|
|
63
|
+
@field_validator("max_session_duration_hours")
|
|
64
|
+
@classmethod
|
|
62
65
|
def max_session_duration_range(cls, v: str | int) -> int:
|
|
63
66
|
if 1 <= int(v) <= 12:
|
|
64
67
|
return int(v)
|
|
@@ -69,7 +72,8 @@ class CustomPolicy(BaseModel):
|
|
|
69
72
|
name: str
|
|
70
73
|
policy: dict[str, Any]
|
|
71
74
|
|
|
72
|
-
@
|
|
75
|
+
@field_validator("name")
|
|
76
|
+
@classmethod
|
|
73
77
|
def name_size(cls, v: str) -> str:
|
|
74
78
|
"""Check the policy name size.
|
|
75
79
|
|
|
@@ -81,7 +85,8 @@ class CustomPolicy(BaseModel):
|
|
|
81
85
|
)
|
|
82
86
|
return v
|
|
83
87
|
|
|
84
|
-
@
|
|
88
|
+
@field_validator("policy")
|
|
89
|
+
@classmethod
|
|
85
90
|
def policy_size(cls, v: dict[str, Any]) -> dict[str, Any]:
|
|
86
91
|
"""Check the policy size.
|
|
87
92
|
|
|
@@ -104,7 +109,8 @@ class AwsRole(BaseModel):
|
|
|
104
109
|
custom_policies: list[CustomPolicy]
|
|
105
110
|
managed_policies: list[ManagedPolicy]
|
|
106
111
|
|
|
107
|
-
@
|
|
112
|
+
@field_validator("name")
|
|
113
|
+
@classmethod
|
|
108
114
|
def name_size(cls, v: str) -> str:
|
|
109
115
|
"""Check the role name size.
|
|
110
116
|
|
|
@@ -116,29 +122,23 @@ class AwsRole(BaseModel):
|
|
|
116
122
|
)
|
|
117
123
|
return v
|
|
118
124
|
|
|
119
|
-
@
|
|
120
|
-
def validate_policies(
|
|
125
|
+
@model_validator(mode="after")
|
|
126
|
+
def validate_policies(self) -> Self:
|
|
121
127
|
"""Check the policies.
|
|
122
128
|
|
|
123
129
|
See https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html
|
|
124
130
|
"""
|
|
125
|
-
custom_policies
|
|
126
|
-
managed_policies = values.get("managed_policies", [])
|
|
127
|
-
if len(custom_policies) + len(managed_policies) > 20:
|
|
131
|
+
if len(self.custom_policies) + len(self.managed_policies) > 20:
|
|
128
132
|
raise ValueError(
|
|
129
|
-
f"The role '{
|
|
133
|
+
f"The role '{self.name}' has too many policies. AWS roles can have at most 20 policies (via quota increase). Please consider consolidating the policies."
|
|
130
134
|
)
|
|
131
|
-
cp_names = [cp.name for cp in custom_policies]
|
|
135
|
+
cp_names = [cp.name for cp in self.custom_policies]
|
|
132
136
|
if len(set(cp_names)) != len(cp_names):
|
|
133
|
-
raise ValueError(
|
|
134
|
-
|
|
135
|
-
)
|
|
136
|
-
mp_names = [mp.name for mp in managed_policies]
|
|
137
|
+
raise ValueError(f"The role '{self.name}' has duplicate custom policies.")
|
|
138
|
+
mp_names = [mp.name for mp in self.managed_policies]
|
|
137
139
|
if len(set(mp_names)) != len(mp_names):
|
|
138
|
-
raise ValueError(
|
|
139
|
-
|
|
140
|
-
)
|
|
141
|
-
return values
|
|
140
|
+
raise ValueError(f"The role '{self.name}' has duplicate managed policies.")
|
|
141
|
+
return self
|
|
142
142
|
|
|
143
143
|
|
|
144
144
|
class RunnerParams(TypedDict):
|
|
@@ -163,7 +163,7 @@ class AwsSamlRolesIntegration(
|
|
|
163
163
|
if not query_func:
|
|
164
164
|
query_func = gql.get_api().query
|
|
165
165
|
return {
|
|
166
|
-
"roles": [c.
|
|
166
|
+
"roles": [c.model_dump() for c in self.get_roles(query_func)],
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
def get_aws_accounts(
|
|
@@ -251,15 +251,22 @@ class AwsSamlRolesIntegration(
|
|
|
251
251
|
aws_accounts = self.get_aws_accounts(
|
|
252
252
|
gql_api.query, account_name=self.params.account_name
|
|
253
253
|
)
|
|
254
|
-
aws_accounts_dict = [
|
|
254
|
+
aws_accounts_dict = [
|
|
255
|
+
account.model_dump(by_alias=True) for account in aws_accounts
|
|
256
|
+
]
|
|
255
257
|
aws_roles = self.get_roles(gql_api.query, account_name=self.params.account_name)
|
|
256
|
-
|
|
258
|
+
try:
|
|
259
|
+
default_tags = get_settings().default_tags
|
|
260
|
+
except ValueError:
|
|
261
|
+
# no external resources settings found
|
|
262
|
+
default_tags = None
|
|
257
263
|
ts = TerrascriptClient(
|
|
258
264
|
self.name.replace("-", "_"),
|
|
259
265
|
"",
|
|
260
266
|
self.params.thread_pool_size,
|
|
261
267
|
aws_accounts_dict,
|
|
262
268
|
secret_reader=self.secret_reader,
|
|
269
|
+
default_tags=default_tags,
|
|
263
270
|
)
|
|
264
271
|
self.populate_saml_iam_roles(ts, aws_roles)
|
|
265
272
|
working_dirs = ts.dump(print_to_file=self.params.print_to_file)
|
|
@@ -7,12 +7,7 @@ from enum import StrEnum
|
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
9
|
import semver
|
|
10
|
-
from pydantic import
|
|
11
|
-
BaseModel,
|
|
12
|
-
ValidationError,
|
|
13
|
-
root_validator,
|
|
14
|
-
validator,
|
|
15
|
-
)
|
|
10
|
+
from pydantic import BaseModel, ValidationError, field_validator, model_validator
|
|
16
11
|
|
|
17
12
|
from reconcile.aws_version_sync.merge_request_manager.merge_request import (
|
|
18
13
|
Renderer,
|
|
@@ -81,7 +76,7 @@ class SupportedResourceProvider(StrEnum):
|
|
|
81
76
|
ELASTICACHE = "elasticache"
|
|
82
77
|
|
|
83
78
|
|
|
84
|
-
class ExternalResource(BaseModel):
|
|
79
|
+
class ExternalResource(BaseModel, arbitrary_types_allowed=True):
|
|
85
80
|
namespace_file: str | None = None
|
|
86
81
|
provider: str = "aws"
|
|
87
82
|
provisioner: ExternalResourceProvisioner
|
|
@@ -94,9 +89,6 @@ class ExternalResource(BaseModel):
|
|
|
94
89
|
# used to map AWS cache name to resource_identifier
|
|
95
90
|
redis_replication_group_id: str | None = None
|
|
96
91
|
|
|
97
|
-
class Config:
|
|
98
|
-
arbitrary_types_allowed = True
|
|
99
|
-
|
|
100
92
|
@property
|
|
101
93
|
def key(self) -> tuple:
|
|
102
94
|
return (
|
|
@@ -106,7 +98,8 @@ class ExternalResource(BaseModel):
|
|
|
106
98
|
self.resource_identifier,
|
|
107
99
|
)
|
|
108
100
|
|
|
109
|
-
@
|
|
101
|
+
@field_validator("resource_engine_version", mode="before")
|
|
102
|
+
@classmethod
|
|
110
103
|
def parse_resource_engine_version(
|
|
111
104
|
cls, v: str | semver.VersionInfo
|
|
112
105
|
) -> semver.VersionInfo:
|
|
@@ -114,7 +107,8 @@ class ExternalResource(BaseModel):
|
|
|
114
107
|
return v
|
|
115
108
|
return parse_semver(str(v), optional_minor_and_patch=True)
|
|
116
109
|
|
|
117
|
-
@
|
|
110
|
+
@model_validator(mode="before")
|
|
111
|
+
@classmethod
|
|
118
112
|
def set_resource_engine_version_format(cls, values: dict) -> dict:
|
|
119
113
|
resource_engine_version, resource_engine_version_format = (
|
|
120
114
|
str(values.get("resource_engine_version")),
|
|
@@ -62,8 +62,8 @@ class QontractServerDatafileDiff(BaseModel):
|
|
|
62
62
|
|
|
63
63
|
datafilepath: str
|
|
64
64
|
datafileschema: str
|
|
65
|
-
old: dict[str, Any] | None
|
|
66
|
-
new: dict[str, Any] | None
|
|
65
|
+
old: dict[str, Any] | None = None
|
|
66
|
+
new: dict[str, Any] | None = None
|
|
67
67
|
|
|
68
68
|
@property
|
|
69
69
|
def old_datafilepath(self) -> str | None:
|
|
@@ -119,7 +119,7 @@ class QontractServerResourcefileDiffState(BaseModel):
|
|
|
119
119
|
content: str
|
|
120
120
|
resourcefileschema: str | None = Field(..., alias="$schema")
|
|
121
121
|
sha256sum: str
|
|
122
|
-
backrefs: list[QontractServerResourcefileBackref] | None
|
|
122
|
+
backrefs: list[QontractServerResourcefileBackref] | None = None
|
|
123
123
|
|
|
124
124
|
|
|
125
125
|
class QontractServerResourcefileDiff(BaseModel):
|
|
@@ -155,7 +155,8 @@ class ChangeLogIntegration(QontractReconcileIntegration[ChangeLogIntegrationPara
|
|
|
155
155
|
changes = aggregate_resource_changes(
|
|
156
156
|
bundle_changes=aggregate_file_moves(parse_bundle_changes(diff)),
|
|
157
157
|
content_store={
|
|
158
|
-
c.path: c.
|
|
158
|
+
c.path: c.model_dump(by_alias=True)
|
|
159
|
+
for c in namespaces + jenkins_configs
|
|
159
160
|
},
|
|
160
161
|
supported_schemas={
|
|
161
162
|
"/openshift/namespace-1.yml",
|
|
@@ -239,4 +240,4 @@ class ChangeLogIntegration(QontractReconcileIntegration[ChangeLogIntegrationPara
|
|
|
239
240
|
change_log.items, key=lambda i: i.merged_at, reverse=True
|
|
240
241
|
)
|
|
241
242
|
if not dry_run:
|
|
242
|
-
integration_state.add(BUNDLE_DIFFS_OBJ, change_log.
|
|
243
|
+
integration_state.add(BUNDLE_DIFFS_OBJ, change_log.model_dump(), force=True)
|
|
@@ -140,7 +140,7 @@ def write_coverage_report_to_mr(
|
|
|
140
140
|
approver_reachability = set()
|
|
141
141
|
for d in change_decisions:
|
|
142
142
|
approvers = [
|
|
143
|
-
f"{cr.context} - {' '.join([f'@{a.org_username}' if a.tag_on_merge_requests else a.org_username for a in cr.approvers])}"
|
|
143
|
+
f"{cr.context} - {' '.join([f'@{a.org_username}' if (a.tag_on_merge_requests or len(cr.approvers) == 1) else a.org_username for a in cr.approvers])}"
|
|
144
144
|
for cr in d.change_responsibles
|
|
145
145
|
]
|
|
146
146
|
if d.coverable_by_fragment_decisions:
|