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/repo_owners.py
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
3
|
import pathlib
|
|
4
|
+
from collections.abc import Iterable, Mapping
|
|
4
5
|
|
|
5
6
|
from ruamel import yaml
|
|
6
7
|
|
|
8
|
+
from reconcile.utils.github_api import GithubRepositoryApi
|
|
9
|
+
from reconcile.utils.gitlab_api import GitLabApi
|
|
10
|
+
|
|
7
11
|
_LOG = logging.getLogger(__name__)
|
|
8
12
|
|
|
9
13
|
|
|
@@ -12,38 +16,24 @@ class RepoOwners:
|
|
|
12
16
|
Abstracts the owners of a repository with per-path granularity.
|
|
13
17
|
"""
|
|
14
18
|
|
|
15
|
-
def __init__(
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
git_cli: GitLabApi | GithubRepositoryApi,
|
|
22
|
+
ref: str = "master",
|
|
23
|
+
recursive: bool = True,
|
|
24
|
+
) -> None:
|
|
16
25
|
self._git_cli = git_cli
|
|
17
26
|
self._ref = ref
|
|
18
|
-
self._owners_map = None
|
|
27
|
+
self._owners_map: dict[str, dict[str, set[str]]] | None = None
|
|
19
28
|
self._recursive = recursive
|
|
20
29
|
|
|
21
30
|
@property
|
|
22
|
-
def owners_map(self):
|
|
31
|
+
def owners_map(self) -> dict[str, dict[str, set[str]]]:
|
|
23
32
|
if self._owners_map is None:
|
|
24
33
|
self._owners_map = self._get_owners_map()
|
|
25
34
|
return self._owners_map
|
|
26
35
|
|
|
27
|
-
def
|
|
28
|
-
"""
|
|
29
|
-
Gets all the owners of the repository.
|
|
30
|
-
|
|
31
|
-
:return: the repository owners
|
|
32
|
-
:rtype: dict
|
|
33
|
-
"""
|
|
34
|
-
repo_owners = {"approvers": set(), "reviewers": set()}
|
|
35
|
-
|
|
36
|
-
if "." in self.owners_map:
|
|
37
|
-
repo_owners["approvers"].update(self.owners_map["."]["approvers"])
|
|
38
|
-
repo_owners["reviewers"].update(self.owners_map["."]["reviewers"])
|
|
39
|
-
|
|
40
|
-
for owners in self.owners_map.values():
|
|
41
|
-
repo_owners["approvers"].update(owners["approvers"])
|
|
42
|
-
repo_owners["reviewers"].update(owners["reviewers"])
|
|
43
|
-
|
|
44
|
-
return repo_owners
|
|
45
|
-
|
|
46
|
-
def get_root_owners(self):
|
|
36
|
+
def get_root_owners(self) -> dict[str, list[str]]:
|
|
47
37
|
"""
|
|
48
38
|
Gets all the owners defined in the repository root.
|
|
49
39
|
|
|
@@ -56,7 +46,7 @@ class RepoOwners:
|
|
|
56
46
|
|
|
57
47
|
return {"approvers": [], "reviewers": []}
|
|
58
48
|
|
|
59
|
-
def get_path_owners(self, path):
|
|
49
|
+
def get_path_owners(self, path: str) -> dict[str, list[str]]:
|
|
60
50
|
"""
|
|
61
51
|
Gets all the owners of a given path, no matter in which
|
|
62
52
|
level of the filesystem tree the owner was specified.
|
|
@@ -67,7 +57,7 @@ class RepoOwners:
|
|
|
67
57
|
:return: the path owners
|
|
68
58
|
:rtype: dict
|
|
69
59
|
"""
|
|
70
|
-
path_owners = {"approvers": set(), "reviewers": set()}
|
|
60
|
+
path_owners: dict[str, set[str]] = {"approvers": set(), "reviewers": set()}
|
|
71
61
|
|
|
72
62
|
if "." in self.owners_map:
|
|
73
63
|
path_owners["approvers"].update(self.owners_map["."]["approvers"])
|
|
@@ -80,7 +70,7 @@ class RepoOwners:
|
|
|
80
70
|
|
|
81
71
|
return self._set_to_sorted_list(path_owners)
|
|
82
72
|
|
|
83
|
-
def get_path_closest_owners(self, path):
|
|
73
|
+
def get_path_closest_owners(self, path: str) -> dict[str, list[str]]:
|
|
84
74
|
"""
|
|
85
75
|
Gets all closest owners of a given path, no matter in which
|
|
86
76
|
level of the filesystem tree the owner was specified.
|
|
@@ -108,7 +98,7 @@ class RepoOwners:
|
|
|
108
98
|
|
|
109
99
|
return {"approvers": [], "reviewers": []}
|
|
110
100
|
|
|
111
|
-
def _get_owners_map(self):
|
|
101
|
+
def _get_owners_map(self) -> dict[str, dict[str, set[str]]]:
|
|
112
102
|
"""
|
|
113
103
|
Maps all the OWNERS files content to their respective
|
|
114
104
|
owned directory.
|
|
@@ -173,7 +163,7 @@ class RepoOwners:
|
|
|
173
163
|
}
|
|
174
164
|
return owners_map
|
|
175
165
|
|
|
176
|
-
def _get_aliases(self):
|
|
166
|
+
def _get_aliases(self) -> dict[str, list[str]] | None:
|
|
177
167
|
"""
|
|
178
168
|
Retrieves the approvers aliases from the OWNERS_ALIASES file.
|
|
179
169
|
|
|
@@ -191,7 +181,9 @@ class RepoOwners:
|
|
|
191
181
|
return aliases["aliases"]
|
|
192
182
|
|
|
193
183
|
@staticmethod
|
|
194
|
-
def _set_to_sorted_list(
|
|
184
|
+
def _set_to_sorted_list(
|
|
185
|
+
owners: Mapping[str, Iterable[str]],
|
|
186
|
+
) -> dict[str, list[str]]:
|
|
195
187
|
approvers = owners["approvers"]
|
|
196
188
|
sorted_approvers = sorted(approvers) if approvers else []
|
|
197
189
|
|
reconcile/utils/rhcsv2_certs.py
CHANGED
|
@@ -1,25 +1,146 @@
|
|
|
1
|
+
import base64
|
|
1
2
|
import re
|
|
2
|
-
from datetime import UTC
|
|
3
|
+
from datetime import UTC
|
|
4
|
+
from enum import StrEnum
|
|
3
5
|
|
|
4
6
|
import requests
|
|
5
7
|
from cryptography import x509
|
|
6
8
|
from cryptography.hazmat.primitives import hashes, serialization
|
|
7
9
|
from cryptography.hazmat.primitives.asymmetric import rsa
|
|
10
|
+
from cryptography.hazmat.primitives.serialization import pkcs12
|
|
8
11
|
from cryptography.x509.oid import NameOID
|
|
9
12
|
from pydantic import BaseModel, Field
|
|
10
13
|
|
|
11
14
|
|
|
12
|
-
class
|
|
15
|
+
class CertificateFormat(StrEnum):
|
|
16
|
+
PEM = "PEM"
|
|
17
|
+
PKCS12 = "PKCS12"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class RhcsV2CertPem(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
13
21
|
certificate: str = Field(alias="tls.crt")
|
|
14
22
|
private_key: str = Field(alias="tls.key")
|
|
15
23
|
ca_cert: str = Field(alias="ca.crt")
|
|
16
24
|
expiration_timestamp: int
|
|
17
25
|
|
|
18
|
-
|
|
19
|
-
|
|
26
|
+
|
|
27
|
+
class RhcsV2CertPkcs12(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
28
|
+
pkcs12_keystore: str = Field(alias="keystore.pkcs12.b64")
|
|
29
|
+
pkcs12_truststore: str = Field(alias="truststore.pkcs12.b64")
|
|
30
|
+
expiration_timestamp: int
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def extract_cert(text: str) -> re.Match:
|
|
34
|
+
# The CA webform returns an HTML page with inline JS that builds an array of “outputList”
|
|
35
|
+
# objects. Each object looks roughly like:
|
|
36
|
+
# outputList = new Object;
|
|
37
|
+
# outputList.outputId = "pretty_cert";
|
|
38
|
+
# outputList.outputSyntax = "pretty_print";
|
|
39
|
+
# outputList.outputVal = " Certificate:\n ... Not After: ...";
|
|
40
|
+
# outputListSet[0] = outputList;
|
|
41
|
+
#
|
|
42
|
+
# outputList = new Object;
|
|
43
|
+
# outputList.outputId = "b64_cert";
|
|
44
|
+
# outputList.outputSyntax = "pretty_print";
|
|
45
|
+
# outputList.outputVal = "-----BEGIN CERTIFICATE-----\n...base64...\n-----END CERTIFICATE-----\n";
|
|
46
|
+
# outputListSet[1] = outputList;
|
|
47
|
+
#
|
|
48
|
+
# We must extract the PEM from the *b64_cert* block (the pretty_cert block contains prose
|
|
49
|
+
# and formatting and is not reliable for parsing).
|
|
50
|
+
#
|
|
51
|
+
# The regex below:
|
|
52
|
+
# - Anchors on `outputId = "b64_cert"` so we only consider the base64 block.
|
|
53
|
+
# - Tolerates arbitrary whitespace around `=` and `.` (services sometimes reformat JS).
|
|
54
|
+
# - Jumps non-greedily over whatever properties sit between outputId and outputVal.
|
|
55
|
+
# - Captures only the PEM body (BEGIN…END), excluding any trailing newline/escape junk.
|
|
56
|
+
# - Accepts multiple terminators after the closing quote: literal "\\r\\n", literal "\\n",
|
|
57
|
+
# or a real newline (`\r?\n`), followed by optional whitespace and the semicolon.
|
|
58
|
+
# - Uses DOTALL so `.` spans line breaks inside the JS/HTML blob.
|
|
59
|
+
|
|
60
|
+
cert_pem = re.search(
|
|
61
|
+
r'outputList\s*\.\s*outputId\s*=\s*"b64_cert".*?'
|
|
62
|
+
r'outputList\s*\.\s*outputVal\s*=\s*"'
|
|
63
|
+
r"(-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----)"
|
|
64
|
+
r"(?:\\r\\n|\\n|\r?\n)?\s*",
|
|
65
|
+
text,
|
|
66
|
+
re.DOTALL,
|
|
67
|
+
)
|
|
68
|
+
if not cert_pem:
|
|
69
|
+
raise ValueError("Could not extract certificate PEM from response")
|
|
70
|
+
return cert_pem
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def get_cert_expiry_timestamp(js_escaped_pem: str) -> int:
|
|
74
|
+
"""Extract certificate expiry timestamp from JavaScript-escaped PEM."""
|
|
75
|
+
|
|
76
|
+
# Convert JavaScript-escaped PEM to proper format: .encode() is needed because
|
|
77
|
+
# unicode_escape decoder only works on bytes, then decode JS escape sequences
|
|
78
|
+
pem_raw = js_escaped_pem.encode().decode("unicode_escape").replace("\\/", "/")
|
|
79
|
+
cert = x509.load_pem_x509_certificate(pem_raw.encode())
|
|
80
|
+
dt_expiry = cert.not_valid_after.replace(tzinfo=UTC)
|
|
81
|
+
return int(dt_expiry.timestamp())
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _format_pem(
|
|
85
|
+
private_key: rsa.RSAPrivateKey,
|
|
86
|
+
cert_pem: str,
|
|
87
|
+
ca_pem: str,
|
|
88
|
+
cert_expiry_timestamp: int,
|
|
89
|
+
) -> RhcsV2CertPem:
|
|
90
|
+
"""Generate RhcsV2Cert with PEM components."""
|
|
91
|
+
private_key_pem = private_key.private_bytes(
|
|
92
|
+
encoding=serialization.Encoding.PEM,
|
|
93
|
+
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
|
94
|
+
encryption_algorithm=serialization.NoEncryption(),
|
|
95
|
+
).decode()
|
|
96
|
+
return RhcsV2CertPem(
|
|
97
|
+
private_key=private_key_pem,
|
|
98
|
+
certificate=cert_pem.encode().decode("unicode_escape").replace("\\/", "/"),
|
|
99
|
+
ca_cert=ca_pem,
|
|
100
|
+
expiration_timestamp=cert_expiry_timestamp,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _format_pkcs12(
|
|
105
|
+
private_key: rsa.RSAPrivateKey,
|
|
106
|
+
cert_pem: str,
|
|
107
|
+
ca_pem: str,
|
|
108
|
+
uid: str,
|
|
109
|
+
pwd: str,
|
|
110
|
+
cert_expiry_timestamp: int,
|
|
111
|
+
) -> RhcsV2CertPkcs12:
|
|
112
|
+
"""Generate PKCS#12 keystore and truststore components, returns base64-encoded strings."""
|
|
113
|
+
clean_cert_pem = cert_pem.encode().decode("unicode_escape").replace("\\/", "/")
|
|
114
|
+
cert_obj = x509.load_pem_x509_certificate(clean_cert_pem.encode())
|
|
115
|
+
ca_obj = x509.load_pem_x509_certificate(ca_pem.encode())
|
|
116
|
+
keystore_p12 = pkcs12.serialize_key_and_certificates(
|
|
117
|
+
name=uid.encode("utf-8"),
|
|
118
|
+
key=private_key,
|
|
119
|
+
cert=cert_obj,
|
|
120
|
+
cas=[ca_obj],
|
|
121
|
+
encryption_algorithm=serialization.BestAvailableEncryption(pwd.encode("utf-8")),
|
|
122
|
+
)
|
|
123
|
+
truststore_p12 = pkcs12.serialize_key_and_certificates(
|
|
124
|
+
name=b"ca-trust",
|
|
125
|
+
key=None,
|
|
126
|
+
cert=None,
|
|
127
|
+
cas=[ca_obj],
|
|
128
|
+
encryption_algorithm=serialization.NoEncryption(),
|
|
129
|
+
)
|
|
130
|
+
return RhcsV2CertPkcs12(
|
|
131
|
+
pkcs12_keystore=base64.b64encode(keystore_p12).decode("utf-8"),
|
|
132
|
+
pkcs12_truststore=base64.b64encode(truststore_p12).decode("utf-8"),
|
|
133
|
+
expiration_timestamp=cert_expiry_timestamp,
|
|
134
|
+
)
|
|
20
135
|
|
|
21
136
|
|
|
22
|
-
def generate_cert(
|
|
137
|
+
def generate_cert(
|
|
138
|
+
issuer_url: str,
|
|
139
|
+
uid: str,
|
|
140
|
+
pwd: str,
|
|
141
|
+
ca_url: str,
|
|
142
|
+
cert_format: CertificateFormat = CertificateFormat.PEM,
|
|
143
|
+
) -> RhcsV2CertPem | RhcsV2CertPkcs12:
|
|
23
144
|
private_key = rsa.generate_private_key(65537, 4096)
|
|
24
145
|
csr = (
|
|
25
146
|
x509.CertificateSigningRequestBuilder()
|
|
@@ -30,6 +151,7 @@ def generate_cert(issuer_url: str, uid: str, pwd: str, ca_url: str) -> RhcsV2Cer
|
|
|
30
151
|
)
|
|
31
152
|
.sign(private_key, hashes.SHA256())
|
|
32
153
|
)
|
|
154
|
+
|
|
33
155
|
data = {
|
|
34
156
|
"uid": uid,
|
|
35
157
|
"pwd": pwd,
|
|
@@ -39,38 +161,19 @@ def generate_cert(issuer_url: str, uid: str, pwd: str, ca_url: str) -> RhcsV2Cer
|
|
|
39
161
|
"renewal": "false",
|
|
40
162
|
"xmlOutput": "false",
|
|
41
163
|
}
|
|
42
|
-
response = requests.post(issuer_url, data=data)
|
|
164
|
+
response = requests.post(issuer_url, data=data, timeout=30)
|
|
43
165
|
response.raise_for_status()
|
|
166
|
+
cert_pem = extract_cert(response.text).group(1)
|
|
167
|
+
cert_expiry_timestamp = get_cert_expiry_timestamp(cert_pem)
|
|
44
168
|
|
|
45
|
-
|
|
46
|
-
r'outputList\.outputVal="(-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----(?:\\n|\r?\n)?)";',
|
|
47
|
-
response.text,
|
|
48
|
-
re.DOTALL,
|
|
49
|
-
)
|
|
50
|
-
if not cert_pem:
|
|
51
|
-
raise ValueError("Could not extract certificate PEM from response")
|
|
52
|
-
cert_expiry = re.search(r"Not\s+After:\s+(.*?UTC)(?:\\n|\r?\n)?", response.text)
|
|
53
|
-
if not cert_expiry:
|
|
54
|
-
raise ValueError("Could not extract expiration date from response")
|
|
55
|
-
# Weekday, Month Day, Year HH:MM:SS PM/AM Timezone
|
|
56
|
-
dt_expiry = datetime.strptime(cert_expiry.group(1), "%A, %B %d, %Y %I:%M:%S %p %Z")
|
|
57
|
-
dt_expiry = dt_expiry.replace(tzinfo=UTC)
|
|
58
|
-
private_key_pem = private_key.private_bytes(
|
|
59
|
-
encoding=serialization.Encoding.PEM,
|
|
60
|
-
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
|
61
|
-
encryption_algorithm=serialization.NoEncryption(),
|
|
62
|
-
).decode()
|
|
63
|
-
|
|
64
|
-
response = requests.get(ca_url)
|
|
169
|
+
response = requests.get(ca_url, timeout=30)
|
|
65
170
|
response.raise_for_status()
|
|
66
171
|
ca_pem = response.text
|
|
67
172
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
.
|
|
74
|
-
|
|
75
|
-
expiration_timestamp=int(dt_expiry.timestamp()),
|
|
76
|
-
)
|
|
173
|
+
match cert_format:
|
|
174
|
+
case CertificateFormat.PKCS12:
|
|
175
|
+
return _format_pkcs12(
|
|
176
|
+
private_key, cert_pem, ca_pem, uid, pwd, cert_expiry_timestamp
|
|
177
|
+
)
|
|
178
|
+
case CertificateFormat.PEM:
|
|
179
|
+
return _format_pem(private_key, cert_pem, ca_pem, cert_expiry_timestamp)
|
reconcile/utils/rosa/session.py
CHANGED
|
@@ -178,6 +178,22 @@ class RosaSession:
|
|
|
178
178
|
)
|
|
179
179
|
result.write_logs_to_logger(logging.info)
|
|
180
180
|
|
|
181
|
+
def upgrade_rosa_roles(
|
|
182
|
+
self,
|
|
183
|
+
cluster_name: str,
|
|
184
|
+
upgrade_version: str,
|
|
185
|
+
policy_version: str,
|
|
186
|
+
dry_run: bool,
|
|
187
|
+
) -> None:
|
|
188
|
+
logging.info(
|
|
189
|
+
f"Upgrade roles in AWS account {self.aws_account_id} to {upgrade_version}"
|
|
190
|
+
)
|
|
191
|
+
if not dry_run:
|
|
192
|
+
result = self.cli_execute(
|
|
193
|
+
f"rosa upgrade roles -c {cluster_name} --cluster-version {upgrade_version} --policy-version {policy_version} -y -m=auto"
|
|
194
|
+
)
|
|
195
|
+
result.write_logs_to_logger(logging.info)
|
|
196
|
+
|
|
181
197
|
|
|
182
198
|
def generate_rosa_creation_script(
|
|
183
199
|
cluster_name: str, cluster: OCMSpec, dry_run: bool
|
|
@@ -7,7 +7,6 @@ from dataclasses import dataclass
|
|
|
7
7
|
from types import ModuleType
|
|
8
8
|
from typing import (
|
|
9
9
|
Any,
|
|
10
|
-
Generic,
|
|
11
10
|
Optional,
|
|
12
11
|
TypeVar,
|
|
13
12
|
)
|
|
@@ -120,7 +119,7 @@ class PydanticRunParams(RunParams, BaseModel):
|
|
|
120
119
|
def copy_and_update(
|
|
121
120
|
self: PydanticRunParamsSelfTypeVar, update: dict[str, Any]
|
|
122
121
|
) -> PydanticRunParamsSelfTypeVar:
|
|
123
|
-
return self.
|
|
122
|
+
return self.model_copy(update=update)
|
|
124
123
|
|
|
125
124
|
def get(self, field: str) -> Any:
|
|
126
125
|
return getattr(self, field)
|
|
@@ -144,7 +143,7 @@ IntegrationClassTypeVar = TypeVar(
|
|
|
144
143
|
)
|
|
145
144
|
|
|
146
145
|
|
|
147
|
-
class QontractReconcileIntegration
|
|
146
|
+
class QontractReconcileIntegration[RunParamsTypeVar: RunParams](ABC):
|
|
148
147
|
"""
|
|
149
148
|
The base class for all integrations. It defines the basic interface to interact
|
|
150
149
|
with an integration and offers hook methods that allow the integration to opt
|
reconcile/utils/runtime/meta.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import dataclasses
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
+
from typing import Any
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
@dataclass
|
|
@@ -8,5 +9,5 @@ class IntegrationMeta:
|
|
|
8
9
|
args: list[str]
|
|
9
10
|
short_help: str | None
|
|
10
11
|
|
|
11
|
-
def to_dict(self):
|
|
12
|
+
def to_dict(self) -> dict[str, Any]:
|
|
12
13
|
return dataclasses.asdict(self)
|
|
@@ -156,7 +156,7 @@ def run_integration_cfg(run_cfg: IntegrationRunConfiguration) -> None:
|
|
|
156
156
|
_integration_wet_run(run_cfg.integration)
|
|
157
157
|
|
|
158
158
|
|
|
159
|
-
def _integration_wet_run(
|
|
159
|
+
def _integration_wet_run[RunParamsTypeVar: RunParams](
|
|
160
160
|
integration: QontractReconcileIntegration[RunParamsTypeVar],
|
|
161
161
|
) -> None:
|
|
162
162
|
"""
|
|
@@ -165,7 +165,7 @@ def _integration_wet_run(
|
|
|
165
165
|
integration.run(False)
|
|
166
166
|
|
|
167
167
|
|
|
168
|
-
def _integration_dry_run(
|
|
168
|
+
def _integration_dry_run[RunParamsTypeVar: RunParams](
|
|
169
169
|
integration: QontractReconcileIntegration[RunParamsTypeVar],
|
|
170
170
|
desired_state_diff: DesiredStateDiff | None,
|
|
171
171
|
) -> None:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# ruff: noqa: N801
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
-
from collections.abc import
|
|
4
|
+
from collections.abc import Sequence
|
|
5
5
|
from typing import (
|
|
6
6
|
TYPE_CHECKING,
|
|
7
7
|
Any,
|
|
@@ -12,6 +12,8 @@ from typing import (
|
|
|
12
12
|
from reconcile.utils import oc_connection_parameters
|
|
13
13
|
|
|
14
14
|
if TYPE_CHECKING:
|
|
15
|
+
from pydantic.main import IncEx
|
|
16
|
+
|
|
15
17
|
from reconcile.gql_definitions.fragments.saas_slo_document import SLODocument
|
|
16
18
|
from reconcile.utils.secret_reader import HasSecret
|
|
17
19
|
|
|
@@ -27,18 +29,12 @@ class SaasFileSecretParameters(Protocol):
|
|
|
27
29
|
@property
|
|
28
30
|
def secret(self) -> HasSecret: ...
|
|
29
31
|
|
|
30
|
-
def
|
|
31
|
-
self,
|
|
32
|
-
*,
|
|
33
|
-
by_alias: bool = False,
|
|
34
|
-
include: AbstractSetIntStr | MappingIntStrAny | None = None,
|
|
32
|
+
def model_dump(
|
|
33
|
+
self, *, by_alias: bool = False, include: IncEx | None = None
|
|
35
34
|
) -> dict[str, Any]: ...
|
|
36
35
|
|
|
37
36
|
|
|
38
37
|
SaasSecretParameters = Sequence[SaasFileSecretParameters] | None
|
|
39
|
-
# Taken from pydantic.typing
|
|
40
|
-
AbstractSetIntStr = Set[int | str]
|
|
41
|
-
MappingIntStrAny = Mapping[int | str, Any]
|
|
42
38
|
|
|
43
39
|
|
|
44
40
|
@runtime_checkable
|
|
@@ -212,11 +208,8 @@ class SaasResourceTemplateTargetNamespace(Protocol):
|
|
|
212
208
|
@property
|
|
213
209
|
def cluster(self) -> oc_connection_parameters.Cluster: ...
|
|
214
210
|
|
|
215
|
-
def
|
|
216
|
-
self,
|
|
217
|
-
*,
|
|
218
|
-
by_alias: bool = False,
|
|
219
|
-
include: AbstractSetIntStr | MappingIntStrAny | None = None,
|
|
211
|
+
def model_dump(
|
|
212
|
+
self, *, by_alias: bool = False, include: IncEx | None = None
|
|
220
213
|
) -> dict[str, Any]: ...
|
|
221
214
|
|
|
222
215
|
|
|
@@ -249,7 +242,7 @@ class SaasResourceTemplateTargetPromotion(Protocol):
|
|
|
249
242
|
@property
|
|
250
243
|
def promotion_data(self) -> Sequence[SaasPromotionData] | None: ...
|
|
251
244
|
|
|
252
|
-
def
|
|
245
|
+
def model_dump(self, *, by_alias: bool = False) -> dict[str, Any]: ...
|
|
253
246
|
|
|
254
247
|
|
|
255
248
|
class Channel(Protocol):
|
|
@@ -274,7 +267,7 @@ class SaasPromotion(Protocol):
|
|
|
274
267
|
@property
|
|
275
268
|
def subscribe(self) -> list[Channel] | None: ...
|
|
276
269
|
|
|
277
|
-
def
|
|
270
|
+
def model_dump(self, *, by_alias: bool = False) -> dict[str, Any]: ...
|
|
278
271
|
|
|
279
272
|
|
|
280
273
|
class SaasResourceTemplateTarget_SaasSecretParameters(Protocol):
|
|
@@ -295,7 +288,7 @@ class SaasResourceTemplateTargetUpstream(Protocol):
|
|
|
295
288
|
@property
|
|
296
289
|
def instance(self) -> SaasJenkinsInstance: ...
|
|
297
290
|
|
|
298
|
-
def
|
|
291
|
+
def model_dump(self, *, by_alias: bool = False) -> dict[str, Any]: ...
|
|
299
292
|
|
|
300
293
|
|
|
301
294
|
class SaasQuayInstance(Protocol):
|
|
@@ -315,7 +308,7 @@ class SaasResourceTemplateTargetImage(Protocol):
|
|
|
315
308
|
@property
|
|
316
309
|
def org(self) -> SaasQuayOrg: ...
|
|
317
310
|
|
|
318
|
-
def
|
|
311
|
+
def model_dump(self, *, by_alias: bool = False) -> dict[str, Any]: ...
|
|
319
312
|
|
|
320
313
|
|
|
321
314
|
class SaasResourceTemplateTarget(HasParameters, HasSecretParameters, Protocol):
|
|
@@ -344,7 +337,7 @@ class SaasResourceTemplateTarget(HasParameters, HasSecretParameters, Protocol):
|
|
|
344
337
|
self, parent_saas_file_name: str, parent_resource_template_name: str
|
|
345
338
|
) -> str: ...
|
|
346
339
|
|
|
347
|
-
def
|
|
340
|
+
def model_dump(self, *, by_alias: bool = False) -> dict[str, Any]: ...
|
|
348
341
|
|
|
349
342
|
|
|
350
343
|
class SaasResourceTemplate(HasParameters, HasSecretParameters, Protocol):
|
|
@@ -381,7 +374,7 @@ class ManagedResourceName(Protocol):
|
|
|
381
374
|
resource: str
|
|
382
375
|
resource_names: list[str]
|
|
383
376
|
|
|
384
|
-
def
|
|
377
|
+
def model_dump(self) -> dict[str, str]: ...
|
|
385
378
|
|
|
386
379
|
|
|
387
380
|
class SaasFile(HasParameters, HasSecretParameters, Protocol):
|
|
@@ -7,15 +7,13 @@ from enum import Enum
|
|
|
7
7
|
from typing import Any, NotRequired, TypedDict
|
|
8
8
|
|
|
9
9
|
from github import Github
|
|
10
|
-
from pydantic import
|
|
11
|
-
BaseModel,
|
|
12
|
-
Field,
|
|
13
|
-
)
|
|
10
|
+
from pydantic import BaseModel, Field, model_validator
|
|
14
11
|
|
|
15
12
|
from reconcile.gql_definitions.fragments.saas_slo_document import (
|
|
16
13
|
SLODocument,
|
|
17
14
|
)
|
|
18
15
|
from reconcile.utils.jenkins_api import JobBuildState
|
|
16
|
+
from reconcile.utils.json import json_dumps
|
|
19
17
|
from reconcile.utils.oc_connection_parameters import Cluster
|
|
20
18
|
from reconcile.utils.saasherder.interfaces import (
|
|
21
19
|
HasParameters,
|
|
@@ -206,7 +204,12 @@ TriggerSpecUnion = (
|
|
|
206
204
|
)
|
|
207
205
|
|
|
208
206
|
|
|
209
|
-
class Namespace(
|
|
207
|
+
class Namespace(
|
|
208
|
+
BaseModel,
|
|
209
|
+
validate_by_name=True,
|
|
210
|
+
validate_by_alias=True,
|
|
211
|
+
arbitrary_types_allowed=True,
|
|
212
|
+
):
|
|
210
213
|
name: str
|
|
211
214
|
environment: SaasEnvironment
|
|
212
215
|
app: SaasApp
|
|
@@ -216,29 +219,19 @@ class Namespace(BaseModel):
|
|
|
216
219
|
..., alias="managedResourceNames"
|
|
217
220
|
)
|
|
218
221
|
|
|
219
|
-
class Config:
|
|
220
|
-
arbitrary_types_allowed = True
|
|
221
|
-
allow_population_by_field_name = True
|
|
222
|
-
|
|
223
222
|
|
|
224
|
-
class PromotionChannelData(BaseModel):
|
|
223
|
+
class PromotionChannelData(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
225
224
|
q_type: str = Field(..., alias="type")
|
|
226
225
|
|
|
227
|
-
class Config:
|
|
228
|
-
allow_population_by_field_name = True
|
|
229
226
|
|
|
230
|
-
|
|
231
|
-
class ParentSaasPromotion(BaseModel):
|
|
227
|
+
class ParentSaasPromotion(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
232
228
|
q_type: str = Field(..., alias="type")
|
|
233
|
-
parent_saas: str | None
|
|
234
|
-
target_config_hash: str | None
|
|
235
|
-
|
|
236
|
-
class Config:
|
|
237
|
-
allow_population_by_field_name = True
|
|
229
|
+
parent_saas: str | None = None
|
|
230
|
+
target_config_hash: str | None = None
|
|
238
231
|
|
|
239
232
|
|
|
240
233
|
class PromotionData(BaseModel):
|
|
241
|
-
channel: str | None
|
|
234
|
+
channel: str | None = None
|
|
242
235
|
data: list[ParentSaasPromotion | PromotionChannelData] | None = None
|
|
243
236
|
|
|
244
237
|
|
|
@@ -263,6 +256,17 @@ class Promotion(BaseModel):
|
|
|
263
256
|
saas_file_paths: list[str] | None = None
|
|
264
257
|
target_paths: list[str] | None = None
|
|
265
258
|
|
|
259
|
+
@model_validator(mode="before")
|
|
260
|
+
@classmethod
|
|
261
|
+
def handle_gql_classes(cls, data: Any) -> Any:
|
|
262
|
+
if data.get("promotion_data"):
|
|
263
|
+
data["promotion_data"] = [
|
|
264
|
+
# convert a GQL class to a dict
|
|
265
|
+
item.model_dump() if hasattr(item, "model_dump") else item
|
|
266
|
+
for item in data["promotion_data"]
|
|
267
|
+
]
|
|
268
|
+
return data
|
|
269
|
+
|
|
266
270
|
|
|
267
271
|
@dataclass
|
|
268
272
|
class ImageAuth:
|
|
@@ -422,7 +426,7 @@ class TargetSpec:
|
|
|
422
426
|
elif v is False:
|
|
423
427
|
parameters[k] = "false"
|
|
424
428
|
elif any(isinstance(v, t) for t in [dict, list, tuple]):
|
|
425
|
-
parameters[k] =
|
|
429
|
+
parameters[k] = json_dumps(v)
|
|
426
430
|
return parameters
|
|
427
431
|
|
|
428
432
|
def _collect_secret_parameters(
|