qontract-reconcile 0.10.2.dev349__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.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/METADATA +12 -11
- {qontract_reconcile-0.10.2.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/RECORD +356 -350
- reconcile/acs_rbac.py +2 -2
- reconcile/aus/advanced_upgrade_service.py +15 -12
- reconcile/aus/base.py +26 -27
- 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 +8 -8
- reconcile/aws_account_manager/reconciler.py +3 -3
- reconcile/aws_ami_cleanup/integration.py +8 -12
- reconcile/aws_ami_share.py +69 -62
- reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
- reconcile/aws_ecr_image_pull_secrets.py +2 -2
- reconcile/aws_iam_keys.py +7 -41
- reconcile/aws_saml_idp/integration.py +12 -4
- reconcile/aws_saml_roles/integration.py +32 -25
- reconcile/aws_version_sync/integration.py +6 -12
- reconcile/change_owners/bundle.py +3 -3
- reconcile/change_owners/change_log_tracking.py +3 -2
- reconcile/change_owners/change_owners.py +1 -1
- reconcile/change_owners/diff.py +2 -4
- reconcile/checkpoint.py +11 -3
- reconcile/cli.py +33 -8
- reconcile/dashdotdb_dora.py +5 -12
- reconcile/dashdotdb_slo.py +1 -1
- reconcile/database_access_manager.py +123 -117
- reconcile/dynatrace_token_provider/integration.py +1 -1
- reconcile/endpoints_discovery/integration.py +4 -1
- reconcile/endpoints_discovery/merge_request.py +1 -1
- reconcile/endpoints_discovery/merge_request_manager.py +9 -11
- reconcile/external_resources/factories.py +5 -12
- reconcile/external_resources/integration.py +1 -1
- reconcile/external_resources/manager.py +24 -10
- reconcile/external_resources/meta.py +0 -1
- reconcile/external_resources/metrics.py +1 -1
- reconcile/external_resources/model.py +13 -13
- reconcile/external_resources/reconciler.py +7 -4
- reconcile/external_resources/secrets_sync.py +6 -8
- reconcile/external_resources/state.py +60 -17
- reconcile/fleet_labeler/integration.py +1 -1
- reconcile/gabi_authorized_users.py +8 -5
- reconcile/gcp_image_mirror.py +2 -2
- reconcile/github_org.py +1 -1
- reconcile/github_owners.py +4 -0
- reconcile/gitlab_housekeeping.py +13 -15
- reconcile/gitlab_members.py +6 -12
- reconcile/gitlab_mr_sqs_consumer.py +2 -2
- reconcile/gitlab_owners.py +15 -11
- reconcile/gitlab_permissions.py +8 -12
- reconcile/glitchtip_project_alerts/integration.py +3 -1
- reconcile/gql_definitions/acs/acs_instances.py +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 +7 -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 +89 -6
- reconcile/gql_definitions/external_resources/external_resources_settings.py +7 -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 +2137 -1053
- 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 +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 +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 +38 -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 -56
- reconcile/ocm_aws_infrastructure_access.py +1 -1
- reconcile/ocm_clusters.py +4 -4
- reconcile/ocm_labels/integration.py +3 -2
- reconcile/ocm_machine_pools.py +23 -23
- reconcile/openshift_base.py +53 -2
- reconcile/openshift_cluster_bots.py +3 -2
- reconcile/openshift_namespace_labels.py +1 -1
- reconcile/openshift_namespaces.py +97 -101
- reconcile/openshift_resources_base.py +6 -2
- reconcile/openshift_rhcs_certs.py +5 -5
- reconcile/openshift_rolebindings.py +7 -11
- reconcile/openshift_saas_deploy.py +6 -7
- reconcile/openshift_saas_deploy_change_tester.py +9 -7
- reconcile/openshift_saas_deploy_trigger_cleaner.py +3 -5
- reconcile/openshift_serviceaccount_tokens.py +2 -2
- reconcile/openshift_upgrade_watcher.py +4 -4
- reconcile/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 -1
- 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/saas_auto_promotions_manager/subscriber.py +4 -3
- reconcile/skupper_network/integration.py +2 -2
- reconcile/slack_usergroups.py +35 -14
- reconcile/sql_query.py +1 -0
- reconcile/status_board.py +6 -6
- reconcile/statuspage/atlassian.py +7 -7
- reconcile/statuspage/integrations/maintenances.py +4 -3
- reconcile/statuspage/page.py +4 -9
- reconcile/statuspage/status.py +5 -8
- reconcile/templates/rosa-classic-cluster-creation.sh.j2 +4 -0
- reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +3 -0
- reconcile/templating/lib/rendering.py +3 -3
- reconcile/templating/renderer.py +4 -3
- reconcile/terraform_aws_route53.py +7 -1
- reconcile/terraform_cloudflare_dns.py +3 -3
- reconcile/terraform_cloudflare_resources.py +5 -5
- reconcile/terraform_cloudflare_users.py +3 -2
- reconcile/terraform_init/integration.py +187 -23
- reconcile/terraform_repo.py +16 -12
- reconcile/terraform_resources.py +17 -7
- reconcile/terraform_tgw_attachments.py +27 -19
- reconcile/terraform_users.py +7 -0
- reconcile/terraform_vpc_peerings.py +14 -3
- reconcile/terraform_vpc_resources/integration.py +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 +13 -13
- reconcile/typed_queries/status_board.py +2 -2
- reconcile/unleash_feature_toggles/integration.py +4 -2
- reconcile/utils/acs/base.py +6 -3
- reconcile/utils/acs/policies.py +2 -2
- reconcile/utils/aggregated_list.py +4 -3
- reconcile/utils/aws_api.py +51 -54
- 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/datetime_util.py +67 -0
- reconcile/utils/deadmanssnitch_api.py +1 -1
- reconcile/utils/differ.py +2 -3
- reconcile/utils/early_exit_cache.py +11 -12
- reconcile/utils/expiration.py +7 -3
- reconcile/utils/external_resource_spec.py +24 -1
- reconcile/utils/filtering.py +1 -1
- reconcile/utils/gitlab_api.py +7 -5
- reconcile/utils/glitchtip/client.py +6 -2
- reconcile/utils/glitchtip/models.py +25 -28
- reconcile/utils/gql.py +4 -7
- reconcile/utils/helm.py +2 -1
- reconcile/utils/helpers.py +1 -1
- reconcile/utils/instrumented_wrappers.py +1 -1
- reconcile/utils/internal_groups/client.py +2 -2
- reconcile/utils/internal_groups/models.py +8 -17
- reconcile/utils/jinja2/utils.py +6 -101
- reconcile/utils/jira_client.py +82 -63
- reconcile/utils/jjb_client.py +9 -12
- reconcile/utils/jobcontroller/controller.py +1 -1
- reconcile/utils/jobcontroller/models.py +17 -1
- reconcile/utils/json.py +70 -0
- reconcile/utils/membershipsources/app_interface_resolver.py +4 -2
- reconcile/utils/membershipsources/models.py +16 -23
- reconcile/utils/membershipsources/resolver.py +4 -2
- reconcile/utils/merge_request_manager/merge_request_manager.py +4 -4
- reconcile/utils/merge_request_manager/parser.py +6 -6
- reconcile/utils/metrics.py +5 -5
- reconcile/utils/models.py +304 -82
- reconcile/utils/mr/app_interface_reporter.py +2 -2
- reconcile/utils/mr/base.py +2 -2
- reconcile/utils/mr/notificator.py +3 -3
- reconcile/utils/mr/update_access_report_base.py +3 -4
- reconcile/utils/mr/user_maintenance.py +3 -2
- reconcile/utils/oc.py +118 -97
- 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 +9 -3
- reconcile/utils/ocm/search_filters.py +3 -6
- reconcile/utils/ocm/service_log.py +4 -6
- reconcile/utils/ocm/sre_capability_labels.py +20 -13
- reconcile/utils/openshift_resource.py +10 -5
- reconcile/utils/output.py +3 -2
- reconcile/utils/pagerduty_api.py +10 -7
- reconcile/utils/promotion_state.py +6 -11
- reconcile/utils/raw_github_api.py +1 -1
- reconcile/utils/rhcsv2_certs.py +1 -4
- reconcile/utils/runtime/integration.py +2 -3
- reconcile/utils/runtime/runner.py +2 -2
- reconcile/utils/saasherder/interfaces.py +13 -20
- reconcile/utils/saasherder/models.py +25 -21
- reconcile/utils/saasherder/saasherder.py +35 -24
- reconcile/utils/slack_api.py +26 -4
- reconcile/utils/sloth.py +171 -2
- reconcile/utils/sqs_gateway.py +2 -1
- reconcile/utils/state.py +2 -1
- reconcile/utils/structs.py +1 -1
- reconcile/utils/terraform_client.py +5 -4
- reconcile/utils/terrascript_aws_client.py +171 -114
- reconcile/utils/unleash/server.py +2 -8
- reconcile/utils/vault.py +5 -12
- reconcile/utils/vcs.py +8 -8
- reconcile/vault_replication.py +107 -42
- tools/app_interface_reporter.py +4 -4
- tools/cli_commands/cost_report/cost_management_api.py +3 -3
- tools/cli_commands/cost_report/view.py +7 -6
- tools/cli_commands/erv2.py +3 -1
- tools/cli_commands/systems_and_tools.py +5 -1
- tools/qontract_cli.py +31 -18
- tools/template_validation.py +3 -1
- {qontract_reconcile-0.10.2.dev349.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev349.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
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import datetime as dt
|
|
2
1
|
import logging
|
|
3
2
|
import sys
|
|
4
3
|
from abc import (
|
|
@@ -17,7 +16,7 @@ from typing import (
|
|
|
17
16
|
)
|
|
18
17
|
|
|
19
18
|
from croniter import croniter
|
|
20
|
-
from pydantic import BaseModel
|
|
19
|
+
from pydantic import BaseModel
|
|
21
20
|
from requests.exceptions import HTTPError
|
|
22
21
|
from semver import VersionInfo
|
|
23
22
|
|
|
@@ -71,6 +70,12 @@ from reconcile.utils.clusterhealth.telemeter import (
|
|
|
71
70
|
TELEMETER_SOURCE,
|
|
72
71
|
TelemeterClusterHealthProvider,
|
|
73
72
|
)
|
|
73
|
+
from reconcile.utils.datetime_util import (
|
|
74
|
+
ensure_utc,
|
|
75
|
+
from_utc_iso_format,
|
|
76
|
+
to_utc_seconds_iso_format,
|
|
77
|
+
utc_now,
|
|
78
|
+
)
|
|
74
79
|
from reconcile.utils.defer import defer
|
|
75
80
|
from reconcile.utils.disabled_integrations import integration_is_enabled
|
|
76
81
|
from reconcile.utils.filtering import remove_none_values_from_dict
|
|
@@ -399,12 +404,12 @@ class AbstractUpgradePolicy(ABC, BaseModel):
|
|
|
399
404
|
|
|
400
405
|
cluster: OCMCluster
|
|
401
406
|
|
|
402
|
-
id: str | None
|
|
403
|
-
next_run: str | None
|
|
404
|
-
schedule: str | None
|
|
407
|
+
id: str | None = None
|
|
408
|
+
next_run: str | None = None
|
|
409
|
+
schedule: str | None = None
|
|
405
410
|
schedule_type: str
|
|
406
411
|
version: str
|
|
407
|
-
state: str | None
|
|
412
|
+
state: str | None = None
|
|
408
413
|
|
|
409
414
|
@abstractmethod
|
|
410
415
|
def create(self, ocm_api: OCMBaseClient) -> None:
|
|
@@ -420,20 +425,17 @@ class AbstractUpgradePolicy(ABC, BaseModel):
|
|
|
420
425
|
|
|
421
426
|
|
|
422
427
|
def addon_upgrade_policy_soonest_next_run() -> str:
|
|
423
|
-
now =
|
|
428
|
+
now = utc_now()
|
|
424
429
|
next_run = now + timedelta(minutes=MIN_DELTA_MINUTES)
|
|
425
|
-
return next_run
|
|
430
|
+
return to_utc_seconds_iso_format(next_run)
|
|
426
431
|
|
|
427
432
|
|
|
428
|
-
class AddonUpgradePolicy(AbstractUpgradePolicy):
|
|
433
|
+
class AddonUpgradePolicy(AbstractUpgradePolicy, arbitrary_types_allowed=True):
|
|
429
434
|
"""Class to create and delete Addon upgrade policies in OCM"""
|
|
430
435
|
|
|
431
436
|
addon_id: str
|
|
432
437
|
addon_service: AddonService
|
|
433
438
|
|
|
434
|
-
class Config:
|
|
435
|
-
arbitrary_types_allowed = True
|
|
436
|
-
|
|
437
439
|
def create(self, ocm_api: OCMBaseClient) -> None:
|
|
438
440
|
self.addon_service.create_addon_upgrade_policy(
|
|
439
441
|
ocm_api=ocm_api,
|
|
@@ -516,9 +518,10 @@ class ControlPlaneUpgradePolicy(AbstractUpgradePolicy):
|
|
|
516
518
|
|
|
517
519
|
|
|
518
520
|
class NodePoolUpgradePolicy(AbstractUpgradePolicy):
|
|
519
|
-
node_pool: str
|
|
520
521
|
"""Class to create NodePoolUpgradePolicies in OCM"""
|
|
521
522
|
|
|
523
|
+
node_pool: str
|
|
524
|
+
|
|
522
525
|
def create(self, ocm_api: OCMBaseClient) -> None:
|
|
523
526
|
policy = {
|
|
524
527
|
"version": self.version,
|
|
@@ -545,7 +548,7 @@ class NodePoolUpgradePolicy(AbstractUpgradePolicy):
|
|
|
545
548
|
return f"node pool upgrade policy - {remove_none_values_from_dict(details)}"
|
|
546
549
|
|
|
547
550
|
|
|
548
|
-
class UpgradePolicyHandler(BaseModel, extra=
|
|
551
|
+
class UpgradePolicyHandler(BaseModel, extra="forbid"):
|
|
549
552
|
"""Class to handle upgrade policy actions"""
|
|
550
553
|
|
|
551
554
|
action: str
|
|
@@ -638,8 +641,8 @@ def update_history(
|
|
|
638
641
|
version_data (VersionData): version data, including history of soakdays
|
|
639
642
|
upgrade_policies (list): query results of clusters upgrade policies
|
|
640
643
|
"""
|
|
641
|
-
now =
|
|
642
|
-
check_in = version_data.check_in or now
|
|
644
|
+
now = utc_now()
|
|
645
|
+
check_in = ensure_utc(version_data.check_in or now)
|
|
643
646
|
|
|
644
647
|
# we iterate over clusters upgrade policies and update the version history
|
|
645
648
|
for spec in org_upgrade_spec.specs:
|
|
@@ -930,7 +933,7 @@ def verify_schedule_should_skip(
|
|
|
930
933
|
# immediately
|
|
931
934
|
delay_minutes = 1 if addon_id else MIN_DELTA_MINUTES
|
|
932
935
|
next_schedule = iter.get_next(
|
|
933
|
-
|
|
936
|
+
datetime, start_time=now + timedelta(minutes=delay_minutes)
|
|
934
937
|
)
|
|
935
938
|
next_schedule_in_seconds = (next_schedule - now).total_seconds()
|
|
936
939
|
next_schedule_in_hours = next_schedule_in_seconds / 3600 # seconds in hour
|
|
@@ -947,7 +950,7 @@ def verify_schedule_should_skip(
|
|
|
947
950
|
f"[{desired.org.org_id}/{desired.org.name}/{desired.cluster.name}] skipping cluster with no upcoming upgrade"
|
|
948
951
|
)
|
|
949
952
|
return None
|
|
950
|
-
return next_schedule
|
|
953
|
+
return to_utc_seconds_iso_format(next_schedule)
|
|
951
954
|
|
|
952
955
|
|
|
953
956
|
def verify_max_upgrades_should_skip(
|
|
@@ -1024,8 +1027,8 @@ def _calculate_node_pool_diffs(
|
|
|
1024
1027
|
) -> UpgradePolicyHandler | None:
|
|
1025
1028
|
for pool in spec.node_pools:
|
|
1026
1029
|
if parse_semver(pool.version).match(f"<{spec.current_version}"):
|
|
1027
|
-
next_schedule = (
|
|
1028
|
-
|
|
1030
|
+
next_schedule = to_utc_seconds_iso_format(
|
|
1031
|
+
now + timedelta(minutes=MIN_DELTA_MINUTES)
|
|
1029
1032
|
)
|
|
1030
1033
|
return UpgradePolicyHandler(
|
|
1031
1034
|
action="create",
|
|
@@ -1082,7 +1085,7 @@ def calculate_diff(
|
|
|
1082
1085
|
set_upgrading(spec.cluster.id, spec.effective_mutexes, sector_name)
|
|
1083
1086
|
|
|
1084
1087
|
addon_service = init_addon_service(desired_state.org.environment)
|
|
1085
|
-
now =
|
|
1088
|
+
now = utc_now()
|
|
1086
1089
|
gates = get_version_gates(ocm_api)
|
|
1087
1090
|
for spec in desired_state.specs:
|
|
1088
1091
|
sector_name = spec.upgrade_policy.conditions.sector
|
|
@@ -1119,12 +1122,10 @@ def calculate_diff(
|
|
|
1119
1122
|
UpgradePolicyHandler(
|
|
1120
1123
|
action="create",
|
|
1121
1124
|
policy=AddonUpgradePolicy(
|
|
1122
|
-
action="create",
|
|
1123
1125
|
cluster=spec.cluster,
|
|
1124
1126
|
version=version,
|
|
1125
1127
|
schedule_type="manual",
|
|
1126
1128
|
addon_id=addon_id,
|
|
1127
|
-
upgrade_type="ADDON",
|
|
1128
1129
|
addon_service=addon_service,
|
|
1129
1130
|
),
|
|
1130
1131
|
)
|
|
@@ -1297,10 +1298,8 @@ def remaining_soak_day_metric_values_for_cluster(
|
|
|
1297
1298
|
remaining_soakdays[idx] = UPGRADE_STARTED_METRIC_VALUE
|
|
1298
1299
|
if current_upgrade.next_run:
|
|
1299
1300
|
# if an upgrade runs for over 6 hours, we mark it as a long running upgrade
|
|
1300
|
-
next_run =
|
|
1301
|
-
|
|
1302
|
-
)
|
|
1303
|
-
now = datetime.utcnow()
|
|
1301
|
+
next_run = from_utc_iso_format(current_upgrade.next_run)
|
|
1302
|
+
now = utc_now()
|
|
1304
1303
|
hours_ago = (now - next_run).total_seconds() / 3600
|
|
1305
1304
|
if hours_ago >= 6:
|
|
1306
1305
|
remaining_soakdays[idx] = UPGRADE_LONG_RUNNING_METRIC_VALUE
|
|
@@ -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
|
},
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
from collections.abc import Callable, Iterable
|
|
2
|
-
from datetime import UTC, datetime
|
|
3
2
|
from typing import Any
|
|
4
3
|
|
|
5
4
|
import jinja2
|
|
@@ -26,6 +25,7 @@ from reconcile.typed_queries.gitlab_instances import get_gitlab_instances
|
|
|
26
25
|
from reconcile.utils import gql, metrics
|
|
27
26
|
from reconcile.utils.aws_api_typed.api import AWSApi, AWSStaticCredentials
|
|
28
27
|
from reconcile.utils.aws_api_typed.iam import AWSAccessKey
|
|
28
|
+
from reconcile.utils.datetime_util import utc_now
|
|
29
29
|
from reconcile.utils.defer import defer
|
|
30
30
|
from reconcile.utils.disabled_integrations import integration_is_enabled
|
|
31
31
|
from reconcile.utils.runtime.integration import (
|
|
@@ -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,10 +98,10 @@ 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
|
-
"timestamp": int(
|
|
104
|
+
"timestamp": int(utc_now().timestamp()),
|
|
105
105
|
})
|
|
106
106
|
return tmpl
|
|
107
107
|
|
|
@@ -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:
|
|
@@ -26,6 +26,7 @@ from reconcile.typed_queries.app_interface_vault_settings import (
|
|
|
26
26
|
)
|
|
27
27
|
from reconcile.utils import gql
|
|
28
28
|
from reconcile.utils.aws_api import AWSApi
|
|
29
|
+
from reconcile.utils.datetime_util import from_utc_iso_format, utc_now
|
|
29
30
|
from reconcile.utils.defer import defer
|
|
30
31
|
from reconcile.utils.parse_dhms_duration import dhms_to_seconds
|
|
31
32
|
from reconcile.utils.secret_reader import create_secret_reader
|
|
@@ -39,15 +40,12 @@ QONTRACT_INTEGRATION = "aws_ami_cleanup"
|
|
|
39
40
|
MANAGED_TAG = {"Key": "managed_by_integration", "Value": QONTRACT_INTEGRATION}
|
|
40
41
|
|
|
41
42
|
|
|
42
|
-
class AWSAmi(BaseModel):
|
|
43
|
+
class AWSAmi(BaseModel, frozen=True):
|
|
43
44
|
name: str
|
|
44
45
|
image_id: str
|
|
45
46
|
creation_date: datetime
|
|
46
47
|
snapshot_ids: list[str]
|
|
47
48
|
|
|
48
|
-
class Config:
|
|
49
|
-
frozen = True
|
|
50
|
-
|
|
51
49
|
|
|
52
50
|
def get_aws_amis_from_launch_templates(ec2_client: EC2Client) -> set[str]:
|
|
53
51
|
amis = set()
|
|
@@ -77,7 +75,7 @@ def get_aws_amis(
|
|
|
77
75
|
owner: str,
|
|
78
76
|
regex: str,
|
|
79
77
|
age_in_seconds: int,
|
|
80
|
-
|
|
78
|
+
now: datetime,
|
|
81
79
|
) -> list[AWSAmi]:
|
|
82
80
|
"""Get amis that match regex older than given age"""
|
|
83
81
|
|
|
@@ -89,10 +87,8 @@ def get_aws_amis(
|
|
|
89
87
|
if not re.search(pattern, image["Name"]):
|
|
90
88
|
continue
|
|
91
89
|
|
|
92
|
-
creation_date =
|
|
93
|
-
|
|
94
|
-
)
|
|
95
|
-
current_delta = utc_now - creation_date
|
|
90
|
+
creation_date = from_utc_iso_format(image["CreationDate"])
|
|
91
|
+
current_delta = now - creation_date
|
|
96
92
|
delete_delta = timedelta(seconds=age_in_seconds)
|
|
97
93
|
|
|
98
94
|
if current_delta < delete_delta:
|
|
@@ -135,7 +131,7 @@ def get_region(
|
|
|
135
131
|
|
|
136
132
|
@defer
|
|
137
133
|
def run(dry_run: bool, thread_pool_size: int, defer: Callable | None = None) -> None:
|
|
138
|
-
|
|
134
|
+
now = utc_now()
|
|
139
135
|
gqlapi = gql.get_api()
|
|
140
136
|
aws_accounts = aws_accounts_query(gqlapi.query).accounts
|
|
141
137
|
|
|
@@ -177,7 +173,7 @@ def run(dry_run: bool, thread_pool_size: int, defer: Callable | None = None) ->
|
|
|
177
173
|
# Build AWSApi object. We will use all those accounts listed in ami_accounts since
|
|
178
174
|
# we will also need to look for used AMIs.
|
|
179
175
|
accounts_dicted = [
|
|
180
|
-
account.
|
|
176
|
+
account.model_dump(by_alias=True)
|
|
181
177
|
for account in aws_accounts or []
|
|
182
178
|
if account.name in ami_accounts
|
|
183
179
|
]
|
|
@@ -222,7 +218,7 @@ def run(dry_run: bool, thread_pool_size: int, defer: Callable | None = None) ->
|
|
|
222
218
|
owner=account.uid,
|
|
223
219
|
regex=cleanup_config.regex,
|
|
224
220
|
age_in_seconds=age_in_seconds,
|
|
225
|
-
|
|
221
|
+
now=now,
|
|
226
222
|
)
|
|
227
223
|
|
|
228
224
|
for ami in aws_amis:
|
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
|
+
)
|