qontract-reconcile 0.10.2.dev361__py3-none-any.whl → 0.10.2.dev474__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.dev474.dist-info}/METADATA +14 -13
- {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/RECORD +371 -364
- {qontract_reconcile-0.10.2.dev361.dist-info → qontract_reconcile-0.10.2.dev474.dist-info}/WHEEL +1 -1
- reconcile/acs_rbac.py +2 -2
- reconcile/aus/advanced_upgrade_service.py +18 -12
- reconcile/aus/aus_sts_gate_handler.py +59 -0
- reconcile/aus/base.py +137 -34
- 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_gate_approver.py +1 -16
- reconcile/aus/version_gates/sts_version_gate_handler.py +5 -72
- reconcile/automated_actions/config/integration.py +16 -4
- reconcile/aws_account_manager/integration.py +21 -9
- reconcile/aws_account_manager/reconciler.py +3 -3
- reconcile/aws_account_manager/utils.py +1 -1
- 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 +1 -1
- 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/README.md +1 -1
- reconcile/change_owners/bundle.py +3 -3
- reconcile/change_owners/change_log_tracking.py +3 -2
- reconcile/change_owners/change_owners.py +108 -42
- reconcile/change_owners/decision.py +1 -1
- reconcile/change_owners/diff.py +0 -2
- reconcile/checkpoint.py +11 -3
- reconcile/cli.py +94 -11
- 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 +6 -10
- 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 +14 -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 +18 -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 +38 -7
- 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 +12 -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 +775 -136
- 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 +37 -7
- 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 +8 -5
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +6 -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 +96 -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_base.py +25 -6
- reconcile/quay_membership.py +55 -29
- reconcile/quay_mirror.py +1 -1
- reconcile/quay_mirror_org.py +6 -4
- reconcile/quay_permissions.py +81 -75
- reconcile/quay_repos.py +35 -37
- reconcile/queries.py +132 -1
- 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/templates/rosa-classic-cluster-creation.sh.j2 +1 -1
- reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +1 -1
- reconcile/templating/lib/rendering.py +3 -3
- reconcile/templating/renderer.py +2 -2
- reconcile/templating/validator.py +4 -4
- 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 +20 -8
- reconcile/terraform_vpc_resources/merge_request.py +12 -2
- reconcile/terraform_vpc_resources/merge_request_manager.py +43 -19
- 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 +20 -15
- 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/environ.py +5 -0
- reconcile/utils/expiration.py +7 -3
- reconcile/utils/external_resource_spec.py +2 -0
- reconcile/utils/filtering.py +1 -1
- reconcile/utils/gitlab_api.py +19 -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 +26 -13
- 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 +252 -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/quay_api.py +74 -87
- 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 +274 -124
- 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
- reconcile/vpc_peerings_validator.py +13 -0
- 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.dev474.dist-info}/entry_points.txt +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from collections import defaultdict
|
|
2
|
+
from dataclasses import dataclass
|
|
2
3
|
|
|
3
4
|
from reconcile.aus.healthchecks import (
|
|
4
5
|
AUSClusterHealthCheckProvider,
|
|
@@ -13,6 +14,7 @@ from reconcile.aus.node_pool_spec import get_node_pool_specs_by_org_cluster
|
|
|
13
14
|
from reconcile.aus.ocm_upgrade_scheduler import OCMClusterUpgradeSchedulerIntegration
|
|
14
15
|
from reconcile.gql_definitions.fragments.aus_organization import AUSOCMOrganization
|
|
15
16
|
from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
|
|
17
|
+
from reconcile.utils.ocm.base import LabelContainer
|
|
16
18
|
from reconcile.utils.ocm.clusters import (
|
|
17
19
|
OCMCluster,
|
|
18
20
|
discover_clusters_for_organizations,
|
|
@@ -22,6 +24,12 @@ from reconcile.utils.ocm_base_client import init_ocm_base_client
|
|
|
22
24
|
QONTRACT_INTEGRATION = "ocm-upgrade-scheduler-org"
|
|
23
25
|
|
|
24
26
|
|
|
27
|
+
@dataclass
|
|
28
|
+
class ClusterUpgradeSpecWithLabels:
|
|
29
|
+
cluster: OCMCluster
|
|
30
|
+
cluster_labels: LabelContainer
|
|
31
|
+
|
|
32
|
+
|
|
25
33
|
class OCMClusterUpgradeSchedulerOrgIntegration(OCMClusterUpgradeSchedulerIntegration):
|
|
26
34
|
@property
|
|
27
35
|
def name(self) -> str:
|
|
@@ -60,7 +68,10 @@ class OCMClusterUpgradeSchedulerOrgIntegration(OCMClusterUpgradeSchedulerIntegra
|
|
|
60
68
|
specs=self._build_cluster_upgrade_specs(
|
|
61
69
|
org=org,
|
|
62
70
|
clusters_by_name={
|
|
63
|
-
c.ocm_cluster.name:
|
|
71
|
+
c.ocm_cluster.name: ClusterUpgradeSpecWithLabels(
|
|
72
|
+
cluster=c.ocm_cluster,
|
|
73
|
+
cluster_labels=c.labels,
|
|
74
|
+
)
|
|
64
75
|
for c in clusters_by_org[org.org_id]
|
|
65
76
|
},
|
|
66
77
|
cluster_health_provider=build_cluster_health_providers_for_organization(
|
|
@@ -79,7 +90,7 @@ class OCMClusterUpgradeSchedulerOrgIntegration(OCMClusterUpgradeSchedulerIntegra
|
|
|
79
90
|
def _build_cluster_upgrade_specs(
|
|
80
91
|
self,
|
|
81
92
|
org: AUSOCMOrganization,
|
|
82
|
-
clusters_by_name: dict[str,
|
|
93
|
+
clusters_by_name: dict[str, ClusterUpgradeSpecWithLabels],
|
|
83
94
|
cluster_health_provider: AUSClusterHealthCheckProvider,
|
|
84
95
|
node_pool_specs_by_cluster_id: dict[str, list[NodePoolSpec]],
|
|
85
96
|
) -> list[ClusterUpgradeSpec]:
|
|
@@ -87,12 +98,16 @@ class OCMClusterUpgradeSchedulerOrgIntegration(OCMClusterUpgradeSchedulerIntegra
|
|
|
87
98
|
ClusterUpgradeSpec(
|
|
88
99
|
org=org,
|
|
89
100
|
upgradePolicy=cluster.upgrade_policy,
|
|
90
|
-
cluster=clusters_by_name[cluster.name],
|
|
101
|
+
cluster=clusters_by_name[cluster.name].cluster,
|
|
102
|
+
cluster_labels=clusters_by_name[cluster.name].cluster_labels,
|
|
91
103
|
health=cluster_health_provider.cluster_health(
|
|
92
|
-
cluster_external_id=clusters_by_name[
|
|
104
|
+
cluster_external_id=clusters_by_name[
|
|
105
|
+
cluster.name
|
|
106
|
+
].cluster.external_id,
|
|
93
107
|
org_id=org.org_id,
|
|
94
108
|
),
|
|
95
|
-
nodePools=node_pool_specs_by_cluster_id.get(ocm_cluster.id)
|
|
109
|
+
nodePools=node_pool_specs_by_cluster_id.get(ocm_cluster.cluster.id)
|
|
110
|
+
or [],
|
|
96
111
|
)
|
|
97
112
|
for cluster in org.upgrade_policy_clusters or []
|
|
98
113
|
# clusters that are not in the UUID dict will be ignored because
|
|
@@ -14,9 +14,6 @@ from reconcile.gql_definitions.common.ocm_environments import (
|
|
|
14
14
|
)
|
|
15
15
|
from reconcile.utils import gql
|
|
16
16
|
from reconcile.utils.grouping import group_by
|
|
17
|
-
from reconcile.utils.jobcontroller.controller import (
|
|
18
|
-
build_job_controller,
|
|
19
|
-
)
|
|
20
17
|
from reconcile.utils.ocm.base import (
|
|
21
18
|
ClusterDetails,
|
|
22
19
|
LabelContainer,
|
|
@@ -63,19 +60,7 @@ class VersionGateApprover(QontractReconcileIntegration[VersionGateApproverParams
|
|
|
63
60
|
|
|
64
61
|
def initialize_handlers(self, query_func: Callable) -> None:
|
|
65
62
|
self.handlers: dict[str, GateHandler] = {
|
|
66
|
-
sts_version_gate_handler.GATE_LABEL: sts_version_gate_handler.STSGateHandler(
|
|
67
|
-
job_controller=build_job_controller(
|
|
68
|
-
integration=QONTRACT_INTEGRATION,
|
|
69
|
-
integration_version=QONTRACT_INTEGRATION_VERSION,
|
|
70
|
-
cluster=self.params.job_controller_cluster,
|
|
71
|
-
namespace=self.params.job_controller_namespace,
|
|
72
|
-
secret_reader=self.secret_reader,
|
|
73
|
-
dry_run=False,
|
|
74
|
-
),
|
|
75
|
-
aws_iam_role=self.params.rosa_role,
|
|
76
|
-
rosa_job_service_account=self.params.rosa_job_service_account,
|
|
77
|
-
rosa_job_image=self.params.rosa_job_image,
|
|
78
|
-
),
|
|
63
|
+
sts_version_gate_handler.GATE_LABEL: sts_version_gate_handler.STSGateHandler(),
|
|
79
64
|
ocp_gate_handler.GATE_LABEL: ocp_gate_handler.OCPGateHandler(),
|
|
80
65
|
ingress_gate_handler.GATE_LABEL: ingress_gate_handler.IngressGateHandler(),
|
|
81
66
|
}
|
|
@@ -1,27 +1,16 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
|
|
3
1
|
from reconcile.aus.version_gates.handler import GateHandler
|
|
4
|
-
from reconcile.utils.jobcontroller.controller import K8sJobController
|
|
5
2
|
from reconcile.utils.ocm.base import OCMCluster, OCMVersionGate
|
|
6
3
|
from reconcile.utils.ocm_base_client import OCMBaseClient
|
|
7
|
-
from reconcile.utils.rosa.rosa_cli import RosaCliError
|
|
8
|
-
from reconcile.utils.rosa.session import RosaSession
|
|
9
4
|
|
|
10
5
|
GATE_LABEL = "api.openshift.com/gate-sts"
|
|
11
6
|
|
|
12
7
|
|
|
13
8
|
class STSGateHandler(GateHandler):
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
rosa_job_image: str | None = None,
|
|
20
|
-
) -> None:
|
|
21
|
-
self.job_controller = job_controller
|
|
22
|
-
self.aws_iam_role = aws_iam_role
|
|
23
|
-
self.rosa_job_image = rosa_job_image
|
|
24
|
-
self.rosa_job_service_account = rosa_job_service_account
|
|
9
|
+
"""
|
|
10
|
+
This handler is used to handle the STS version gate.
|
|
11
|
+
Right now we just ack all gate-sts gates
|
|
12
|
+
The actual job of role upgrade is now a part of AUS and is handled by the AUSSTSGateHandler.
|
|
13
|
+
"""
|
|
25
14
|
|
|
26
15
|
@staticmethod
|
|
27
16
|
def gate_applicable_to_cluster(cluster: OCMCluster) -> bool:
|
|
@@ -41,60 +30,4 @@ class STSGateHandler(GateHandler):
|
|
|
41
30
|
gate: OCMVersionGate,
|
|
42
31
|
dry_run: bool,
|
|
43
32
|
) -> bool:
|
|
44
|
-
if (
|
|
45
|
-
not cluster.id
|
|
46
|
-
or not cluster.aws
|
|
47
|
-
or not cluster.aws.sts
|
|
48
|
-
or not cluster.is_sts()
|
|
49
|
-
):
|
|
50
|
-
# checked already but mypy :/
|
|
51
|
-
return False
|
|
52
|
-
|
|
53
|
-
if cluster.is_rosa_hypershift():
|
|
54
|
-
# thanks to hypershift managed policies, there is nothing to do for us here
|
|
55
|
-
# returning True will ack the version gate
|
|
56
|
-
return True
|
|
57
|
-
if not cluster.is_rosa_classic():
|
|
58
|
-
# we manage roels only for rosa classic clusters
|
|
59
|
-
# returning here will prevent OSD STS clusters to be handled right now
|
|
60
|
-
logging.error(
|
|
61
|
-
f"Cluster {cluster.id} is not a ROSA cluster. "
|
|
62
|
-
"STS version gates are only handled for ROSA classic clusters."
|
|
63
|
-
)
|
|
64
|
-
return False
|
|
65
|
-
|
|
66
|
-
rosa = RosaSession(
|
|
67
|
-
aws_account_id=cluster.aws.aws_account_id,
|
|
68
|
-
aws_region=cluster.region.id,
|
|
69
|
-
aws_iam_role=self.aws_iam_role,
|
|
70
|
-
ocm_org_id=ocm_org_id,
|
|
71
|
-
ocm_api=ocm_api,
|
|
72
|
-
job_controller=self.job_controller,
|
|
73
|
-
image=self.rosa_job_image,
|
|
74
|
-
service_account=self.rosa_job_service_account,
|
|
75
|
-
)
|
|
76
|
-
|
|
77
|
-
try:
|
|
78
|
-
# account role handling
|
|
79
|
-
account_role_prefix = cluster.aws.account_role_prefix
|
|
80
|
-
if not account_role_prefix:
|
|
81
|
-
raise Exception(
|
|
82
|
-
f"Can't upgrade account roles. Cluster {cluster.name} does not define spec.aws.account_role_prefix"
|
|
83
|
-
)
|
|
84
|
-
rosa.upgrade_account_roles(
|
|
85
|
-
role_prefix=account_role_prefix,
|
|
86
|
-
minor_version=gate.version_raw_id_prefix,
|
|
87
|
-
channel_group=cluster.version.channel_group,
|
|
88
|
-
dry_run=dry_run,
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
# operator role handling
|
|
92
|
-
rosa.upgrade_operator_roles(
|
|
93
|
-
cluster_id=cluster.id,
|
|
94
|
-
dry_run=dry_run,
|
|
95
|
-
)
|
|
96
|
-
except RosaCliError as e:
|
|
97
|
-
logging.error(f"Failed to upgrade roles for cluster {cluster.name}: {e}")
|
|
98
|
-
e.write_logs_to_logger(logging.error)
|
|
99
|
-
return False
|
|
100
33
|
return True
|
|
@@ -7,7 +7,7 @@ from collections.abc import (
|
|
|
7
7
|
from typing import Any
|
|
8
8
|
|
|
9
9
|
import yaml
|
|
10
|
-
from kubernetes.client import (
|
|
10
|
+
from kubernetes.client import (
|
|
11
11
|
ApiClient,
|
|
12
12
|
V1ConfigMap,
|
|
13
13
|
V1ObjectMeta,
|
|
@@ -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
|
|
|
@@ -128,12 +128,24 @@ class AwsAccountMgmtIntegration(
|
|
|
128
128
|
for payer_account in payer_accounts
|
|
129
129
|
for org_account in payer_account.organization_accounts or []
|
|
130
130
|
}
|
|
131
|
-
|
|
132
131
|
non_organization_accounts = [
|
|
133
132
|
account
|
|
134
133
|
for account in all_aws_accounts
|
|
135
134
|
if account.name not in all_organization_account_names
|
|
136
135
|
]
|
|
136
|
+
|
|
137
|
+
# check account requests for invalid emails
|
|
138
|
+
used_emails: set[str] = {
|
|
139
|
+
owner.email
|
|
140
|
+
for account in all_aws_accounts
|
|
141
|
+
for owner in account.account_owners
|
|
142
|
+
}
|
|
143
|
+
for payer_account in payer_accounts:
|
|
144
|
+
for account_request in payer_account.account_requests or []:
|
|
145
|
+
if account_request.account_owner.email in used_emails:
|
|
146
|
+
raise ValueError(
|
|
147
|
+
f"Invalid email for account request {account_request.name} in payer account {payer_account.name}. Email {account_request.account_owner.email} is already used."
|
|
148
|
+
)
|
|
137
149
|
return payer_accounts, non_organization_accounts
|
|
138
150
|
|
|
139
151
|
def save_access_key(self, account: str, access_key: AWSAccessKey) -> None:
|
|
@@ -187,7 +199,7 @@ class AwsAccountMgmtIntegration(
|
|
|
187
199
|
template=account_template,
|
|
188
200
|
account_request=account_request,
|
|
189
201
|
uid=uid,
|
|
190
|
-
settings=self.params.
|
|
202
|
+
settings=self.params.model_dump(),
|
|
191
203
|
),
|
|
192
204
|
account_request_file_path=f"data/{account_request.path.strip('/')}",
|
|
193
205
|
)
|
|
@@ -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:
|
|
@@ -24,7 +24,7 @@ def validate(account: AWSAccountV1) -> bool:
|
|
|
24
24
|
raise ExceptionGroup("Multiple quotas are referenced in the account", errors)
|
|
25
25
|
|
|
26
26
|
if account.organization_accounts or account.account_requests:
|
|
27
|
-
# it's payer account
|
|
27
|
+
# it's a "payer account"
|
|
28
28
|
if not account.premium_support:
|
|
29
29
|
raise ValueError(
|
|
30
30
|
f"Premium support is required for payer account {account.name}"
|
|
@@ -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
|
+
)
|