qontract-reconcile 0.10.2.dev394__py3-none-any.whl → 0.10.2.dev414__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/METADATA +4 -3
- {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/RECORD +308 -308
- reconcile/acs_rbac.py +2 -2
- reconcile/aus/advanced_upgrade_service.py +15 -12
- reconcile/aus/base.py +9 -13
- reconcile/aus/cluster_version_data.py +15 -5
- reconcile/aus/models.py +1 -1
- reconcile/automated_actions/config/integration.py +15 -3
- reconcile/aws_account_manager/integration.py +6 -6
- reconcile/aws_account_manager/reconciler.py +3 -3
- reconcile/aws_ami_cleanup/integration.py +2 -5
- reconcile/aws_ami_share.py +69 -62
- reconcile/aws_saml_idp/integration.py +5 -3
- reconcile/aws_saml_roles/integration.py +23 -22
- reconcile/aws_version_sync/integration.py +6 -12
- reconcile/change_owners/bundle.py +3 -3
- reconcile/change_owners/change_log_tracking.py +3 -2
- reconcile/change_owners/change_owners.py +1 -1
- reconcile/dashdotdb_dora.py +1 -1
- reconcile/dashdotdb_slo.py +1 -1
- reconcile/database_access_manager.py +8 -9
- reconcile/dynatrace_token_provider/integration.py +1 -1
- reconcile/endpoints_discovery/integration.py +4 -1
- reconcile/endpoints_discovery/merge_request.py +1 -1
- reconcile/endpoints_discovery/merge_request_manager.py +1 -1
- reconcile/external_resources/integration.py +1 -1
- reconcile/external_resources/manager.py +19 -7
- reconcile/external_resources/metrics.py +1 -1
- reconcile/external_resources/model.py +6 -6
- reconcile/external_resources/reconciler.py +7 -4
- reconcile/external_resources/secrets_sync.py +2 -2
- reconcile/external_resources/state.py +56 -14
- reconcile/fleet_labeler/integration.py +1 -1
- reconcile/gcp_image_mirror.py +2 -2
- reconcile/github_org.py +1 -1
- reconcile/github_owners.py +4 -0
- reconcile/gitlab_members.py +6 -12
- reconcile/gitlab_permissions.py +8 -12
- reconcile/glitchtip_project_alerts/integration.py +3 -1
- reconcile/gql_definitions/acs/acs_instances.py +5 -5
- reconcile/gql_definitions/acs/acs_policies.py +5 -5
- reconcile/gql_definitions/acs/acs_rbac.py +5 -5
- reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +5 -5
- reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +5 -5
- reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +5 -5
- reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
- reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
- reconcile/gql_definitions/automated_actions/instance.py +46 -7
- reconcile/gql_definitions/aws_account_manager/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
- reconcile/gql_definitions/aws_version_sync/clusters.py +5 -5
- reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
- reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
- reconcile/gql_definitions/change_owners/queries/self_service_roles.py +5 -5
- reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +5 -5
- reconcile/gql_definitions/common/alerting_services_settings.py +5 -5
- reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
- reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
- reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_roles.py +5 -5
- reconcile/gql_definitions/common/app_interface_state_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
- reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
- reconcile/gql_definitions/common/apps.py +5 -5
- reconcile/gql_definitions/common/aws_vpc_requests.py +5 -5
- reconcile/gql_definitions/common/aws_vpcs.py +5 -5
- reconcile/gql_definitions/common/clusters.py +5 -5
- reconcile/gql_definitions/common/clusters_minimal.py +5 -5
- reconcile/gql_definitions/common/clusters_with_dms.py +5 -5
- reconcile/gql_definitions/common/clusters_with_peering.py +5 -5
- reconcile/gql_definitions/common/github_orgs.py +5 -5
- reconcile/gql_definitions/common/jira_settings.py +5 -5
- reconcile/gql_definitions/common/jiralert_settings.py +5 -5
- reconcile/gql_definitions/common/ldap_settings.py +5 -5
- reconcile/gql_definitions/common/namespaces.py +5 -5
- reconcile/gql_definitions/common/namespaces_minimal.py +7 -5
- reconcile/gql_definitions/common/ocm_env_telemeter.py +5 -5
- reconcile/gql_definitions/common/ocm_environments.py +5 -5
- reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
- reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -5
- reconcile/gql_definitions/common/pipeline_providers.py +5 -5
- reconcile/gql_definitions/common/quay_instances.py +5 -5
- reconcile/gql_definitions/common/quay_orgs.py +5 -5
- reconcile/gql_definitions/common/reserved_networks.py +5 -5
- reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
- reconcile/gql_definitions/common/saas_files.py +5 -5
- reconcile/gql_definitions/common/saas_target_namespaces.py +5 -5
- reconcile/gql_definitions/common/saasherder_settings.py +5 -5
- reconcile/gql_definitions/common/slack_workspaces.py +5 -5
- reconcile/gql_definitions/common/smtp_client_settings.py +5 -5
- reconcile/gql_definitions/common/state_aws_account.py +5 -5
- reconcile/gql_definitions/common/users.py +5 -5
- reconcile/gql_definitions/common/users_with_paths.py +5 -5
- reconcile/gql_definitions/cost_report/app_names.py +5 -5
- reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
- reconcile/gql_definitions/cost_report/settings.py +5 -5
- reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +5 -5
- reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +5 -5
- reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
- reconcile/gql_definitions/email_sender/apps.py +5 -5
- reconcile/gql_definitions/email_sender/emails.py +5 -5
- reconcile/gql_definitions/email_sender/users.py +5 -5
- reconcile/gql_definitions/endpoints_discovery/apps.py +5 -5
- reconcile/gql_definitions/external_resources/aws_accounts.py +5 -5
- reconcile/gql_definitions/external_resources/external_resources_modules.py +5 -5
- reconcile/gql_definitions/external_resources/external_resources_namespaces.py +5 -5
- reconcile/gql_definitions/external_resources/external_resources_settings.py +5 -5
- reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
- reconcile/gql_definitions/fleet_labeler/fleet_labels.py +5 -5
- reconcile/gql_definitions/fragments/aus_organization.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_common.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
- reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
- reconcile/gql_definitions/fragments/aws_organization.py +5 -5
- reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
- reconcile/gql_definitions/fragments/aws_vpc_request.py +5 -5
- reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
- reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
- reconcile/gql_definitions/fragments/disable.py +5 -5
- reconcile/gql_definitions/fragments/email_service.py +5 -5
- reconcile/gql_definitions/fragments/email_user.py +5 -5
- reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
- reconcile/gql_definitions/fragments/membership_source.py +5 -5
- reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
- reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
- reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
- reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
- reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
- reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
- reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
- reconcile/gql_definitions/fragments/resource_values.py +5 -5
- reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
- reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
- reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
- reconcile/gql_definitions/fragments/terraform_state.py +5 -5
- reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
- reconcile/gql_definitions/fragments/user.py +5 -5
- reconcile/gql_definitions/fragments/vault_secret.py +5 -5
- reconcile/gql_definitions/gcp/gcp_docker_repos.py +5 -5
- reconcile/gql_definitions/gcp/gcp_projects.py +5 -5
- reconcile/gql_definitions/gitlab_members/gitlab_instances.py +5 -5
- reconcile/gql_definitions/gitlab_members/permissions.py +5 -5
- reconcile/gql_definitions/glitchtip/glitchtip_instance.py +5 -5
- reconcile/gql_definitions/glitchtip/glitchtip_project.py +5 -5
- reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +5 -5
- reconcile/gql_definitions/integrations/integrations.py +5 -5
- reconcile/gql_definitions/introspection.json +231 -0
- reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +5 -5
- reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +5 -5
- reconcile/gql_definitions/jira/jira_servers.py +5 -5
- reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +5 -5
- reconcile/gql_definitions/jumphosts/jumphosts.py +5 -5
- reconcile/gql_definitions/ldap_groups/roles.py +5 -5
- reconcile/gql_definitions/ldap_groups/settings.py +5 -5
- reconcile/gql_definitions/maintenance/maintenances.py +5 -5
- reconcile/gql_definitions/membershipsources/roles.py +5 -5
- reconcile/gql_definitions/ocm_labels/clusters.py +5 -5
- reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
- reconcile/gql_definitions/openshift_cluster_bots/clusters.py +5 -5
- reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
- reconcile/gql_definitions/openshift_groups/managed_roles.py +5 -5
- reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +5 -5
- reconcile/gql_definitions/quay_membership/quay_membership.py +5 -5
- reconcile/gql_definitions/rhcs/certs.py +5 -5
- reconcile/gql_definitions/rhidp/organizations.py +5 -5
- reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
- reconcile/gql_definitions/service_dependencies/service_dependencies.py +5 -5
- reconcile/gql_definitions/sharding/aws_accounts.py +5 -5
- reconcile/gql_definitions/sharding/ocm_organization.py +5 -5
- reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
- reconcile/gql_definitions/skupper_network/skupper_networks.py +5 -5
- reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
- reconcile/gql_definitions/slack_usergroups/permissions.py +5 -5
- reconcile/gql_definitions/slack_usergroups/users.py +5 -5
- reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
- reconcile/gql_definitions/status_board/status_board.py +5 -5
- reconcile/gql_definitions/statuspage/statuspages.py +5 -5
- reconcile/gql_definitions/templating/template_collection.py +5 -5
- reconcile/gql_definitions/templating/templates.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +5 -5
- reconcile/gql_definitions/terraform_init/aws_accounts.py +5 -5
- reconcile/gql_definitions/terraform_repo/terraform_repo.py +5 -5
- reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
- reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +5 -5
- reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +5 -5
- reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +5 -5
- reconcile/gql_definitions/vault_instances/vault_instances.py +5 -5
- reconcile/gql_definitions/vault_policies/vault_policies.py +5 -5
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +5 -5
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
- reconcile/integrations_manager.py +3 -3
- reconcile/jenkins_worker_fleets.py +9 -8
- reconcile/jira_permissions_validator.py +2 -2
- reconcile/ldap_groups/integration.py +1 -1
- reconcile/ocm/types.py +35 -57
- reconcile/ocm_aws_infrastructure_access.py +1 -1
- reconcile/ocm_clusters.py +4 -4
- reconcile/ocm_labels/integration.py +3 -2
- reconcile/ocm_machine_pools.py +23 -23
- reconcile/openshift_base.py +53 -2
- reconcile/openshift_cluster_bots.py +1 -1
- reconcile/openshift_namespace_labels.py +1 -1
- reconcile/openshift_namespaces.py +97 -101
- reconcile/openshift_resources_base.py +6 -2
- reconcile/openshift_rhcs_certs.py +5 -5
- reconcile/openshift_rolebindings.py +7 -11
- reconcile/openshift_saas_deploy.py +4 -5
- reconcile/openshift_saas_deploy_change_tester.py +9 -7
- reconcile/openshift_serviceaccount_tokens.py +2 -2
- reconcile/openshift_upgrade_watcher.py +1 -1
- reconcile/oum/labelset.py +5 -3
- reconcile/oum/models.py +1 -4
- reconcile/prometheus_rules_tester/integration.py +3 -3
- reconcile/quay_mirror.py +1 -1
- reconcile/queries.py +6 -0
- reconcile/rhidp/common.py +3 -5
- reconcile/rhidp/sso_client/base.py +1 -1
- reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
- reconcile/skupper_network/integration.py +2 -2
- reconcile/slack_usergroups.py +31 -11
- reconcile/status_board.py +6 -6
- reconcile/statuspage/atlassian.py +7 -7
- reconcile/statuspage/page.py +4 -9
- reconcile/templating/lib/rendering.py +3 -3
- reconcile/templating/renderer.py +2 -2
- reconcile/terraform_cloudflare_dns.py +3 -3
- reconcile/terraform_cloudflare_resources.py +5 -5
- reconcile/terraform_cloudflare_users.py +3 -2
- reconcile/terraform_init/integration.py +2 -2
- reconcile/terraform_repo.py +16 -12
- reconcile/terraform_resources.py +6 -6
- reconcile/terraform_tgw_attachments.py +20 -18
- reconcile/terraform_vpc_resources/integration.py +3 -1
- reconcile/typed_queries/cost_report/app_names.py +1 -1
- reconcile/typed_queries/cost_report/cost_namespaces.py +2 -2
- reconcile/typed_queries/saas_files.py +11 -11
- reconcile/typed_queries/status_board.py +2 -2
- reconcile/unleash_feature_toggles/integration.py +4 -2
- reconcile/utils/acs/base.py +6 -3
- reconcile/utils/acs/policies.py +2 -2
- reconcile/utils/aws_api.py +51 -20
- reconcile/utils/aws_api_typed/organization.py +4 -2
- reconcile/utils/deadmanssnitch_api.py +1 -1
- reconcile/utils/early_exit_cache.py +8 -10
- reconcile/utils/gitlab_api.py +7 -5
- reconcile/utils/glitchtip/client.py +6 -2
- reconcile/utils/glitchtip/models.py +25 -28
- reconcile/utils/gql.py +4 -7
- reconcile/utils/instrumented_wrappers.py +1 -1
- reconcile/utils/internal_groups/client.py +2 -2
- reconcile/utils/internal_groups/models.py +8 -17
- reconcile/utils/jinja2/utils.py +2 -5
- reconcile/utils/jobcontroller/controller.py +1 -1
- reconcile/utils/jobcontroller/models.py +17 -1
- reconcile/utils/json.py +39 -1
- reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
- reconcile/utils/membershipsources/models.py +16 -23
- reconcile/utils/membershipsources/resolver.py +4 -2
- reconcile/utils/merge_request_manager/merge_request_manager.py +1 -1
- reconcile/utils/merge_request_manager/parser.py +4 -4
- reconcile/utils/metrics.py +5 -5
- reconcile/utils/models.py +304 -82
- reconcile/utils/mr/notificator.py +1 -1
- reconcile/utils/mr/user_maintenance.py +3 -2
- reconcile/utils/oc.py +112 -92
- reconcile/utils/ocm/addons.py +0 -1
- reconcile/utils/ocm/base.py +17 -20
- reconcile/utils/ocm/cluster_groups.py +1 -1
- reconcile/utils/ocm/identity_providers.py +2 -2
- reconcile/utils/ocm/labels.py +1 -1
- reconcile/utils/ocm/products.py +8 -8
- reconcile/utils/ocm/service_log.py +1 -1
- reconcile/utils/ocm/sre_capability_labels.py +20 -13
- reconcile/utils/openshift_resource.py +5 -0
- reconcile/utils/pagerduty_api.py +5 -2
- reconcile/utils/promotion_state.py +6 -11
- reconcile/utils/raw_github_api.py +1 -1
- reconcile/utils/rhcsv2_certs.py +1 -4
- reconcile/utils/runtime/integration.py +1 -1
- reconcile/utils/saasherder/interfaces.py +13 -20
- reconcile/utils/saasherder/models.py +23 -20
- reconcile/utils/saasherder/saasherder.py +26 -17
- reconcile/utils/slack_api.py +2 -2
- reconcile/utils/structs.py +1 -1
- reconcile/utils/terraform_client.py +1 -1
- reconcile/utils/terrascript_aws_client.py +47 -43
- reconcile/utils/unleash/server.py +2 -8
- reconcile/utils/vault.py +4 -11
- reconcile/utils/vcs.py +8 -8
- reconcile/vault_replication.py +1 -1
- tools/cli_commands/cost_report/cost_management_api.py +3 -3
- tools/cli_commands/cost_report/view.py +7 -6
- tools/cli_commands/erv2.py +3 -1
- tools/qontract_cli.py +6 -5
- tools/template_validation.py +3 -1
- {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/entry_points.txt +0 -0
reconcile/acs_rbac.py
CHANGED
|
@@ -65,7 +65,7 @@ class AcsRole(BaseModel):
|
|
|
65
65
|
assignments: list[AssignmentPair]
|
|
66
66
|
permission_set_name: str
|
|
67
67
|
access_scope: AcsAccessScope
|
|
68
|
-
system_default: bool | None
|
|
68
|
+
system_default: bool | None = None
|
|
69
69
|
|
|
70
70
|
@classmethod
|
|
71
71
|
def build(cls, permission: Permission, usernames: list[str]) -> Self:
|
|
@@ -151,7 +151,7 @@ class AcsRbacIntegration(QontractReconcileIntegration[NoParams]):
|
|
|
151
151
|
for permission in role.oidc_permissions or []:
|
|
152
152
|
if isinstance(permission, OidcPermissionAcsV1):
|
|
153
153
|
permission_usernames[
|
|
154
|
-
Permission(**permission.
|
|
154
|
+
Permission(**permission.model_dump(by_alias=True))
|
|
155
155
|
].append(user.org_username)
|
|
156
156
|
return list(starmap(AcsRole.build, permission_usernames.items()))
|
|
157
157
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from collections import defaultdict
|
|
3
3
|
from datetime import timedelta
|
|
4
|
-
from typing import Optional
|
|
4
|
+
from typing import Annotated, Optional
|
|
5
5
|
|
|
6
6
|
from pydantic import (
|
|
7
7
|
BaseModel,
|
|
8
8
|
Field,
|
|
9
9
|
ValidationError,
|
|
10
|
-
|
|
10
|
+
field_validator,
|
|
11
11
|
)
|
|
12
12
|
from pydantic.dataclasses import dataclass
|
|
13
13
|
|
|
@@ -289,13 +289,16 @@ class OrganizationLabelSet(BaseModel):
|
|
|
289
289
|
|
|
290
290
|
blocked_versions: CSV | None = Field(alias=aus_label_key("blocked-versions"))
|
|
291
291
|
|
|
292
|
-
sector_max_parallel_upgrades:
|
|
293
|
-
|
|
294
|
-
|
|
292
|
+
sector_max_parallel_upgrades: Annotated[
|
|
293
|
+
dict[str, str],
|
|
294
|
+
labelset_groupfield(
|
|
295
|
+
group_prefix=aus_label_key("sector-max-parallel-upgrades.")
|
|
296
|
+
),
|
|
297
|
+
]
|
|
295
298
|
|
|
296
|
-
sector_deps:
|
|
297
|
-
group_prefix=aus_label_key("sector-deps.")
|
|
298
|
-
|
|
299
|
+
sector_deps: Annotated[
|
|
300
|
+
dict[str, CSV], labelset_groupfield(group_prefix=aus_label_key("sector-deps."))
|
|
301
|
+
]
|
|
299
302
|
"""
|
|
300
303
|
Each sector with dependencies is represented as a `sector-deps.<sector-name>` label
|
|
301
304
|
with a CSV formatted list of dependant sectors. The custom `labelset_groupfield``
|
|
@@ -357,7 +360,7 @@ def _build_org_upgrade_spec(
|
|
|
357
360
|
]
|
|
358
361
|
|
|
359
362
|
org_labelset = build_labelset(org_labels, OrganizationLabelSet)
|
|
360
|
-
final_org = org.
|
|
363
|
+
final_org = org.model_copy(deep=True)
|
|
361
364
|
final_org.blocked_versions = org_labelset.blocked_versions # type: ignore
|
|
362
365
|
final_org.sectors = org_labelset.sector_dependencies()
|
|
363
366
|
final_org.inherit_version_data = inherit_version_data
|
|
@@ -411,7 +414,7 @@ class ClusterUpgradePolicyLabelSet(BaseModel):
|
|
|
411
414
|
"""
|
|
412
415
|
|
|
413
416
|
soak_days: int = Field(alias=aus_label_key("soak-days"), ge=0)
|
|
414
|
-
workloads: CSV = Field(alias=aus_label_key("workloads"),
|
|
417
|
+
workloads: CSV = Field(alias=aus_label_key("workloads"), min_length=1)
|
|
415
418
|
schedule: str = Field(alias=aus_label_key("schedule"))
|
|
416
419
|
mutexes: CSV | None = Field(alias=aus_label_key("mutexes"))
|
|
417
420
|
sector: str | None = Field(alias=aus_label_key("sector"))
|
|
@@ -419,14 +422,14 @@ class ClusterUpgradePolicyLabelSet(BaseModel):
|
|
|
419
422
|
version_gate_approvals: CSV | None = Field(
|
|
420
423
|
alias=aus_label_key("version-gate-approvals")
|
|
421
424
|
)
|
|
422
|
-
_schedule_validator =
|
|
425
|
+
_schedule_validator = field_validator("schedule")(cron_validator)
|
|
423
426
|
|
|
424
427
|
def build_labels_dict(self) -> dict[str, str]:
|
|
425
428
|
"""
|
|
426
429
|
Build a dictionary of all labels in this labelset.
|
|
427
430
|
"""
|
|
428
431
|
labels = {}
|
|
429
|
-
for k, v in self.
|
|
432
|
+
for k, v in self.model_dump(by_alias=True).items():
|
|
430
433
|
if v is None:
|
|
431
434
|
continue
|
|
432
435
|
if isinstance(v, list):
|
reconcile/aus/base.py
CHANGED
|
@@ -16,7 +16,7 @@ from typing import (
|
|
|
16
16
|
)
|
|
17
17
|
|
|
18
18
|
from croniter import croniter
|
|
19
|
-
from pydantic import BaseModel
|
|
19
|
+
from pydantic import BaseModel
|
|
20
20
|
from requests.exceptions import HTTPError
|
|
21
21
|
from semver import VersionInfo
|
|
22
22
|
|
|
@@ -404,12 +404,12 @@ class AbstractUpgradePolicy(ABC, BaseModel):
|
|
|
404
404
|
|
|
405
405
|
cluster: OCMCluster
|
|
406
406
|
|
|
407
|
-
id: str | None
|
|
408
|
-
next_run: str | None
|
|
409
|
-
schedule: str | None
|
|
407
|
+
id: str | None = None
|
|
408
|
+
next_run: str | None = None
|
|
409
|
+
schedule: str | None = None
|
|
410
410
|
schedule_type: str
|
|
411
411
|
version: str
|
|
412
|
-
state: str | None
|
|
412
|
+
state: str | None = None
|
|
413
413
|
|
|
414
414
|
@abstractmethod
|
|
415
415
|
def create(self, ocm_api: OCMBaseClient) -> None:
|
|
@@ -430,15 +430,12 @@ def addon_upgrade_policy_soonest_next_run() -> str:
|
|
|
430
430
|
return to_utc_seconds_iso_format(next_run)
|
|
431
431
|
|
|
432
432
|
|
|
433
|
-
class AddonUpgradePolicy(AbstractUpgradePolicy):
|
|
433
|
+
class AddonUpgradePolicy(AbstractUpgradePolicy, arbitrary_types_allowed=True):
|
|
434
434
|
"""Class to create and delete Addon upgrade policies in OCM"""
|
|
435
435
|
|
|
436
436
|
addon_id: str
|
|
437
437
|
addon_service: AddonService
|
|
438
438
|
|
|
439
|
-
class Config:
|
|
440
|
-
arbitrary_types_allowed = True
|
|
441
|
-
|
|
442
439
|
def create(self, ocm_api: OCMBaseClient) -> None:
|
|
443
440
|
self.addon_service.create_addon_upgrade_policy(
|
|
444
441
|
ocm_api=ocm_api,
|
|
@@ -521,9 +518,10 @@ class ControlPlaneUpgradePolicy(AbstractUpgradePolicy):
|
|
|
521
518
|
|
|
522
519
|
|
|
523
520
|
class NodePoolUpgradePolicy(AbstractUpgradePolicy):
|
|
524
|
-
node_pool: str
|
|
525
521
|
"""Class to create NodePoolUpgradePolicies in OCM"""
|
|
526
522
|
|
|
523
|
+
node_pool: str
|
|
524
|
+
|
|
527
525
|
def create(self, ocm_api: OCMBaseClient) -> None:
|
|
528
526
|
policy = {
|
|
529
527
|
"version": self.version,
|
|
@@ -550,7 +548,7 @@ class NodePoolUpgradePolicy(AbstractUpgradePolicy):
|
|
|
550
548
|
return f"node pool upgrade policy - {remove_none_values_from_dict(details)}"
|
|
551
549
|
|
|
552
550
|
|
|
553
|
-
class UpgradePolicyHandler(BaseModel, extra=
|
|
551
|
+
class UpgradePolicyHandler(BaseModel, extra="forbid"):
|
|
554
552
|
"""Class to handle upgrade policy actions"""
|
|
555
553
|
|
|
556
554
|
action: str
|
|
@@ -1124,12 +1122,10 @@ def calculate_diff(
|
|
|
1124
1122
|
UpgradePolicyHandler(
|
|
1125
1123
|
action="create",
|
|
1126
1124
|
policy=AddonUpgradePolicy(
|
|
1127
|
-
action="create",
|
|
1128
1125
|
cluster=spec.cluster,
|
|
1129
1126
|
version=version,
|
|
1130
1127
|
schedule_type="manual",
|
|
1131
1128
|
addon_id=addon_id,
|
|
1132
|
-
upgrade_type="ADDON",
|
|
1133
1129
|
addon_service=addon_service,
|
|
1134
1130
|
),
|
|
1135
1131
|
)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import json
|
|
2
1
|
from collections.abc import Iterable
|
|
3
2
|
from datetime import datetime
|
|
4
3
|
from typing import (
|
|
@@ -20,6 +19,17 @@ class WorkloadHistory(BaseModel):
|
|
|
20
19
|
soak_days: float = 0.0
|
|
21
20
|
reporting: list[str] = Field(default_factory=list)
|
|
22
21
|
|
|
22
|
+
def __eq__(self, value: Any) -> bool:
|
|
23
|
+
if isinstance(value, WorkloadHistory):
|
|
24
|
+
return super().__eq__(value)
|
|
25
|
+
|
|
26
|
+
if isinstance(value, dict):
|
|
27
|
+
return self.soak_days == value.get("soak_days", 0.0) and sorted(
|
|
28
|
+
self.reporting
|
|
29
|
+
) == sorted(value.get("reporting", []))
|
|
30
|
+
|
|
31
|
+
return False
|
|
32
|
+
|
|
23
33
|
|
|
24
34
|
class VersionHistory(BaseModel):
|
|
25
35
|
workloads: dict[str, WorkloadHistory] = Field(default_factory=dict)
|
|
@@ -38,7 +48,7 @@ class Stats(BaseModel):
|
|
|
38
48
|
|
|
39
49
|
min_version: str
|
|
40
50
|
min_version_per_workload: dict[str, str] = Field(default_factory=dict)
|
|
41
|
-
inherited: Optional["Stats"]
|
|
51
|
+
inherited: Optional["Stats"] = None
|
|
42
52
|
|
|
43
53
|
def inherit(self, added: "Stats") -> None:
|
|
44
54
|
"""adds the provided stats to our inherited data
|
|
@@ -93,12 +103,12 @@ class VersionData(BaseModel):
|
|
|
93
103
|
upgrade policies.
|
|
94
104
|
"""
|
|
95
105
|
|
|
96
|
-
check_in: datetime | None
|
|
106
|
+
check_in: datetime | None = None
|
|
97
107
|
versions: dict[str, VersionHistory] = Field(default_factory=dict)
|
|
98
|
-
stats: Stats | None
|
|
108
|
+
stats: Stats | None = None
|
|
99
109
|
|
|
100
110
|
def jsondict(self) -> dict[str, Any]:
|
|
101
|
-
return
|
|
111
|
+
return self.model_dump(mode="json", exclude_none=True)
|
|
102
112
|
|
|
103
113
|
def save(self, state: State, ocm_name: str) -> None:
|
|
104
114
|
state.add(ocm_name, self.jsondict(), force=True)
|
reconcile/aus/models.py
CHANGED
|
@@ -232,7 +232,7 @@ class SectorConfigError(Exception):
|
|
|
232
232
|
|
|
233
233
|
class Sector(BaseModel):
|
|
234
234
|
name: str
|
|
235
|
-
max_parallel_upgrades: str | None
|
|
235
|
+
max_parallel_upgrades: str | None = None
|
|
236
236
|
dependencies: list[Sector] = Field(default_factory=list)
|
|
237
237
|
_specs: dict[str, ClusterUpgradeSpec] = PrivateAttr(default_factory=dict)
|
|
238
238
|
|
|
@@ -20,6 +20,7 @@ from reconcile.gql_definitions.automated_actions.instance import (
|
|
|
20
20
|
AutomatedActionExternalResourceFlushElastiCacheV1,
|
|
21
21
|
AutomatedActionExternalResourceRdsRebootV1,
|
|
22
22
|
AutomatedActionExternalResourceRdsSnapshotV1,
|
|
23
|
+
AutomatedActionOpenshiftTriggerCronjobV1,
|
|
23
24
|
AutomatedActionOpenshiftWorkloadDeleteV1,
|
|
24
25
|
AutomatedActionOpenshiftWorkloadRestartArgumentV1,
|
|
25
26
|
AutomatedActionOpenshiftWorkloadRestartV1,
|
|
@@ -82,7 +83,7 @@ class AutomatedActionsConfigIntegration(
|
|
|
82
83
|
query_func = gql.get_api().query
|
|
83
84
|
return {
|
|
84
85
|
"automated_actions_instances": [
|
|
85
|
-
c.
|
|
86
|
+
c.model_dump() for c in self.get_automated_actions_instances(query_func)
|
|
86
87
|
]
|
|
87
88
|
}
|
|
88
89
|
|
|
@@ -167,7 +168,7 @@ class AutomatedActionsConfigIntegration(
|
|
|
167
168
|
case AutomatedActionActionListV1():
|
|
168
169
|
# no special handling needed, just dump the values
|
|
169
170
|
parameters.extend(
|
|
170
|
-
arg.
|
|
171
|
+
arg.model_dump(exclude_none=True, exclude_defaults=True)
|
|
171
172
|
for arg in action.action_list_arguments or []
|
|
172
173
|
)
|
|
173
174
|
case AutomatedActionExternalResourceFlushElastiCacheV1():
|
|
@@ -205,6 +206,17 @@ class AutomatedActionsConfigIntegration(
|
|
|
205
206
|
"account": f"^{rds_snapshot_er.provisioner.name}$",
|
|
206
207
|
"identifier": rds_snapshot_arg.identifier,
|
|
207
208
|
})
|
|
209
|
+
case AutomatedActionOpenshiftTriggerCronjobV1():
|
|
210
|
+
parameters.extend(
|
|
211
|
+
{
|
|
212
|
+
# all parameter values are regexes in the OPA policy
|
|
213
|
+
# therefore, cluster and namespace must be fixed to the current strings
|
|
214
|
+
"cluster": f"^{arg.namespace.cluster.name}$",
|
|
215
|
+
"namespace": f"^{arg.namespace.name}$",
|
|
216
|
+
"cronjob": arg.cronjob,
|
|
217
|
+
}
|
|
218
|
+
for arg in action.openshift_trigger_cronjob_arguments
|
|
219
|
+
)
|
|
208
220
|
case AutomatedActionOpenshiftWorkloadDeleteV1():
|
|
209
221
|
parameters.extend(
|
|
210
222
|
{
|
|
@@ -257,7 +269,7 @@ class AutomatedActionsConfigIntegration(
|
|
|
257
269
|
{
|
|
258
270
|
"users": {user.username: sorted(user.roles) for user in users},
|
|
259
271
|
"roles": {
|
|
260
|
-
role: [policy.
|
|
272
|
+
role: [policy.model_dump() for policy in policies]
|
|
261
273
|
for role, policies in roles.items()
|
|
262
274
|
},
|
|
263
275
|
},
|
|
@@ -42,14 +42,14 @@ QONTRACT_INTEGRATION_VERSION = make_semver(1, 0, 0)
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
class AwsAccountMgmtIntegrationParams(PydanticRunParams):
|
|
45
|
-
account_name: str | None
|
|
45
|
+
account_name: str | None = None
|
|
46
46
|
flavor: str
|
|
47
47
|
organization_account_role: str = "OrganizationAccountAccessRole"
|
|
48
48
|
default_tags: dict[str, str] = {}
|
|
49
49
|
initial_user_name: str = "terraform"
|
|
50
50
|
initial_user_policy_arn: str = "arn:aws:iam::aws:policy/AdministratorAccess"
|
|
51
51
|
initial_user_secret_vault_path: str = (
|
|
52
|
-
"app-sre-v2/creds/terraform/{account_name}/config"
|
|
52
|
+
"app-sre-v2/creds/terraform/{account_name}/config" # noqa: RUF027
|
|
53
53
|
)
|
|
54
54
|
# To avoid the accidental deletion of the resource file, explicitly set the
|
|
55
55
|
# qontract.cli option in the integration extraArgs!
|
|
@@ -76,9 +76,9 @@ class AwsAccountMgmtIntegration(
|
|
|
76
76
|
query_func, account_name=self.params.account_name
|
|
77
77
|
)
|
|
78
78
|
return {
|
|
79
|
-
"payer_accounts": [account.
|
|
79
|
+
"payer_accounts": [account.model_dump() for account in payer_accounts],
|
|
80
80
|
"non_organization_accounts": [
|
|
81
|
-
account.
|
|
81
|
+
account.model_dump() for account in non_organization_accounts
|
|
82
82
|
],
|
|
83
83
|
}
|
|
84
84
|
|
|
@@ -98,7 +98,7 @@ class AwsAccountMgmtIntegration(
|
|
|
98
98
|
lstrip_blocks=True,
|
|
99
99
|
keep_trailing_newline=True,
|
|
100
100
|
).render({
|
|
101
|
-
"accountRequest": account_request.
|
|
101
|
+
"accountRequest": account_request.model_dump(by_alias=True),
|
|
102
102
|
"uid": uid,
|
|
103
103
|
"settings": settings,
|
|
104
104
|
"timestamp": int(utc_now().timestamp()),
|
|
@@ -187,7 +187,7 @@ class AwsAccountMgmtIntegration(
|
|
|
187
187
|
template=account_template,
|
|
188
188
|
account_request=account_request,
|
|
189
189
|
uid=uid,
|
|
190
|
-
settings=self.params.
|
|
190
|
+
settings=self.params.model_dump(),
|
|
191
191
|
),
|
|
192
192
|
account_request_file_path=f"data/{account_request.path.strip('/')}",
|
|
193
193
|
)
|
|
@@ -35,7 +35,7 @@ class Quota(Protocol):
|
|
|
35
35
|
quota_code: str
|
|
36
36
|
value: float
|
|
37
37
|
|
|
38
|
-
def
|
|
38
|
+
def model_dump(self) -> dict[str, Any]: ...
|
|
39
39
|
|
|
40
40
|
|
|
41
41
|
class Contact(Protocol):
|
|
@@ -44,7 +44,7 @@ class Contact(Protocol):
|
|
|
44
44
|
email: str
|
|
45
45
|
phone_number: str
|
|
46
46
|
|
|
47
|
-
def
|
|
47
|
+
def model_dump(self) -> dict[str, Any]: ...
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
class AWSReconciler:
|
|
@@ -167,7 +167,7 @@ class AWSReconciler:
|
|
|
167
167
|
self, aws_api: AWSApi, name: str, quotas: Iterable[Quota]
|
|
168
168
|
) -> list[str] | None:
|
|
169
169
|
"""Request service quota changes."""
|
|
170
|
-
quotas_dict = [q.
|
|
170
|
+
quotas_dict = [q.model_dump() for q in quotas]
|
|
171
171
|
with self.state.transaction(
|
|
172
172
|
state_key(name, TASK_REQUEST_SERVICE_QUOTA)
|
|
173
173
|
) as _state:
|
|
@@ -40,15 +40,12 @@ QONTRACT_INTEGRATION = "aws_ami_cleanup"
|
|
|
40
40
|
MANAGED_TAG = {"Key": "managed_by_integration", "Value": QONTRACT_INTEGRATION}
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
class AWSAmi(BaseModel):
|
|
43
|
+
class AWSAmi(BaseModel, frozen=True):
|
|
44
44
|
name: str
|
|
45
45
|
image_id: str
|
|
46
46
|
creation_date: datetime
|
|
47
47
|
snapshot_ids: list[str]
|
|
48
48
|
|
|
49
|
-
class Config:
|
|
50
|
-
frozen = True
|
|
51
|
-
|
|
52
49
|
|
|
53
50
|
def get_aws_amis_from_launch_templates(ec2_client: EC2Client) -> set[str]:
|
|
54
51
|
amis = set()
|
|
@@ -176,7 +173,7 @@ def run(dry_run: bool, thread_pool_size: int, defer: Callable | None = None) ->
|
|
|
176
173
|
# Build AWSApi object. We will use all those accounts listed in ami_accounts since
|
|
177
174
|
# we will also need to look for used AMIs.
|
|
178
175
|
accounts_dicted = [
|
|
179
|
-
account.
|
|
176
|
+
account.model_dump(by_alias=True)
|
|
180
177
|
for account in aws_accounts or []
|
|
181
178
|
if account.name in ami_accounts
|
|
182
179
|
]
|
reconcile/aws_ami_share.py
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
import re
|
|
2
3
|
from collections.abc import (
|
|
3
|
-
Callable,
|
|
4
4
|
Iterable,
|
|
5
5
|
Mapping,
|
|
6
6
|
)
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
9
|
from reconcile import queries
|
|
10
|
+
from reconcile.typed_queries.aws_account_tags import get_aws_account_tags
|
|
11
|
+
from reconcile.typed_queries.external_resources import get_settings
|
|
10
12
|
from reconcile.utils.aws_api import AWSApi
|
|
11
|
-
from reconcile.utils.defer import defer
|
|
12
13
|
|
|
13
14
|
QONTRACT_INTEGRATION = "aws-ami-share"
|
|
14
|
-
|
|
15
|
+
|
|
16
|
+
MANAGED_TAG = {"managed_by_integration": QONTRACT_INTEGRATION}
|
|
15
17
|
|
|
16
18
|
|
|
17
19
|
def filter_accounts(accounts: Iterable[dict[str, Any]]) -> list[dict[str, Any]]:
|
|
@@ -37,65 +39,70 @@ def get_region(
|
|
|
37
39
|
return region
|
|
38
40
|
|
|
39
41
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
+
def share_ami(
|
|
43
|
+
dry_run: bool,
|
|
44
|
+
src_account: Mapping[str, Any],
|
|
45
|
+
share: Mapping[str, Any],
|
|
46
|
+
default_tags: dict[str, str],
|
|
47
|
+
aws_api: AWSApi,
|
|
48
|
+
) -> None:
|
|
49
|
+
dst_account = share["account"]
|
|
50
|
+
regex = re.compile(share["regex"])
|
|
51
|
+
region = get_region(share, src_account, dst_account)
|
|
52
|
+
src_amis = aws_api.get_amis_details(src_account, src_account, regex, region)
|
|
53
|
+
dst_amis = aws_api.get_amis_details(dst_account, src_account, regex, region)
|
|
54
|
+
|
|
55
|
+
for ami_id, src_ami_tags in src_amis.items():
|
|
56
|
+
dst_ami_tags = dst_amis.get(ami_id)
|
|
57
|
+
if dst_ami_tags is None:
|
|
58
|
+
logging.info([
|
|
59
|
+
"share_ami",
|
|
60
|
+
src_account["name"],
|
|
61
|
+
dst_account["name"],
|
|
62
|
+
ami_id,
|
|
63
|
+
])
|
|
64
|
+
if not dry_run:
|
|
65
|
+
aws_api.share_ami(src_account, dst_account["uid"], ami_id, region)
|
|
66
|
+
dst_account_tags = default_tags | get_aws_account_tags(
|
|
67
|
+
dst_account.get("organization", None)
|
|
68
|
+
)
|
|
69
|
+
desired_tags = src_ami_tags | dst_account_tags | MANAGED_TAG
|
|
70
|
+
current_tags = dst_ami_tags or {}
|
|
71
|
+
|
|
72
|
+
if desired_tags != current_tags:
|
|
73
|
+
logging.info([
|
|
74
|
+
"tag_shared_ami",
|
|
75
|
+
dst_account["name"],
|
|
76
|
+
ami_id,
|
|
77
|
+
desired_tags,
|
|
78
|
+
])
|
|
79
|
+
if not dry_run:
|
|
80
|
+
aws_api.create_tags(dst_account, ami_id, desired_tags)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def run(dry_run: bool) -> None:
|
|
42
84
|
accounts = queries.get_aws_accounts(sharing=True)
|
|
43
85
|
sharing_accounts = filter_accounts(accounts)
|
|
44
86
|
settings = queries.get_app_interface_settings()
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
"share_ami",
|
|
68
|
-
src_account["name"],
|
|
69
|
-
dst_account["name"],
|
|
70
|
-
src_ami_id,
|
|
71
|
-
])
|
|
72
|
-
if not dry_run:
|
|
73
|
-
aws_api.share_ami(
|
|
74
|
-
src_account, dst_account["uid"], src_ami_id, region
|
|
75
|
-
)
|
|
76
|
-
# we assume an unshared ami does not have tags
|
|
77
|
-
found_dst_amis = [{"image_id": src_ami_id, "tags": []}]
|
|
78
|
-
|
|
79
|
-
dst_ami = found_dst_amis[0]
|
|
80
|
-
dst_ami_id = dst_ami["image_id"]
|
|
81
|
-
dst_ami_tags = dst_ami["tags"]
|
|
82
|
-
if MANAGED_TAG not in dst_ami_tags:
|
|
83
|
-
logging.info([
|
|
84
|
-
"tag_shared_ami",
|
|
85
|
-
dst_account["name"],
|
|
86
|
-
dst_ami_id,
|
|
87
|
-
MANAGED_TAG,
|
|
88
|
-
])
|
|
89
|
-
if not dry_run:
|
|
90
|
-
aws_api.create_tag(dst_account, dst_ami_id, MANAGED_TAG)
|
|
91
|
-
src_ami_tags = src_ami["tags"]
|
|
92
|
-
for src_tag in src_ami_tags:
|
|
93
|
-
if src_tag not in dst_ami_tags:
|
|
94
|
-
logging.info([
|
|
95
|
-
"tag_shared_ami",
|
|
96
|
-
dst_account["name"],
|
|
97
|
-
dst_ami_id,
|
|
98
|
-
src_tag,
|
|
99
|
-
])
|
|
100
|
-
if not dry_run:
|
|
101
|
-
aws_api.create_tag(dst_account, dst_ami_id, src_tag)
|
|
87
|
+
try:
|
|
88
|
+
default_tags = get_settings().default_tags
|
|
89
|
+
except ValueError:
|
|
90
|
+
# no external resources settings found
|
|
91
|
+
default_tags = {}
|
|
92
|
+
|
|
93
|
+
with AWSApi(
|
|
94
|
+
1,
|
|
95
|
+
sharing_accounts,
|
|
96
|
+
settings=settings,
|
|
97
|
+
init_users=False,
|
|
98
|
+
) as aws_api:
|
|
99
|
+
for src_account in sharing_accounts:
|
|
100
|
+
for share in src_account.get("sharing") or []:
|
|
101
|
+
if share["provider"] == "ami":
|
|
102
|
+
share_ami(
|
|
103
|
+
dry_run=dry_run,
|
|
104
|
+
src_account=src_account,
|
|
105
|
+
share=share,
|
|
106
|
+
default_tags=default_tags,
|
|
107
|
+
aws_api=aws_api,
|
|
108
|
+
)
|
|
@@ -82,7 +82,7 @@ class AwsSamlIdpIntegration(QontractReconcileIntegration[AwsSamlIdpIntegrationPa
|
|
|
82
82
|
if not query_func:
|
|
83
83
|
query_func = gql.get_api().query
|
|
84
84
|
return {
|
|
85
|
-
"accounts": [c.
|
|
85
|
+
"accounts": [c.model_dump() for c in self.get_aws_accounts(query_func)],
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
def get_aws_accounts(
|
|
@@ -125,7 +125,9 @@ class AwsSamlIdpIntegration(QontractReconcileIntegration[AwsSamlIdpIntegrationPa
|
|
|
125
125
|
aws_accounts = self.get_aws_accounts(
|
|
126
126
|
gql_api.query, account_name=self.params.account_name
|
|
127
127
|
)
|
|
128
|
-
aws_accounts_dict = [
|
|
128
|
+
aws_accounts_dict = [
|
|
129
|
+
account.model_dump(by_alias=True) for account in aws_accounts
|
|
130
|
+
]
|
|
129
131
|
try:
|
|
130
132
|
default_tags = get_settings().default_tags
|
|
131
133
|
except ValueError:
|
|
@@ -143,7 +145,7 @@ class AwsSamlIdpIntegration(QontractReconcileIntegration[AwsSamlIdpIntegrationPa
|
|
|
143
145
|
for saml_idp_config in self.build_saml_idp_config(
|
|
144
146
|
aws_accounts,
|
|
145
147
|
saml_idp_name=self.params.saml_idp_name,
|
|
146
|
-
saml_metadata=self.get_saml_metadata(self.params.saml_metadata_url),
|
|
148
|
+
saml_metadata=self.get_saml_metadata(str(self.params.saml_metadata_url)),
|
|
147
149
|
):
|
|
148
150
|
ts.populate_saml_idp(
|
|
149
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,
|
|
@@ -59,7 +60,8 @@ class AwsSamlRolesIntegrationParams(PydanticRunParams):
|
|
|
59
60
|
extended_early_exit_cache_ttl_seconds: int = 3600
|
|
60
61
|
log_cached_log_output: bool = False
|
|
61
62
|
|
|
62
|
-
@
|
|
63
|
+
@field_validator("max_session_duration_hours")
|
|
64
|
+
@classmethod
|
|
63
65
|
def max_session_duration_range(cls, v: str | int) -> int:
|
|
64
66
|
if 1 <= int(v) <= 12:
|
|
65
67
|
return int(v)
|
|
@@ -70,7 +72,8 @@ class CustomPolicy(BaseModel):
|
|
|
70
72
|
name: str
|
|
71
73
|
policy: dict[str, Any]
|
|
72
74
|
|
|
73
|
-
@
|
|
75
|
+
@field_validator("name")
|
|
76
|
+
@classmethod
|
|
74
77
|
def name_size(cls, v: str) -> str:
|
|
75
78
|
"""Check the policy name size.
|
|
76
79
|
|
|
@@ -82,7 +85,8 @@ class CustomPolicy(BaseModel):
|
|
|
82
85
|
)
|
|
83
86
|
return v
|
|
84
87
|
|
|
85
|
-
@
|
|
88
|
+
@field_validator("policy")
|
|
89
|
+
@classmethod
|
|
86
90
|
def policy_size(cls, v: dict[str, Any]) -> dict[str, Any]:
|
|
87
91
|
"""Check the policy size.
|
|
88
92
|
|
|
@@ -105,7 +109,8 @@ class AwsRole(BaseModel):
|
|
|
105
109
|
custom_policies: list[CustomPolicy]
|
|
106
110
|
managed_policies: list[ManagedPolicy]
|
|
107
111
|
|
|
108
|
-
@
|
|
112
|
+
@field_validator("name")
|
|
113
|
+
@classmethod
|
|
109
114
|
def name_size(cls, v: str) -> str:
|
|
110
115
|
"""Check the role name size.
|
|
111
116
|
|
|
@@ -117,29 +122,23 @@ class AwsRole(BaseModel):
|
|
|
117
122
|
)
|
|
118
123
|
return v
|
|
119
124
|
|
|
120
|
-
@
|
|
121
|
-
def validate_policies(
|
|
125
|
+
@model_validator(mode="after")
|
|
126
|
+
def validate_policies(self) -> Self:
|
|
122
127
|
"""Check the policies.
|
|
123
128
|
|
|
124
129
|
See https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html
|
|
125
130
|
"""
|
|
126
|
-
custom_policies
|
|
127
|
-
managed_policies = values.get("managed_policies", [])
|
|
128
|
-
if len(custom_policies) + len(managed_policies) > 20:
|
|
131
|
+
if len(self.custom_policies) + len(self.managed_policies) > 20:
|
|
129
132
|
raise ValueError(
|
|
130
|
-
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."
|
|
131
134
|
)
|
|
132
|
-
cp_names = [cp.name for cp in custom_policies]
|
|
135
|
+
cp_names = [cp.name for cp in self.custom_policies]
|
|
133
136
|
if len(set(cp_names)) != len(cp_names):
|
|
134
|
-
raise ValueError(
|
|
135
|
-
|
|
136
|
-
)
|
|
137
|
-
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]
|
|
138
139
|
if len(set(mp_names)) != len(mp_names):
|
|
139
|
-
raise ValueError(
|
|
140
|
-
|
|
141
|
-
)
|
|
142
|
-
return values
|
|
140
|
+
raise ValueError(f"The role '{self.name}' has duplicate managed policies.")
|
|
141
|
+
return self
|
|
143
142
|
|
|
144
143
|
|
|
145
144
|
class RunnerParams(TypedDict):
|
|
@@ -164,7 +163,7 @@ class AwsSamlRolesIntegration(
|
|
|
164
163
|
if not query_func:
|
|
165
164
|
query_func = gql.get_api().query
|
|
166
165
|
return {
|
|
167
|
-
"roles": [c.
|
|
166
|
+
"roles": [c.model_dump() for c in self.get_roles(query_func)],
|
|
168
167
|
}
|
|
169
168
|
|
|
170
169
|
def get_aws_accounts(
|
|
@@ -252,7 +251,9 @@ class AwsSamlRolesIntegration(
|
|
|
252
251
|
aws_accounts = self.get_aws_accounts(
|
|
253
252
|
gql_api.query, account_name=self.params.account_name
|
|
254
253
|
)
|
|
255
|
-
aws_accounts_dict = [
|
|
254
|
+
aws_accounts_dict = [
|
|
255
|
+
account.model_dump(by_alias=True) for account in aws_accounts
|
|
256
|
+
]
|
|
256
257
|
aws_roles = self.get_roles(gql_api.query, account_name=self.params.account_name)
|
|
257
258
|
try:
|
|
258
259
|
default_tags = get_settings().default_tags
|