qontract-reconcile 0.10.2.dev310__py3-none-any.whl → 0.10.2.dev439__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.
Potentially problematic release.
This version of qontract-reconcile might be problematic. Click here for more details.
- {qontract_reconcile-0.10.2.dev310.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/METADATA +13 -12
- {qontract_reconcile-0.10.2.dev310.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/RECORD +396 -391
- reconcile/acs_rbac.py +2 -2
- reconcile/aus/advanced_upgrade_service.py +18 -12
- reconcile/aus/base.py +134 -32
- reconcile/aus/cluster_version_data.py +15 -5
- reconcile/aus/models.py +3 -1
- reconcile/aus/ocm_addons_upgrade_scheduler_org.py +1 -0
- reconcile/aus/ocm_upgrade_scheduler.py +8 -1
- reconcile/aus/ocm_upgrade_scheduler_org.py +20 -5
- reconcile/aus/version_gates/sts_version_gate_handler.py +54 -1
- reconcile/automated_actions/config/integration.py +16 -4
- reconcile/aws_account_manager/integration.py +8 -8
- reconcile/aws_account_manager/reconciler.py +3 -3
- reconcile/aws_ami_cleanup/integration.py +8 -12
- reconcile/aws_ami_share.py +69 -62
- reconcile/aws_cloudwatch_log_retention/integration.py +155 -126
- reconcile/aws_ecr_image_pull_secrets.py +5 -5
- reconcile/aws_iam_keys.py +1 -0
- reconcile/aws_saml_idp/integration.py +12 -4
- reconcile/aws_saml_roles/integration.py +32 -25
- reconcile/aws_version_sync/integration.py +125 -84
- 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 +12 -4
- reconcile/cli.py +111 -18
- reconcile/cluster_deployment_mapper.py +2 -3
- reconcile/dashdotdb_dora.py +5 -12
- reconcile/dashdotdb_slo.py +1 -1
- reconcile/database_access_manager.py +125 -121
- reconcile/deadmanssnitch.py +1 -5
- 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 +8 -5
- reconcile/external_resources/meta.py +0 -1
- reconcile/external_resources/metrics.py +1 -1
- reconcile/external_resources/model.py +20 -20
- reconcile/external_resources/reconciler.py +7 -4
- reconcile/external_resources/secrets_sync.py +10 -14
- reconcile/external_resources/state.py +26 -16
- 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 +10 -10
- reconcile/gql_definitions/acs/acs_policies.py +5 -5
- reconcile/gql_definitions/acs/acs_rbac.py +6 -6
- reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +32 -32
- reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +26 -26
- reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +6 -7
- 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 +51 -12
- reconcile/gql_definitions/aws_account_manager/aws_accounts.py +11 -11
- reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +20 -10
- reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +28 -68
- reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +20 -10
- reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +20 -10
- reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
- reconcile/gql_definitions/aws_version_sync/clusters.py +10 -10
- 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 +9 -9
- reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +18 -18
- reconcile/gql_definitions/common/alerting_services_settings.py +9 -9
- 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 +120 -0
- reconcile/gql_definitions/common/app_interface_state_settings.py +10 -10
- 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 +22 -9
- reconcile/gql_definitions/common/aws_vpcs.py +11 -11
- reconcile/gql_definitions/common/clusters.py +37 -35
- reconcile/gql_definitions/common/clusters_minimal.py +14 -14
- reconcile/gql_definitions/common/clusters_with_dms.py +6 -6
- reconcile/gql_definitions/common/clusters_with_peering.py +29 -30
- reconcile/gql_definitions/common/github_orgs.py +10 -10
- reconcile/gql_definitions/common/jira_settings.py +10 -10
- reconcile/gql_definitions/common/jiralert_settings.py +5 -5
- reconcile/gql_definitions/common/ldap_settings.py +5 -5
- reconcile/gql_definitions/common/namespaces.py +42 -44
- reconcile/gql_definitions/common/namespaces_minimal.py +15 -13
- reconcile/gql_definitions/common/ocm_env_telemeter.py +12 -12
- reconcile/gql_definitions/common/ocm_environments.py +19 -19
- reconcile/gql_definitions/common/pagerduty_instances.py +9 -9
- reconcile/gql_definitions/common/pgp_reencryption_settings.py +6 -6
- reconcile/gql_definitions/common/pipeline_providers.py +29 -29
- 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 +44 -44
- reconcile/gql_definitions/common/saas_target_namespaces.py +10 -10
- 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 +19 -19
- reconcile/gql_definitions/common/state_aws_account.py +7 -8
- 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 +9 -9
- reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +43 -43
- reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +10 -10
- 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 +8 -8
- reconcile/gql_definitions/email_sender/users.py +6 -6
- reconcile/gql_definitions/endpoints_discovery/apps.py +10 -10
- reconcile/gql_definitions/external_resources/aws_accounts.py +9 -9
- reconcile/gql_definitions/external_resources/external_resources_modules.py +23 -23
- reconcile/gql_definitions/external_resources/external_resources_namespaces.py +494 -410
- reconcile/gql_definitions/external_resources/external_resources_settings.py +28 -26
- reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
- reconcile/gql_definitions/fleet_labeler/fleet_labels.py +40 -40
- 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_vpc_request_subnet.py → aws_organization.py} +12 -8
- 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 +9 -9
- reconcile/gql_definitions/gcp/gcp_projects.py +9 -9
- reconcile/gql_definitions/gitlab_members/gitlab_instances.py +9 -9
- reconcile/gql_definitions/gitlab_members/permissions.py +9 -9
- reconcile/gql_definitions/glitchtip/glitchtip_instance.py +9 -9
- reconcile/gql_definitions/glitchtip/glitchtip_project.py +11 -11
- reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +9 -9
- reconcile/gql_definitions/integrations/integrations.py +48 -51
- reconcile/gql_definitions/introspection.json +3510 -1865
- reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +11 -11
- reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +10 -10
- reconcile/gql_definitions/jira/jira_servers.py +5 -5
- reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +14 -10
- reconcile/gql_definitions/jumphosts/jumphosts.py +13 -13
- reconcile/gql_definitions/ldap_groups/roles.py +5 -5
- reconcile/gql_definitions/ldap_groups/settings.py +9 -9
- reconcile/gql_definitions/maintenance/maintenances.py +5 -5
- reconcile/gql_definitions/membershipsources/roles.py +5 -5
- reconcile/gql_definitions/ocm_labels/clusters.py +18 -19
- reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
- reconcile/gql_definitions/openshift_cluster_bots/clusters.py +22 -22
- reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
- reconcile/gql_definitions/openshift_groups/managed_roles.py +6 -6
- reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +10 -10
- reconcile/gql_definitions/quay_membership/quay_membership.py +6 -6
- reconcile/gql_definitions/rhcs/certs.py +33 -87
- reconcile/gql_definitions/rhcs/openshift_resource_rhcs_cert.py +43 -0
- reconcile/gql_definitions/rhidp/organizations.py +18 -18
- reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
- reconcile/gql_definitions/service_dependencies/service_dependencies.py +8 -8
- reconcile/gql_definitions/sharding/aws_accounts.py +10 -10
- reconcile/gql_definitions/sharding/ocm_organization.py +8 -8
- reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
- reconcile/gql_definitions/skupper_network/skupper_networks.py +10 -10
- reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
- reconcile/gql_definitions/slack_usergroups/permissions.py +9 -9
- 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 +6 -7
- reconcile/gql_definitions/statuspage/statuspages.py +9 -9
- 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 +6 -6
- reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +11 -11
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +11 -11
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +20 -25
- reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +6 -6
- reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +12 -12
- reconcile/gql_definitions/terraform_init/aws_accounts.py +23 -9
- reconcile/gql_definitions/terraform_repo/terraform_repo.py +9 -9
- reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
- reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +450 -402
- reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +23 -17
- reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +9 -9
- reconcile/gql_definitions/vault_instances/vault_instances.py +61 -61
- reconcile/gql_definitions/vault_policies/vault_policies.py +11 -11
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +8 -8
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
- reconcile/integrations_manager.py +3 -3
- reconcile/jenkins_job_builder.py +1 -1
- reconcile/jenkins_worker_fleets.py +80 -11
- 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 +33 -27
- reconcile/openshift_base.py +122 -10
- reconcile/openshift_cluster_bots.py +5 -5
- reconcile/openshift_groups.py +5 -0
- reconcile/openshift_limitranges.py +1 -1
- reconcile/openshift_namespace_labels.py +1 -1
- reconcile/openshift_namespaces.py +97 -101
- reconcile/openshift_resources_base.py +10 -5
- reconcile/openshift_rhcs_certs.py +77 -40
- reconcile/openshift_rolebindings.py +230 -130
- 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 +8 -7
- reconcile/openshift_tekton_resources.py +1 -1
- reconcile/openshift_upgrade_watcher.py +4 -4
- reconcile/openshift_users.py +5 -3
- reconcile/oum/labelset.py +5 -3
- reconcile/oum/models.py +1 -4
- reconcile/oum/providers.py +1 -1
- reconcile/prometheus_rules_tester/integration.py +4 -4
- reconcile/quay_mirror.py +1 -1
- reconcile/queries.py +131 -0
- reconcile/requests_sender.py +8 -3
- reconcile/resource_scraper.py +1 -5
- reconcile/rhidp/common.py +3 -5
- reconcile/rhidp/sso_client/base.py +19 -10
- reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
- reconcile/saas_auto_promotions_manager/subscriber.py +4 -3
- reconcile/sendgrid_teammates.py +20 -9
- reconcile/skupper_network/integration.py +2 -2
- reconcile/slack_usergroups.py +35 -14
- reconcile/sql_query.py +1 -0
- reconcile/status.py +2 -2
- 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 +5 -1
- reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +4 -1
- reconcile/templating/lib/merge_request_manager.py +2 -2
- reconcile/templating/lib/rendering.py +3 -3
- reconcile/templating/renderer.py +12 -13
- reconcile/terraform_aws_route53.py +18 -8
- reconcile/terraform_cloudflare_dns.py +3 -3
- reconcile/terraform_cloudflare_resources.py +12 -13
- reconcile/terraform_cloudflare_users.py +3 -2
- reconcile/terraform_init/integration.py +187 -23
- reconcile/terraform_repo.py +16 -12
- reconcile/terraform_resources.py +18 -10
- reconcile/terraform_tgw_attachments.py +28 -20
- reconcile/terraform_users.py +27 -22
- reconcile/terraform_vpc_peerings.py +15 -3
- reconcile/terraform_vpc_resources/integration.py +23 -8
- reconcile/typed_queries/app_interface_roles.py +10 -0
- 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 -20
- reconcile/utils/aws_api_typed/api.py +38 -9
- reconcile/utils/aws_api_typed/cloudformation.py +149 -0
- reconcile/utils/aws_api_typed/logs.py +73 -0
- reconcile/utils/aws_api_typed/organization.py +4 -2
- reconcile/utils/binary.py +7 -12
- reconcile/utils/datetime_util.py +67 -0
- reconcile/utils/deadmanssnitch_api.py +1 -1
- reconcile/utils/differ.py +2 -3
- reconcile/utils/early_exit_cache.py +11 -12
- reconcile/utils/expiration.py +7 -3
- reconcile/utils/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/gpg.py +5 -3
- reconcile/utils/gql.py +4 -7
- reconcile/utils/helm.py +2 -1
- reconcile/utils/helpers.py +1 -1
- reconcile/utils/imap_client.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/jenkins_api.py +24 -1
- reconcile/utils/jinja2/utils.py +6 -8
- reconcile/utils/jira_client.py +82 -63
- reconcile/utils/jjb_client.py +78 -46
- reconcile/utils/jobcontroller/controller.py +2 -2
- reconcile/utils/jobcontroller/models.py +17 -1
- reconcile/utils/json.py +74 -0
- reconcile/utils/ldap_client.py +4 -3
- reconcile/utils/lean_terraform_client.py +3 -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/__init__.py +3 -1
- reconcile/utils/mr/app_interface_reporter.py +6 -3
- reconcile/utils/mr/aws_access.py +1 -1
- reconcile/utils/mr/base.py +7 -13
- reconcile/utils/mr/clusters_updates.py +4 -2
- reconcile/utils/mr/notificator.py +3 -3
- reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py +4 -1
- reconcile/utils/mr/promote_qontract.py +28 -12
- reconcile/utils/mr/update_access_report_base.py +3 -4
- reconcile/utils/mr/user_maintenance.py +7 -6
- reconcile/utils/oc.py +445 -336
- reconcile/utils/oc_filters.py +3 -3
- reconcile/utils/ocm/addons.py +0 -1
- reconcile/utils/ocm/base.py +18 -21
- 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/ocm.py +81 -71
- 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/ocm_base_client.py +4 -4
- reconcile/utils/openshift_resource.py +83 -52
- reconcile/utils/openssl.py +2 -2
- 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 +11 -8
- reconcile/utils/repo_owners.py +21 -29
- reconcile/utils/rhcsv2_certs.py +138 -35
- reconcile/utils/rosa/session.py +16 -0
- reconcile/utils/runtime/integration.py +2 -3
- reconcile/utils/runtime/meta.py +2 -1
- 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 +60 -32
- reconcile/utils/secret_reader.py +6 -6
- reconcile/utils/sharding.py +1 -1
- reconcile/utils/slack_api.py +26 -4
- reconcile/utils/sloth.py +224 -0
- reconcile/utils/sqs_gateway.py +16 -11
- reconcile/utils/state.py +2 -1
- reconcile/utils/structs.py +1 -1
- reconcile/utils/terraform_client.py +29 -26
- reconcile/utils/terrascript_aws_client.py +200 -116
- reconcile/utils/three_way_diff_strategy.py +1 -1
- reconcile/utils/unleash/server.py +2 -8
- reconcile/utils/vault.py +44 -41
- reconcile/utils/vcs.py +8 -8
- reconcile/vault_replication.py +119 -58
- 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/cli_commands/gpg_encrypt.py +4 -1
- tools/cli_commands/systems_and_tools.py +5 -1
- tools/qontract_cli.py +36 -21
- tools/template_validation.py +3 -1
- reconcile/gql_definitions/ocm_oidc_idp/__init__.py +0 -0
- reconcile/gql_definitions/ocm_subscription_labels/__init__.py +0 -0
- reconcile/jenkins/__init__.py +0 -0
- reconcile/jenkins/types.py +0 -77
- {qontract_reconcile-0.10.2.dev310.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev310.dist-info → qontract_reconcile-0.10.2.dev439.dist-info}/entry_points.txt +0 -0
reconcile/utils/models.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
from collections import UserList
|
|
2
2
|
from collections.abc import (
|
|
3
|
-
Callable,
|
|
4
|
-
Generator,
|
|
5
3
|
MutableMapping,
|
|
6
4
|
)
|
|
7
5
|
from typing import Any
|
|
@@ -9,75 +7,311 @@ from typing import Any
|
|
|
9
7
|
from croniter import croniter
|
|
10
8
|
from pydantic import (
|
|
11
9
|
BaseModel,
|
|
10
|
+
GetCoreSchemaHandler,
|
|
12
11
|
ValidationError,
|
|
13
12
|
)
|
|
14
|
-
from
|
|
15
|
-
from pydantic.fields import ModelField
|
|
13
|
+
from pydantic_core import core_schema
|
|
16
14
|
|
|
17
15
|
DEFAULT_STRING = "I was too lazy to define a string here"
|
|
18
16
|
DEFAULT_INT = 42
|
|
17
|
+
DEFAULT_BOOL = False
|
|
18
|
+
DEFAULT_JSON_STR = "{}"
|
|
19
|
+
DEFAULT_JSON: dict = {}
|
|
20
|
+
|
|
21
|
+
DataType = MutableMapping[str, Any] | str | int | bool | list | None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _process_field_with_alias(
|
|
25
|
+
field_info: dict[str, Any],
|
|
26
|
+
data: DataType,
|
|
27
|
+
alias: str | None,
|
|
28
|
+
use_defaults: bool,
|
|
29
|
+
definitions: list[dict[str, Any]] | None,
|
|
30
|
+
) -> DataType:
|
|
31
|
+
"""Process a field by recursively calling data_default_none and handling alias.
|
|
32
|
+
|
|
33
|
+
This helper function encapsulates the common pattern used by 'default',
|
|
34
|
+
'nullable', and 'model' field types where we need to:
|
|
35
|
+
1. Recursively process the field's schema
|
|
36
|
+
2. Handle data extraction via alias if data is a dict
|
|
37
|
+
3. Update the data dict with the processed value if alias exists
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
field_info: Schema information for the field to process
|
|
41
|
+
data: Input data (can be dict, primitive, list, or None)
|
|
42
|
+
alias: Field alias for dict key access, or None for unnamed fields
|
|
43
|
+
use_defaults: Whether to use default values for missing required fields
|
|
44
|
+
definitions: Schema definitions for definition-ref fields
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Processed data value, or updated data dict if alias was provided
|
|
48
|
+
"""
|
|
49
|
+
# Recursively process the field's schema
|
|
50
|
+
value = data_default_none(
|
|
51
|
+
field_info["schema"],
|
|
52
|
+
data.get(alias) if isinstance(data, MutableMapping) and alias else data,
|
|
53
|
+
use_defaults=use_defaults,
|
|
54
|
+
definitions=definitions,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
# For unnamed fields (in recursive calls), return value directly
|
|
58
|
+
if not alias:
|
|
59
|
+
return value
|
|
60
|
+
|
|
61
|
+
# For named fields, update the data dict
|
|
62
|
+
if not isinstance(data, MutableMapping):
|
|
63
|
+
raise TypeError(
|
|
64
|
+
f"Expected MutableMapping for field '{alias}', got {type(data).__name__}"
|
|
65
|
+
)
|
|
66
|
+
data[alias] = value
|
|
67
|
+
return data
|
|
19
68
|
|
|
20
69
|
|
|
21
70
|
def data_default_none(
|
|
22
|
-
klass: type[BaseModel]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
71
|
+
klass: type[BaseModel] | dict,
|
|
72
|
+
data: DataType,
|
|
73
|
+
use_defaults: bool = True,
|
|
74
|
+
definitions: list[dict[str, Any]] | None = None,
|
|
75
|
+
) -> DataType:
|
|
76
|
+
"""Set default values for required fields in Pydantic model data.
|
|
77
|
+
|
|
78
|
+
This function recursively processes Pydantic model schemas and fills in
|
|
79
|
+
missing fields with appropriate default values based on their type. It's
|
|
80
|
+
primarily used to handle GraphQL query results where fields may be missing.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
klass: Pydantic BaseModel class or schema dict to process
|
|
84
|
+
data: Input data to fill with defaults (can be dict, primitive, list, or None)
|
|
85
|
+
use_defaults: If True, use type-based defaults for missing required fields.
|
|
86
|
+
If False, raise ValueError for missing required fields.
|
|
87
|
+
definitions: Schema definitions for definition-ref fields (e.g., VaultSecret).
|
|
88
|
+
Automatically extracted from models with "definitions" schema type.
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Data with defaults filled in. Return type matches input data structure.
|
|
92
|
+
|
|
93
|
+
Field handling behavior:
|
|
94
|
+
- Optional fields (T | None): Default to None if missing
|
|
95
|
+
- Required primitives: Use DEFAULT_STRING, DEFAULT_INT, or DEFAULT_BOOL
|
|
96
|
+
- Fields with explicit defaults: Use the provided default value
|
|
97
|
+
- Lists: Default to empty list [] if missing, recursively process items
|
|
98
|
+
- Nested models: Recursively process with data_default_none
|
|
99
|
+
- Union types: Try each variant until one validates ("first match wins")
|
|
100
|
+
|
|
101
|
+
Example:
|
|
102
|
+
>>> class User(BaseModel):
|
|
103
|
+
... name: str
|
|
104
|
+
... age: int | None
|
|
105
|
+
>>> data_default_none(User, {})
|
|
106
|
+
{'name': 'I was too lazy to define a string here', 'age': None}
|
|
107
|
+
"""
|
|
108
|
+
# =========================================================================
|
|
109
|
+
# Extract schema from BaseModel class or use provided schema dict
|
|
110
|
+
# =========================================================================
|
|
111
|
+
if isinstance(klass, dict):
|
|
112
|
+
# Recursive calls pass schema dict directly
|
|
113
|
+
schema = klass
|
|
114
|
+
else:
|
|
115
|
+
# Extract schema from Pydantic BaseModel class
|
|
116
|
+
match klass.__pydantic_core_schema__["type"]:
|
|
117
|
+
case "definitions":
|
|
118
|
+
# Models using definition-ref (e.g., VaultSecret fragments)
|
|
119
|
+
schema = klass.__pydantic_core_schema__["schema"]["schema"]
|
|
120
|
+
definitions = klass.__pydantic_core_schema__["definitions"]
|
|
121
|
+
case _:
|
|
122
|
+
# Standard models
|
|
123
|
+
schema = klass.__pydantic_core_schema__["schema"]
|
|
124
|
+
|
|
125
|
+
# =========================================================================
|
|
126
|
+
# Get fields to process - either model fields or single schema
|
|
127
|
+
# =========================================================================
|
|
128
|
+
if schema["type"] == "model-fields":
|
|
129
|
+
# Multiple fields in a model
|
|
130
|
+
fields = schema["fields"].items()
|
|
131
|
+
else:
|
|
132
|
+
# Single field schema (used in recursive calls for field processing)
|
|
133
|
+
fields = [(None, schema)]
|
|
134
|
+
|
|
135
|
+
# =========================================================================
|
|
136
|
+
# Process each field according to its schema type
|
|
137
|
+
# =========================================================================
|
|
138
|
+
for name, field_info in fields:
|
|
139
|
+
# Use alias if defined (e.g., "serverUrl" instead of "server_url")
|
|
140
|
+
alias = field_info.get("validation_alias", name)
|
|
141
|
+
|
|
142
|
+
match field_info["type"]:
|
|
143
|
+
# =================================================================
|
|
144
|
+
# PRIMITIVE TYPES: str, int, bool
|
|
145
|
+
# =================================================================
|
|
146
|
+
case "str" | "int" | "bool" | "json":
|
|
147
|
+
# If data is provided, return it as-is (don't override user data)
|
|
148
|
+
if data is not None:
|
|
149
|
+
return data
|
|
150
|
+
|
|
151
|
+
# If no defaults allowed, raise error for missing required field
|
|
152
|
+
if not use_defaults:
|
|
153
|
+
raise ValueError(f"Field {alias or ''} is required but not set.")
|
|
154
|
+
|
|
155
|
+
# Return type-specific default value
|
|
156
|
+
match field_info["type"]:
|
|
157
|
+
case "str":
|
|
158
|
+
return DEFAULT_STRING
|
|
159
|
+
case "int":
|
|
160
|
+
return DEFAULT_INT
|
|
161
|
+
case "bool":
|
|
162
|
+
return DEFAULT_BOOL
|
|
163
|
+
case "json":
|
|
164
|
+
return DEFAULT_JSON_STR
|
|
165
|
+
|
|
166
|
+
# =================================================================
|
|
167
|
+
# FIELDS WITH EXPLICIT DEFAULT VALUES
|
|
168
|
+
# =================================================================
|
|
169
|
+
case "default":
|
|
170
|
+
# If no data provided, use the field's default value
|
|
171
|
+
if data is None:
|
|
172
|
+
return field_info["default"]
|
|
173
|
+
|
|
174
|
+
# Data exists - recursively process it in case it's a complex type
|
|
175
|
+
return _process_field_with_alias(
|
|
176
|
+
field_info, data, alias, use_defaults, definitions
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
# =================================================================
|
|
180
|
+
# NULLABLE/OPTIONAL FIELDS: T | None
|
|
181
|
+
# =================================================================
|
|
182
|
+
case "nullable":
|
|
183
|
+
# If no data provided, optional fields default to None
|
|
184
|
+
if data is None:
|
|
185
|
+
return None
|
|
186
|
+
|
|
187
|
+
# Data exists - recursively process the non-None value
|
|
188
|
+
return _process_field_with_alias(
|
|
189
|
+
field_info, data, alias, use_defaults, definitions
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
# =================================================================
|
|
193
|
+
# MODEL FIELDS: Nested Pydantic models within parent model
|
|
194
|
+
# =================================================================
|
|
195
|
+
case "model-field":
|
|
196
|
+
# Ensure data is a dict (create empty dict if needed)
|
|
197
|
+
# This allows processing models even when parent data is missing
|
|
198
|
+
if not isinstance(data, MutableMapping):
|
|
199
|
+
data = {}
|
|
200
|
+
|
|
201
|
+
# Recursively process the nested model
|
|
202
|
+
data[alias] = data_default_none(
|
|
203
|
+
field_info["schema"],
|
|
204
|
+
data.get(alias),
|
|
205
|
+
use_defaults=use_defaults,
|
|
206
|
+
definitions=definitions,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
# =================================================================
|
|
210
|
+
# STANDALONE MODELS: BaseModel used as field type
|
|
211
|
+
# =================================================================
|
|
212
|
+
case "model":
|
|
213
|
+
# Recursively process nested BaseModel
|
|
214
|
+
return _process_field_with_alias(
|
|
215
|
+
field_info, data, alias, use_defaults, definitions
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# =================================================================
|
|
219
|
+
# DEFINITION REFERENCES: e.g., VaultSecret fragments
|
|
220
|
+
# =================================================================
|
|
221
|
+
case "definition-ref":
|
|
222
|
+
# Definition-ref fields require a MutableMapping data structure
|
|
223
|
+
assert isinstance(data, MutableMapping)
|
|
224
|
+
|
|
225
|
+
# Definitions are required for resolving references
|
|
226
|
+
if not definitions:
|
|
227
|
+
raise RuntimeError(
|
|
228
|
+
"definitions parameter is required for definition-ref fields"
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
# Find the referenced schema definition by ref ID
|
|
232
|
+
ref_schema = next(
|
|
233
|
+
definition
|
|
234
|
+
for definition in definitions
|
|
235
|
+
if definition["ref"] == field_info["schema_ref"]
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Recursively process using the referenced schema
|
|
239
|
+
value = data_default_none(
|
|
240
|
+
ref_schema["schema"],
|
|
241
|
+
data.get(alias) if alias else data,
|
|
242
|
+
use_defaults=use_defaults,
|
|
243
|
+
definitions=definitions,
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# For unnamed fields, return value directly
|
|
247
|
+
if not alias:
|
|
248
|
+
return value
|
|
249
|
+
|
|
250
|
+
data[alias] = value
|
|
251
|
+
|
|
252
|
+
# =================================================================
|
|
253
|
+
# UNION TYPES: T1 | T2 | ...
|
|
254
|
+
# =================================================================
|
|
255
|
+
case "union":
|
|
256
|
+
# Strategy: Try each union variant until one validates successfully
|
|
257
|
+
# This is a "first match wins" approach
|
|
258
|
+
for sub_field in field_info["choices"]:
|
|
259
|
+
try:
|
|
260
|
+
# Try to process data with this union variant
|
|
261
|
+
# use_defaults=False ensures we only match existing data
|
|
262
|
+
sub_data = data_default_none(
|
|
263
|
+
sub_field,
|
|
264
|
+
# don't alter data structure here
|
|
265
|
+
dict(data) if isinstance(data, MutableMapping) else data,
|
|
266
|
+
use_defaults=use_defaults,
|
|
267
|
+
definitions=definitions,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
# Validate the result matches the expected type
|
|
271
|
+
match sub_field["type"]:
|
|
272
|
+
case "str":
|
|
273
|
+
if not isinstance(sub_data, str):
|
|
274
|
+
raise ValueError()
|
|
275
|
+
case "int":
|
|
276
|
+
if not isinstance(sub_data, int):
|
|
277
|
+
raise ValueError()
|
|
278
|
+
case "bool":
|
|
279
|
+
if not isinstance(sub_data, bool):
|
|
280
|
+
raise ValueError()
|
|
281
|
+
case "model":
|
|
282
|
+
if not isinstance(sub_data, dict):
|
|
283
|
+
raise ValueError()
|
|
284
|
+
# Validate the dict can be instantiated as the model
|
|
285
|
+
sub_field["cls"].model_validate(sub_data)
|
|
286
|
+
if isinstance(data, MutableMapping):
|
|
287
|
+
data.update(sub_data)
|
|
288
|
+
|
|
289
|
+
# If we get here, this union variant matched successfully
|
|
290
|
+
return data
|
|
291
|
+
except (ValidationError, ValueError):
|
|
292
|
+
# This variant didn't match, try the next one
|
|
293
|
+
continue
|
|
294
|
+
|
|
295
|
+
# =================================================================
|
|
296
|
+
# LIST TYPES: list[T]
|
|
297
|
+
# =================================================================
|
|
298
|
+
case "list":
|
|
299
|
+
# If data is not a list, default to empty list
|
|
300
|
+
if not isinstance(data, list):
|
|
301
|
+
return []
|
|
302
|
+
|
|
303
|
+
# Recursively process each list item
|
|
304
|
+
return [
|
|
305
|
+
data_default_none(
|
|
306
|
+
field_info["items_schema"],
|
|
307
|
+
item.get(alias)
|
|
308
|
+
if isinstance(item, MutableMapping) and alias
|
|
309
|
+
else item,
|
|
310
|
+
use_defaults=use_defaults,
|
|
311
|
+
definitions=definitions,
|
|
312
|
+
)
|
|
313
|
+
for item in data
|
|
49
314
|
]
|
|
50
|
-
elif field.sub_fields:
|
|
51
|
-
if all(
|
|
52
|
-
isinstance(sub_field.type_, type)
|
|
53
|
-
and issubclass(sub_field.type_, BaseModel)
|
|
54
|
-
for sub_field in field.sub_fields
|
|
55
|
-
):
|
|
56
|
-
# Union[ClassA, ClassB] field
|
|
57
|
-
for sub_field in field.sub_fields:
|
|
58
|
-
if isinstance(data[field.alias], dict):
|
|
59
|
-
try:
|
|
60
|
-
d = dict(data[field.alias])
|
|
61
|
-
d.update(data_default_none(sub_field.type_, d))
|
|
62
|
-
# Lets confirm we found a matching union class
|
|
63
|
-
sub_field.type_(**d)
|
|
64
|
-
data[field.alias] = d
|
|
65
|
-
break
|
|
66
|
-
except ValidationError:
|
|
67
|
-
continue
|
|
68
|
-
elif isinstance(data[field.alias], list) and len(field.sub_fields) == 1:
|
|
69
|
-
# list[Union[ClassA, ClassB]] field
|
|
70
|
-
for sub_data in data[field.alias]:
|
|
71
|
-
for sub_field in field.sub_fields[0].sub_fields or []:
|
|
72
|
-
try:
|
|
73
|
-
d = dict(sub_data)
|
|
74
|
-
d.update(data_default_none(sub_field.type_, d))
|
|
75
|
-
# Lets confirm we found a matching union class
|
|
76
|
-
sub_field.type_(**d)
|
|
77
|
-
sub_data.update(d)
|
|
78
|
-
break
|
|
79
|
-
except ValidationError:
|
|
80
|
-
continue
|
|
81
315
|
|
|
82
316
|
return data
|
|
83
317
|
|
|
@@ -89,28 +323,16 @@ class CSV(UserList[str]):
|
|
|
89
323
|
"""
|
|
90
324
|
|
|
91
325
|
@classmethod
|
|
92
|
-
def
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
items = [] if not value else value.split(",")
|
|
99
|
-
return items
|
|
326
|
+
def __get_pydantic_core_schema__( # noqa: PLW3201
|
|
327
|
+
cls, source: type[Any], handler: GetCoreSchemaHandler
|
|
328
|
+
) -> core_schema.CoreSchema:
|
|
329
|
+
return core_schema.with_info_before_validator_function(
|
|
330
|
+
cls._validate, core_schema.list_schema()
|
|
331
|
+
)
|
|
100
332
|
|
|
101
333
|
@classmethod
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
) -> "list[str]":
|
|
105
|
-
min_items = field.field_info.extra.get("csv_min_items")
|
|
106
|
-
max_items = field.field_info.extra.get("csv_max_items")
|
|
107
|
-
|
|
108
|
-
v_len = len(v)
|
|
109
|
-
if min_items is not None and v_len < min_items:
|
|
110
|
-
raise pydantic_errors.ListMinLengthError(limit_value=min_items)
|
|
111
|
-
if max_items is not None and v_len > max_items:
|
|
112
|
-
raise pydantic_errors.ListMaxLengthError(limit_value=max_items)
|
|
113
|
-
return v
|
|
334
|
+
def _validate(cls, __input_value: str, _: Any) -> list[str]:
|
|
335
|
+
return [] if not __input_value else __input_value.split(",")
|
|
114
336
|
|
|
115
337
|
|
|
116
338
|
def cron_validator(value: str) -> str:
|
reconcile/utils/mr/__init__.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
1
3
|
from reconcile.utils.mr.app_interface_reporter import CreateAppInterfaceReporter
|
|
2
4
|
from reconcile.utils.mr.aws_access import CreateDeleteAwsAccessKey
|
|
3
5
|
from reconcile.utils.mr.base import (
|
|
@@ -44,7 +46,7 @@ class UnknownMergeRequestTypeError(Exception):
|
|
|
44
46
|
"""
|
|
45
47
|
|
|
46
48
|
|
|
47
|
-
def init_from_sqs_message(message) -> MergeRequestBase:
|
|
49
|
+
def init_from_sqs_message(message: dict[str, Any]) -> MergeRequestBase:
|
|
48
50
|
# First, let's find the classes that are inheriting from
|
|
49
51
|
# MergeRequestBase and create a map where the class.name is
|
|
50
52
|
# the key and the class itself is the value.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
from
|
|
1
|
+
from collections.abc import Iterable
|
|
2
2
|
from pathlib import Path
|
|
3
3
|
|
|
4
4
|
from ruamel.yaml.scalarstring import PreservedScalarString
|
|
5
5
|
|
|
6
|
+
from reconcile.utils.datetime_util import utc_now
|
|
6
7
|
from reconcile.utils.gitlab_api import GitLabApi
|
|
7
8
|
from reconcile.utils.mr.base import (
|
|
8
9
|
MergeRequestBase,
|
|
@@ -14,7 +15,9 @@ from reconcile.utils.mr.labels import AUTO_MERGE
|
|
|
14
15
|
class CreateAppInterfaceReporter(MergeRequestBase):
|
|
15
16
|
name = "create_app_interface_reporter_mr"
|
|
16
17
|
|
|
17
|
-
def __init__(
|
|
18
|
+
def __init__(
|
|
19
|
+
self, reports: Iterable[dict[str, str]], email_body: str, reports_path: str
|
|
20
|
+
) -> None:
|
|
18
21
|
self.reports = reports
|
|
19
22
|
self.email_body = email_body
|
|
20
23
|
self.reports_path = reports_path
|
|
@@ -23,7 +26,7 @@ class CreateAppInterfaceReporter(MergeRequestBase):
|
|
|
23
26
|
|
|
24
27
|
self.labels = [AUTO_MERGE]
|
|
25
28
|
|
|
26
|
-
now =
|
|
29
|
+
now = utc_now()
|
|
27
30
|
self.isodate = now.isoformat()
|
|
28
31
|
self.ts = now.strftime("%Y%m%d%H%M%S")
|
|
29
32
|
|
reconcile/utils/mr/aws_access.py
CHANGED
|
@@ -18,7 +18,7 @@ BODY_TEMPLATE = PROJ_ROOT / "templates" / "aws_access_key_email.j2"
|
|
|
18
18
|
class CreateDeleteAwsAccessKey(MergeRequestBase):
|
|
19
19
|
name = "create_delete_aws_access_key_mr"
|
|
20
20
|
|
|
21
|
-
def __init__(self, account, path, key):
|
|
21
|
+
def __init__(self, account: str, path: str, key: str):
|
|
22
22
|
self.account = account
|
|
23
23
|
self.path = path.lstrip("/")
|
|
24
24
|
self.key = key
|
reconcile/utils/mr/base.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import json
|
|
2
1
|
import logging
|
|
3
2
|
from abc import (
|
|
4
3
|
ABC,
|
|
@@ -11,10 +10,10 @@ from uuid import uuid4
|
|
|
11
10
|
from gitlab.exceptions import GitlabError
|
|
12
11
|
from jinja2 import Template
|
|
13
12
|
|
|
14
|
-
from reconcile import typed_queries
|
|
15
13
|
from reconcile.gql_definitions.fragments.user import User
|
|
16
14
|
from reconcile.utils.constants import PROJ_ROOT
|
|
17
15
|
from reconcile.utils.gitlab_api import GitLabApi
|
|
16
|
+
from reconcile.utils.json import json_dumps
|
|
18
17
|
from reconcile.utils.mr.labels import DO_NOT_MERGE_HOLD
|
|
19
18
|
from reconcile.utils.sqs_gateway import SQSGateway
|
|
20
19
|
|
|
@@ -45,14 +44,14 @@ class MergeRequestBase(ABC):
|
|
|
45
44
|
|
|
46
45
|
name = "merge-request-base"
|
|
47
46
|
|
|
48
|
-
def __init__(self):
|
|
47
|
+
def __init__(self) -> None:
|
|
49
48
|
# Let's first get all the attributes from the instance
|
|
50
49
|
# and use for the SQS Msg payload. With that, the msg
|
|
51
50
|
# to the SQS is enough to create a new, similar, instance
|
|
52
51
|
# of the child class.
|
|
53
52
|
self.sqs_msg_data = {**self.__dict__}
|
|
54
53
|
|
|
55
|
-
self.labels = [DO_NOT_MERGE_HOLD]
|
|
54
|
+
self.labels: Iterable[str] = [DO_NOT_MERGE_HOLD]
|
|
56
55
|
|
|
57
56
|
random_id = str(uuid4())[:6]
|
|
58
57
|
self.branch = f"{self.name}-{random_id}"
|
|
@@ -110,19 +109,14 @@ class MergeRequestBase(ABC):
|
|
|
110
109
|
}
|
|
111
110
|
|
|
112
111
|
def infer_author(
|
|
113
|
-
self,
|
|
112
|
+
self, github_user_id: str | None, all_users: Iterable[User] | None = None
|
|
114
113
|
) -> str | None:
|
|
115
|
-
if not
|
|
114
|
+
if not github_user_id:
|
|
116
115
|
return None
|
|
117
116
|
if not all_users:
|
|
118
117
|
return None
|
|
119
118
|
|
|
120
|
-
|
|
121
|
-
users = None
|
|
122
|
-
if author_email.endswith(typed_queries.smtp.settings().mail_address):
|
|
123
|
-
users = [u for u in all_users if username == u.org_username]
|
|
124
|
-
elif author_email.endswith("users.noreply.github.com"):
|
|
125
|
-
users = [u for u in all_users if username == u.github_username]
|
|
119
|
+
users = [u for u in all_users if u.github_username == github_user_id]
|
|
126
120
|
|
|
127
121
|
if users:
|
|
128
122
|
return users[0].org_username
|
|
@@ -199,7 +193,7 @@ class MergeRequestBase(ABC):
|
|
|
199
193
|
# logging this exception
|
|
200
194
|
raise MergeRequestProcessingError(
|
|
201
195
|
f"error processing {self.name} changes "
|
|
202
|
-
f"{
|
|
196
|
+
f"{json_dumps(self.sqs_msg_data)} "
|
|
203
197
|
f"into temporary branch {self.branch}. "
|
|
204
198
|
f"Reason: {err}"
|
|
205
199
|
) from err
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
1
2
|
from io import StringIO
|
|
3
|
+
from typing import Any
|
|
2
4
|
|
|
3
5
|
from ruamel.yaml import YAML
|
|
4
6
|
|
|
@@ -15,7 +17,7 @@ yaml.width = 4096
|
|
|
15
17
|
class CreateClustersUpdates(MergeRequestBase):
|
|
16
18
|
name = "create_clusters_updates_mr"
|
|
17
19
|
|
|
18
|
-
def __init__(self, clusters_updates):
|
|
20
|
+
def __init__(self, clusters_updates: Mapping[str, dict[str, Any]]):
|
|
19
21
|
self.clusters_updates = clusters_updates
|
|
20
22
|
|
|
21
23
|
super().__init__()
|
|
@@ -30,7 +32,7 @@ class CreateClustersUpdates(MergeRequestBase):
|
|
|
30
32
|
def description(self) -> str:
|
|
31
33
|
return DecisionCommand.APPROVED.value
|
|
32
34
|
|
|
33
|
-
def process(self, gitlab_cli: GitLabApi):
|
|
35
|
+
def process(self, gitlab_cli: GitLabApi) -> None:
|
|
34
36
|
changes = False
|
|
35
37
|
for cluster_name, cluster_updates in self.clusters_updates.items():
|
|
36
38
|
if not cluster_updates:
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from datetime import datetime
|
|
3
2
|
from pathlib import Path
|
|
4
3
|
|
|
5
4
|
from pydantic import BaseModel
|
|
6
5
|
|
|
6
|
+
from reconcile.utils.datetime_util import utc_now
|
|
7
7
|
from reconcile.utils.gitlab_api import GitLabApi
|
|
8
8
|
from reconcile.utils.mr.base import (
|
|
9
9
|
MergeRequestBase,
|
|
@@ -35,7 +35,7 @@ class CreateAppInterfaceNotificator(MergeRequestBase):
|
|
|
35
35
|
email_base_path: Path = Path("data") / "app-interface" / "emails",
|
|
36
36
|
dry_run: bool = False,
|
|
37
37
|
):
|
|
38
|
-
self._notification_as_dict = notification.
|
|
38
|
+
self._notification_as_dict = notification.model_dump()
|
|
39
39
|
super().__init__()
|
|
40
40
|
self._notification = notification
|
|
41
41
|
self._email_base_path = email_base_path
|
|
@@ -58,7 +58,7 @@ class CreateAppInterfaceNotificator(MergeRequestBase):
|
|
|
58
58
|
)
|
|
59
59
|
|
|
60
60
|
def process(self, gitlab_cli: GitLabApi) -> None:
|
|
61
|
-
now =
|
|
61
|
+
now = utc_now()
|
|
62
62
|
ts = now.strftime("%Y%m%d%H%M%S")
|
|
63
63
|
short_date = now.strftime("%Y-%m-%d")
|
|
64
64
|
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
1
4
|
from ruamel import yaml
|
|
2
5
|
|
|
3
6
|
from reconcile.utils.gitlab_api import GitLabApi
|
|
@@ -8,7 +11,7 @@ from reconcile.utils.mr.labels import AUTO_MERGE
|
|
|
8
11
|
class CreateOCMUpgradeSchedulerOrgUpdates(MergeRequestBase):
|
|
9
12
|
name = "create_ocm_upgrade_scheduler_org_updates_mr"
|
|
10
13
|
|
|
11
|
-
def __init__(self, updates_info):
|
|
14
|
+
def __init__(self, updates_info: Mapping[str, Any]):
|
|
12
15
|
self.updates_info = updates_info
|
|
13
16
|
|
|
14
17
|
super().__init__()
|