qontract-reconcile 0.10.2.dev299__py3-none-any.whl → 0.10.2.dev430__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/METADATA +13 -12
- {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/RECORD +399 -394
- 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 +4 -4
- 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 +8 -11
- 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 +23 -10
- 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 +492 -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 +10 -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 +3050 -1393
- 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 +448 -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 +5 -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 +4 -0
- reconcile/templates/rosa-hcp-cluster-creation.sh.j2 +3 -0
- 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 +27 -19
- reconcile/terraform_users.py +29 -21
- reconcile/terraform_vpc_peerings.py +16 -4
- reconcile/terraform_vpc_resources/integration.py +32 -2
- 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 +59 -43
- 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 +27 -20
- reconcile/utils/ocm/cluster_groups.py +1 -1
- reconcile/utils/ocm/identity_providers.py +2 -2
- reconcile/utils/ocm/labels.py +1 -1
- reconcile/utils/ocm/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 +4 -4
- reconcile/utils/terraform_client.py +32 -29
- reconcile/utils/terrascript_aws_client.py +658 -480
- reconcile/utils/three_way_diff_strategy.py +1 -1
- reconcile/utils/throughput.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
- reconcile/vpc_peerings_validator.py +2 -2
- 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/sre_checkpoints/util.py +5 -3
- 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.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev299.dist-info → qontract_reconcile-0.10.2.dev430.dist-info}/entry_points.txt +0 -0
reconcile/utils/jjb_client.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import difflib
|
|
2
2
|
import filecmp
|
|
3
|
-
import json
|
|
4
3
|
import logging
|
|
5
4
|
import os
|
|
6
5
|
import re
|
|
@@ -8,7 +7,9 @@ import shutil
|
|
|
8
7
|
import subprocess
|
|
9
8
|
import tempfile
|
|
10
9
|
import xml.etree.ElementTree as ET
|
|
10
|
+
from collections.abc import Iterable, Mapping
|
|
11
11
|
from os import path
|
|
12
|
+
from pathlib import Path
|
|
12
13
|
from subprocess import (
|
|
13
14
|
PIPE,
|
|
14
15
|
STDOUT,
|
|
@@ -17,14 +18,16 @@ from subprocess import (
|
|
|
17
18
|
from typing import Any
|
|
18
19
|
|
|
19
20
|
import yaml
|
|
20
|
-
from jenkins_jobs.builder import JenkinsManager
|
|
21
21
|
from jenkins_jobs.errors import JenkinsJobsException
|
|
22
|
-
from jenkins_jobs.
|
|
23
|
-
from jenkins_jobs.
|
|
22
|
+
from jenkins_jobs.loader import load_files
|
|
23
|
+
from jenkins_jobs.roots import Roots
|
|
24
24
|
from sretoolbox.utils import retry
|
|
25
25
|
|
|
26
26
|
from reconcile.utils import throughput
|
|
27
27
|
from reconcile.utils.helpers import toggle_logger
|
|
28
|
+
from reconcile.utils.json import json_dumps
|
|
29
|
+
from reconcile.utils.secret_reader import SecretReaderBase
|
|
30
|
+
from reconcile.utils.state import State
|
|
28
31
|
from reconcile.utils.vcs import GITHUB_BASE_URL
|
|
29
32
|
|
|
30
33
|
JJB_INI = "[jenkins]\nurl = https://JENKINS_URL"
|
|
@@ -33,14 +36,22 @@ JJB_INI = "[jenkins]\nurl = https://JENKINS_URL"
|
|
|
33
36
|
class JJB:
|
|
34
37
|
"""Wrapper around Jenkins Jobs"""
|
|
35
38
|
|
|
36
|
-
def __init__(
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
configs: list[dict[str, Any]],
|
|
42
|
+
ssl_verify: bool = True,
|
|
43
|
+
secret_reader: SecretReaderBase | None = None,
|
|
44
|
+
print_only: bool = False,
|
|
45
|
+
) -> None:
|
|
37
46
|
self.print_only = print_only
|
|
38
47
|
self.secret_reader = secret_reader
|
|
48
|
+
if not self.print_only and self.secret_reader is None:
|
|
49
|
+
raise ValueError("secret_reader must be provided if print_only is False")
|
|
39
50
|
self.collect_configs(configs)
|
|
40
51
|
self.modify_logger()
|
|
41
52
|
self.python_https_verify = str(int(ssl_verify))
|
|
42
53
|
|
|
43
|
-
def collect_configs(self, configs):
|
|
54
|
+
def collect_configs(self, configs: list[dict[str, Any]]) -> None:
|
|
44
55
|
instances = {
|
|
45
56
|
c["instance"]["name"]: {
|
|
46
57
|
"serverUrl": c["instance"]["serverUrl"],
|
|
@@ -57,7 +68,7 @@ class JJB:
|
|
|
57
68
|
server_url = data["serverUrl"]
|
|
58
69
|
wd = tempfile.mkdtemp()
|
|
59
70
|
ini = JJB_INI
|
|
60
|
-
if not self.print_only:
|
|
71
|
+
if not self.print_only and self.secret_reader:
|
|
61
72
|
ini = self.secret_reader.read(token)
|
|
62
73
|
ini = ini.replace('"', "")
|
|
63
74
|
ini = ini.replace("false", "False")
|
|
@@ -92,7 +103,7 @@ class JJB:
|
|
|
92
103
|
self.instance_urls = instance_urls
|
|
93
104
|
self.working_dirs = working_dirs
|
|
94
105
|
|
|
95
|
-
def overwrite_configs(self, configs):
|
|
106
|
+
def overwrite_configs(self, configs: Mapping[str, str] | State) -> None:
|
|
96
107
|
"""This function will override the existing
|
|
97
108
|
config files in the working directories with
|
|
98
109
|
the supplied configs"""
|
|
@@ -101,12 +112,12 @@ class JJB:
|
|
|
101
112
|
with open(config_path, "w", encoding="locale") as f:
|
|
102
113
|
f.write(configs[name])
|
|
103
114
|
|
|
104
|
-
def sort(self, configs):
|
|
115
|
+
def sort(self, configs: list[dict[str, Any]]) -> None:
|
|
105
116
|
configs.sort(key=self.sort_by_name)
|
|
106
|
-
configs.sort(key=self.sort_by_type)
|
|
117
|
+
configs.sort(key=lambda x: self.sort_by_type(x) or 0)
|
|
107
118
|
|
|
108
119
|
@staticmethod
|
|
109
|
-
def sort_by_type(config):
|
|
120
|
+
def sort_by_type(config: Mapping[str, Any]) -> int:
|
|
110
121
|
if config["type"] == "defaults":
|
|
111
122
|
return 0
|
|
112
123
|
if config["type"] == "global-defaults":
|
|
@@ -123,12 +134,13 @@ class JJB:
|
|
|
123
134
|
return 40
|
|
124
135
|
if config["type"] == "jobs":
|
|
125
136
|
return 50
|
|
137
|
+
return 100
|
|
126
138
|
|
|
127
139
|
@staticmethod
|
|
128
|
-
def sort_by_name(config):
|
|
140
|
+
def sort_by_name(config: Mapping[str, Any]) -> str:
|
|
129
141
|
return config["name"]
|
|
130
142
|
|
|
131
|
-
def get_configs(self):
|
|
143
|
+
def get_configs(self) -> dict[str, str]:
|
|
132
144
|
"""This function gets the configs from the
|
|
133
145
|
working directories"""
|
|
134
146
|
configs = {}
|
|
@@ -139,7 +151,7 @@ class JJB:
|
|
|
139
151
|
|
|
140
152
|
return configs
|
|
141
153
|
|
|
142
|
-
def generate(self, io_dir, fetch_state):
|
|
154
|
+
def generate(self, io_dir: str, fetch_state: str) -> None:
|
|
143
155
|
"""
|
|
144
156
|
Generates job definitions from JJB configs
|
|
145
157
|
|
|
@@ -163,7 +175,7 @@ class JJB:
|
|
|
163
175
|
self.execute(args)
|
|
164
176
|
throughput.change_files_ownership(io_dir)
|
|
165
177
|
|
|
166
|
-
def print_diffs(self, io_dir, instance_name=None):
|
|
178
|
+
def print_diffs(self, io_dir: str, instance_name: str | None = None) -> None:
|
|
167
179
|
"""Print the diffs between the current and
|
|
168
180
|
the desired job definitions"""
|
|
169
181
|
current_path = path.join(io_dir, "jjb", "current")
|
|
@@ -179,7 +191,7 @@ class JJB:
|
|
|
179
191
|
self.print_diff(delete, current_path, "delete")
|
|
180
192
|
self.print_diff(common, desired_path, "update")
|
|
181
193
|
|
|
182
|
-
def print_diff(self, files, replace_path, action):
|
|
194
|
+
def print_diff(self, files: Iterable[str], replace_path: str, action: str) -> None:
|
|
183
195
|
for f in files:
|
|
184
196
|
if action == "update":
|
|
185
197
|
ft = self.toggle_cd(f)
|
|
@@ -210,11 +222,16 @@ class JJB:
|
|
|
210
222
|
]
|
|
211
223
|
logging.debug("DIFF:\n" + "".join(diff))
|
|
212
224
|
|
|
213
|
-
def compare_files(
|
|
225
|
+
def compare_files(
|
|
226
|
+
self,
|
|
227
|
+
from_files: Iterable[str],
|
|
228
|
+
subtract_files: Iterable[str],
|
|
229
|
+
in_op: bool = False,
|
|
230
|
+
) -> list[str]:
|
|
214
231
|
return [f for f in from_files if (self.toggle_cd(f) in subtract_files) is in_op]
|
|
215
232
|
|
|
216
233
|
@staticmethod
|
|
217
|
-
def get_files(search_path, instance_name=None):
|
|
234
|
+
def get_files(search_path: str, instance_name: str | None = None) -> list[str]:
|
|
218
235
|
if instance_name is not None:
|
|
219
236
|
search_path = path.join(search_path, instance_name)
|
|
220
237
|
return [
|
|
@@ -222,7 +239,7 @@ class JJB:
|
|
|
222
239
|
]
|
|
223
240
|
|
|
224
241
|
@staticmethod
|
|
225
|
-
def toggle_cd(file_name):
|
|
242
|
+
def toggle_cd(file_name: str) -> str:
|
|
226
243
|
if "desired" in file_name:
|
|
227
244
|
return file_name.replace("desired", "current")
|
|
228
245
|
return file_name.replace("current", "desired")
|
|
@@ -248,43 +265,40 @@ class JJB:
|
|
|
248
265
|
raise
|
|
249
266
|
|
|
250
267
|
@staticmethod
|
|
251
|
-
def get_jjb(args):
|
|
268
|
+
def get_jjb(args: Iterable[str]) -> Any:
|
|
252
269
|
from jenkins_jobs.cli.entry import JenkinsJobs # noqa: PLC0415
|
|
253
270
|
|
|
254
271
|
return JenkinsJobs(args)
|
|
255
272
|
|
|
256
|
-
def execute(self, args):
|
|
273
|
+
def execute(self, args: Iterable[str]) -> None:
|
|
257
274
|
jjb = self.get_jjb(args)
|
|
258
275
|
with toggle_logger():
|
|
259
276
|
jjb.execute()
|
|
260
277
|
|
|
261
|
-
def modify_logger(self):
|
|
278
|
+
def modify_logger(self) -> None:
|
|
262
279
|
yaml.warnings({"YAMLLoadWarning": False})
|
|
263
280
|
formatter = logging.Formatter("%(levelname)s: %(message)s")
|
|
264
281
|
logger = logging.getLogger()
|
|
265
282
|
logger.handlers[0].setFormatter(formatter)
|
|
266
283
|
|
|
267
|
-
def cleanup(self):
|
|
284
|
+
def cleanup(self) -> None:
|
|
268
285
|
for wd in self.working_dirs.values():
|
|
269
286
|
shutil.rmtree(wd)
|
|
270
287
|
|
|
271
288
|
@retry(exceptions=(JenkinsJobsException))
|
|
272
|
-
def get_jobs(self, wd, name):
|
|
289
|
+
def get_jobs(self, wd: str, name: str) -> list[dict[str, Any]]:
|
|
273
290
|
ini_path = f"{wd}/{name}.ini"
|
|
274
291
|
config_path = f"{wd}/config.yaml"
|
|
275
292
|
|
|
276
293
|
args = ["--conf", ini_path, "test", config_path]
|
|
277
294
|
jjb = self.get_jjb(args)
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
jobs, _ = parser.expandYaml(registry, jjb.options.names)
|
|
295
|
+
roots = Roots(jjb.jjb_config)
|
|
296
|
+
load_files(jjb.jjb_config, roots, [Path(config_path)])
|
|
297
|
+
job_view_data_list = roots.generate_jobs()
|
|
298
|
+
return [job.data for job in job_view_data_list]
|
|
283
299
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
def get_job_webhooks_data(self):
|
|
287
|
-
job_webhooks_data = {}
|
|
300
|
+
def get_job_webhooks_data(self) -> dict[str, list[dict[str, Any]]]:
|
|
301
|
+
job_webhooks_data: dict[str, list[dict[str, Any]]] = {}
|
|
288
302
|
for name, wd in self.working_dirs.items():
|
|
289
303
|
jobs = self.get_jobs(wd, name)
|
|
290
304
|
|
|
@@ -313,7 +327,7 @@ class JJB:
|
|
|
313
327
|
|
|
314
328
|
return job_webhooks_data
|
|
315
329
|
|
|
316
|
-
def get_repos(self):
|
|
330
|
+
def get_repos(self) -> set[str]:
|
|
317
331
|
repos = set()
|
|
318
332
|
for name, wd in self.working_dirs.items():
|
|
319
333
|
jobs = self.get_jobs(wd, name)
|
|
@@ -325,7 +339,7 @@ class JJB:
|
|
|
325
339
|
logging.debug(f"missing github url: {job_name}")
|
|
326
340
|
return repos
|
|
327
341
|
|
|
328
|
-
def get_admins(self):
|
|
342
|
+
def get_admins(self) -> set[str]:
|
|
329
343
|
admins = set()
|
|
330
344
|
for name, wd in self.working_dirs.items():
|
|
331
345
|
jobs = self.get_jobs(wd, name)
|
|
@@ -340,15 +354,17 @@ class JJB:
|
|
|
340
354
|
return admins
|
|
341
355
|
|
|
342
356
|
@staticmethod
|
|
343
|
-
def get_repo_url(job):
|
|
357
|
+
def get_repo_url(job: Mapping[str, Any]) -> str:
|
|
344
358
|
repo_url_raw = job["properties"][0]["github"]["url"]
|
|
345
359
|
return repo_url_raw.strip("/").replace(".git", "")
|
|
346
360
|
|
|
347
361
|
@staticmethod
|
|
348
|
-
def get_ref(job:
|
|
362
|
+
def get_ref(job: Mapping[str, Any]) -> str:
|
|
349
363
|
return job["scm"][0]["git"]["branches"][0]
|
|
350
364
|
|
|
351
|
-
def get_all_jobs(
|
|
365
|
+
def get_all_jobs(
|
|
366
|
+
self, job_types: Iterable[str] | None = None, instance_name: str | None = None
|
|
367
|
+
) -> dict[str, list[dict[str, Any]]]:
|
|
352
368
|
if job_types is None:
|
|
353
369
|
job_types = []
|
|
354
370
|
all_jobs: dict[str, list[dict]] = {}
|
|
@@ -366,8 +382,8 @@ class JJB:
|
|
|
366
382
|
|
|
367
383
|
return all_jobs
|
|
368
384
|
|
|
369
|
-
def print_jobs(self, job_name=None):
|
|
370
|
-
all_jobs = {}
|
|
385
|
+
def print_jobs(self, job_name: str | None = None) -> None:
|
|
386
|
+
all_jobs: dict[str, list[dict[str, Any]]] = {}
|
|
371
387
|
found = False
|
|
372
388
|
for name, wd in self.working_dirs.items():
|
|
373
389
|
logging.debug(f"getting jobs from {name}")
|
|
@@ -380,7 +396,7 @@ class JJB:
|
|
|
380
396
|
found = True
|
|
381
397
|
if not found:
|
|
382
398
|
raise ValueError(f"job name {job_name} is not found")
|
|
383
|
-
print(
|
|
399
|
+
print(json_dumps(all_jobs, indent=2))
|
|
384
400
|
|
|
385
401
|
def get_job_by_repo_url(self, repo_url: str, job_type: str) -> dict[str, Any]:
|
|
386
402
|
for jobs in self.get_all_jobs(job_types=[job_type]).values():
|
|
@@ -394,7 +410,7 @@ class JJB:
|
|
|
394
410
|
raise ValueError(f"job with {job_type=} and {repo_url=} not found")
|
|
395
411
|
|
|
396
412
|
@staticmethod
|
|
397
|
-
def get_trigger_phrases_regex(job:
|
|
413
|
+
def get_trigger_phrases_regex(job: Mapping[str, Any]) -> str | None:
|
|
398
414
|
for trigger in job["triggers"]:
|
|
399
415
|
if "gitlab" in trigger:
|
|
400
416
|
return trigger["gitlab"].get("note-regex")
|
|
@@ -403,7 +419,7 @@ class JJB:
|
|
|
403
419
|
return None
|
|
404
420
|
|
|
405
421
|
@staticmethod
|
|
406
|
-
def get_gitlab_webhook_trigger(job:
|
|
422
|
+
def get_gitlab_webhook_trigger(job: Mapping[str, Any]) -> list[str]:
|
|
407
423
|
gitlab_triggers = job["triggers"][0]["gitlab"]
|
|
408
424
|
# pr-check job should be triggered by merge request events
|
|
409
425
|
# and certain comments: [test]|/retest|/lgtm|/lgtm cancel|/hold|/hold cancel
|
|
@@ -3,7 +3,7 @@ import time
|
|
|
3
3
|
from datetime import datetime
|
|
4
4
|
from typing import Protocol, TextIO
|
|
5
5
|
|
|
6
|
-
from kubernetes.client import (
|
|
6
|
+
from kubernetes.client import (
|
|
7
7
|
ApiClient,
|
|
8
8
|
V1Job,
|
|
9
9
|
V1ObjectMeta,
|
|
@@ -100,7 +100,7 @@ class K8sJobController:
|
|
|
100
100
|
"""
|
|
101
101
|
new_cache = {}
|
|
102
102
|
for item in self.oc.get_items(
|
|
103
|
-
kind="Job",
|
|
103
|
+
kind="Job.batch",
|
|
104
104
|
namespace=self.namespace,
|
|
105
105
|
):
|
|
106
106
|
openshift_resource = OpenshiftResource(
|
|
@@ -38,6 +38,8 @@ class JobValidationError(Exception):
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
JOB_GENERATION_ANNOTATION = "qontract-reconcile/job.generation"
|
|
41
|
+
MAX_JOB_NAME_LENGTH = 63
|
|
42
|
+
UNIT_OF_WORK_DIGEST_LENGTH = 10
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
class K8sJob(ABC):
|
|
@@ -72,7 +74,21 @@ class K8sJob(ABC):
|
|
|
72
74
|
"""
|
|
73
75
|
|
|
74
76
|
def name(self) -> str:
|
|
75
|
-
|
|
77
|
+
"""
|
|
78
|
+
Generate the full job name by combining the name prefix with a digest.
|
|
79
|
+
|
|
80
|
+
The name is constructed from the name_prefix (truncated to ensure total
|
|
81
|
+
length compliance) and the unit_of_work_digest. The total length is
|
|
82
|
+
limited to MAX_JOB_NAME_LENGTH (63 characters) to comply with Kubernetes
|
|
83
|
+
naming constraints.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
A unique job name in the format: {name_prefix}-{digest}
|
|
87
|
+
"""
|
|
88
|
+
prefix = self.name_prefix()[
|
|
89
|
+
: MAX_JOB_NAME_LENGTH - UNIT_OF_WORK_DIGEST_LENGTH - 1
|
|
90
|
+
]
|
|
91
|
+
return f"{prefix}-{self.unit_of_work_digest(UNIT_OF_WORK_DIGEST_LENGTH)}"
|
|
76
92
|
|
|
77
93
|
@abstractmethod
|
|
78
94
|
def name_prefix(self) -> str:
|
reconcile/utils/json.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from collections.abc import Callable
|
|
3
|
+
from dataclasses import asdict, is_dataclass
|
|
4
|
+
from datetime import date, datetime
|
|
5
|
+
from decimal import Decimal
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from typing import Any, Literal
|
|
8
|
+
|
|
9
|
+
from pydantic import BaseModel
|
|
10
|
+
from pydantic.main import IncEx
|
|
11
|
+
|
|
12
|
+
JSON_COMPACT_SEPARATORS = (",", ":")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def pydantic_encoder(obj: Any) -> Any:
|
|
16
|
+
if isinstance(obj, BaseModel):
|
|
17
|
+
return obj.model_dump()
|
|
18
|
+
|
|
19
|
+
if is_dataclass(obj):
|
|
20
|
+
return asdict(obj) # type: ignore
|
|
21
|
+
|
|
22
|
+
if isinstance(obj, (datetime, date)):
|
|
23
|
+
return obj.isoformat()
|
|
24
|
+
|
|
25
|
+
if isinstance(obj, Enum):
|
|
26
|
+
return obj.value
|
|
27
|
+
|
|
28
|
+
if isinstance(obj, Decimal):
|
|
29
|
+
return float(obj)
|
|
30
|
+
|
|
31
|
+
raise TypeError(
|
|
32
|
+
f"Object of type '{obj.__class__.__name__}' is not JSON serializable"
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def json_dumps(
|
|
37
|
+
data: Any,
|
|
38
|
+
*,
|
|
39
|
+
compact: bool = False,
|
|
40
|
+
indent: int | None = None,
|
|
41
|
+
cls: type[json.JSONEncoder] | None = None,
|
|
42
|
+
defaults: Callable | None = None,
|
|
43
|
+
# BaseModel dump parameters
|
|
44
|
+
by_alias: bool = True,
|
|
45
|
+
exclude_none: bool = False,
|
|
46
|
+
exclude: IncEx | None = None,
|
|
47
|
+
mode: Literal["json", "python"] = "json",
|
|
48
|
+
) -> str:
|
|
49
|
+
"""
|
|
50
|
+
Serialize `data` to a consistent JSON formatted `str` with dict keys sorted.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
data: The data to serialize.
|
|
54
|
+
compact: If True, use compact separators (no spaces after commas or colons).
|
|
55
|
+
indent: If specified, pretty-print the JSON with this many spaces of indentation.
|
|
56
|
+
cls: A custom JSONEncoder subclass to use for serialization.
|
|
57
|
+
Returns:
|
|
58
|
+
A JSON formatted string.
|
|
59
|
+
"""
|
|
60
|
+
if isinstance(data, BaseModel):
|
|
61
|
+
data = data.model_dump(
|
|
62
|
+
mode=mode, by_alias=by_alias, exclude_none=exclude_none, exclude=exclude
|
|
63
|
+
)
|
|
64
|
+
if mode == "python":
|
|
65
|
+
defaults = pydantic_encoder
|
|
66
|
+
separators = JSON_COMPACT_SEPARATORS if compact else None
|
|
67
|
+
return json.dumps(
|
|
68
|
+
data,
|
|
69
|
+
indent=indent,
|
|
70
|
+
separators=separators,
|
|
71
|
+
sort_keys=True,
|
|
72
|
+
cls=cls,
|
|
73
|
+
default=defaults,
|
|
74
|
+
)
|
reconcile/utils/ldap_client.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from collections import defaultdict
|
|
2
2
|
from collections.abc import Iterable
|
|
3
|
+
from typing import Any, Self
|
|
3
4
|
|
|
4
5
|
from ldap3 import (
|
|
5
6
|
ALL,
|
|
@@ -17,15 +18,15 @@ class LdapClient:
|
|
|
17
18
|
appropriately.
|
|
18
19
|
"""
|
|
19
20
|
|
|
20
|
-
def __init__(self, base_dn: str, connection: Connection):
|
|
21
|
+
def __init__(self, base_dn: str, connection: Connection) -> None:
|
|
21
22
|
self.base_dn = base_dn
|
|
22
23
|
self.connection = connection
|
|
23
24
|
|
|
24
|
-
def __enter__(self):
|
|
25
|
+
def __enter__(self) -> Self:
|
|
25
26
|
self.connection.bind()
|
|
26
27
|
return self
|
|
27
28
|
|
|
28
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
29
|
+
def __exit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None:
|
|
29
30
|
self.connection.unbind()
|
|
30
31
|
|
|
31
32
|
def get_users(self, uids: Iterable[str]) -> set[str]:
|
|
@@ -6,7 +6,9 @@ from collections.abc import Mapping
|
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def state_rm_access_key(
|
|
9
|
+
def state_rm_access_key(
|
|
10
|
+
working_dirs: Mapping[str, str], account: str, user: str
|
|
11
|
+
) -> bool:
|
|
10
12
|
wd = working_dirs[account]
|
|
11
13
|
init_result = subprocess.run(["terraform", "init"], check=False, cwd=wd)
|
|
12
14
|
if init_result.returncode != 0:
|
|
@@ -55,6 +55,8 @@ def resolve_app_interface_membership_source(
|
|
|
55
55
|
|
|
56
56
|
def build_member_list(role: RoleV1) -> list[RoleMember]:
|
|
57
57
|
members: list[RoleMember] = []
|
|
58
|
-
members.extend([RoleUser(**u.
|
|
59
|
-
members.extend([
|
|
58
|
+
members.extend([RoleUser(**u.model_dump()) for u in role.users or []])
|
|
59
|
+
members.extend([
|
|
60
|
+
RoleBot(**b.model_dump()) for b in role.bots or [] if b.org_username
|
|
61
|
+
])
|
|
60
62
|
return members
|
|
@@ -7,7 +7,6 @@ from typing import (
|
|
|
7
7
|
|
|
8
8
|
from pydantic import (
|
|
9
9
|
BaseModel,
|
|
10
|
-
Extra,
|
|
11
10
|
)
|
|
12
11
|
|
|
13
12
|
from reconcile.gql_definitions.fragments.membership_source import (
|
|
@@ -23,7 +22,7 @@ class User(Protocol):
|
|
|
23
22
|
@property
|
|
24
23
|
def org_username(self) -> str: ...
|
|
25
24
|
|
|
26
|
-
def
|
|
25
|
+
def model_dump(self, *, by_alias: bool = False) -> dict[str, Any]: ...
|
|
27
26
|
|
|
28
27
|
|
|
29
28
|
class Bot(Protocol):
|
|
@@ -33,7 +32,7 @@ class Bot(Protocol):
|
|
|
33
32
|
@property
|
|
34
33
|
def org_username(self) -> str | None: ...
|
|
35
34
|
|
|
36
|
-
def
|
|
35
|
+
def model_dump(self, *, by_alias: bool = False) -> dict[str, Any]: ...
|
|
37
36
|
|
|
38
37
|
|
|
39
38
|
class RoleWithMemberships(Protocol):
|
|
@@ -50,33 +49,27 @@ class RoleWithMemberships(Protocol):
|
|
|
50
49
|
def member_sources(self) -> Sequence[RoleMembershipSource] | None: ...
|
|
51
50
|
|
|
52
51
|
|
|
53
|
-
class RoleUser(BaseModel):
|
|
52
|
+
class RoleUser(BaseModel, extra="ignore"):
|
|
54
53
|
name: str
|
|
55
54
|
org_username: str
|
|
56
|
-
github_username: str | None
|
|
57
|
-
quay_username: str | None
|
|
58
|
-
pagerduty_username: str | None
|
|
59
|
-
aws_username: str | None
|
|
60
|
-
cloudflare_user: str | None
|
|
61
|
-
public_gpg_key: str | None
|
|
55
|
+
github_username: str | None = None
|
|
56
|
+
quay_username: str | None = None
|
|
57
|
+
pagerduty_username: str | None = None
|
|
58
|
+
aws_username: str | None = None
|
|
59
|
+
cloudflare_user: str | None = None
|
|
60
|
+
public_gpg_key: str | None = None
|
|
62
61
|
tag_on_cluster_updates: bool | None = False
|
|
63
62
|
tag_on_merge_requests: bool | None = False
|
|
64
63
|
|
|
65
|
-
class Config:
|
|
66
|
-
extra = Extra.ignore
|
|
67
64
|
|
|
68
|
-
|
|
69
|
-
class RoleBot(BaseModel):
|
|
65
|
+
class RoleBot(BaseModel, extra="ignore"):
|
|
70
66
|
name: str
|
|
71
|
-
description: str | None
|
|
72
|
-
org_username: str | None
|
|
73
|
-
github_username: str | None
|
|
74
|
-
gitlab_username: str | None
|
|
75
|
-
openshift_serviceaccount: str | None
|
|
76
|
-
quay_username: str | None
|
|
77
|
-
|
|
78
|
-
class Config:
|
|
79
|
-
extra = Extra.ignore
|
|
67
|
+
description: str | None = None
|
|
68
|
+
org_username: str | None = None
|
|
69
|
+
github_username: str | None = None
|
|
70
|
+
gitlab_username: str | None = None
|
|
71
|
+
openshift_serviceaccount: str | None = None
|
|
72
|
+
quay_username: str | None = None
|
|
80
73
|
|
|
81
74
|
|
|
82
75
|
RoleMember = RoleUser | RoleBot
|
|
@@ -98,8 +98,10 @@ def resolve_role_members(
|
|
|
98
98
|
members: list[RoleMember] = []
|
|
99
99
|
|
|
100
100
|
# bring in the local users and bots ...
|
|
101
|
-
members.extend(RoleUser(**u.
|
|
102
|
-
members.extend(
|
|
101
|
+
members.extend(RoleUser(**u.model_dump()) for u in r.users or [])
|
|
102
|
+
members.extend(
|
|
103
|
+
RoleBot(**b.model_dump()) for b in r.bots or [] if b.org_username
|
|
104
|
+
)
|
|
103
105
|
|
|
104
106
|
# ... and enhance with the ones from member sources
|
|
105
107
|
for ms in r.member_sources or []:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from abc import abstractmethod
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import Any,
|
|
4
|
+
from typing import Any, TypeVar
|
|
5
5
|
|
|
6
6
|
from gitlab.v4.objects import ProjectMergeRequest
|
|
7
7
|
from pydantic import BaseModel
|
|
@@ -17,12 +17,12 @@ T = TypeVar("T", bound=BaseModel)
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
@dataclass
|
|
20
|
-
class OpenMergeRequest
|
|
20
|
+
class OpenMergeRequest[T: BaseModel]:
|
|
21
21
|
raw: ProjectMergeRequest
|
|
22
22
|
mr_info: T
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
class MergeRequestManagerBase
|
|
25
|
+
class MergeRequestManagerBase[T: BaseModel]:
|
|
26
26
|
""" """
|
|
27
27
|
|
|
28
28
|
def __init__(self, vcs: VCS, parser: Parser, mr_label: str):
|
|
@@ -42,7 +42,7 @@ class MergeRequestManagerBase(Generic[T]):
|
|
|
42
42
|
expected_data: dict[str, Any],
|
|
43
43
|
) -> OpenMergeRequest | None:
|
|
44
44
|
for mr in self._open_mrs:
|
|
45
|
-
mr_info_dict = mr.mr_info.
|
|
45
|
+
mr_info_dict = mr.mr_info.model_dump()
|
|
46
46
|
if all(mr_info_dict.get(k) == expected_data.get(k) for k in expected_data):
|
|
47
47
|
return mr
|
|
48
48
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import re
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import TypeVar
|
|
3
3
|
|
|
4
4
|
from pydantic import BaseModel
|
|
5
5
|
|
|
@@ -17,7 +17,7 @@ class ParserVersionError(Exception):
|
|
|
17
17
|
T = TypeVar("T", bound=BaseModel)
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class Parser
|
|
20
|
+
class Parser[T: BaseModel]:
|
|
21
21
|
"""This class is only concerned with parsing an MR description rendered by the Renderer."""
|
|
22
22
|
|
|
23
23
|
def __init__(
|
|
@@ -60,8 +60,8 @@ class Parser(Generic[T]):
|
|
|
60
60
|
|
|
61
61
|
if self.expected_version != self._find_by_name(self.version_ref, parts[1]):
|
|
62
62
|
raise ParserVersionError("Version is outdated")
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
self.klass, self._data_from_description(parts[1]), use_defaults=False
|
|
66
|
-
)
|
|
63
|
+
data = data_default_none(
|
|
64
|
+
self.klass, self._data_from_description(parts[1]), use_defaults=False
|
|
67
65
|
)
|
|
66
|
+
assert isinstance(data, dict)
|
|
67
|
+
return self.klass(**data)
|
reconcile/utils/metrics.py
CHANGED
|
@@ -144,7 +144,7 @@ class GaugeMetric(BaseMetric):
|
|
|
144
144
|
|
|
145
145
|
@classmethod
|
|
146
146
|
def metric_family(cls) -> GaugeMetricFamily:
|
|
147
|
-
labels = [f.alias for f in cls.
|
|
147
|
+
labels = [f.alias or name for name, f in cls.model_fields.items()]
|
|
148
148
|
return GaugeMetricFamily(cls.name(), cls.__doc__ or "", labels=labels)
|
|
149
149
|
|
|
150
150
|
@classmethod
|
|
@@ -167,7 +167,7 @@ class CounterMetric(BaseMetric):
|
|
|
167
167
|
|
|
168
168
|
@classmethod
|
|
169
169
|
def metric_family(cls) -> CounterMetricFamily:
|
|
170
|
-
labels = [f.alias for f in cls.
|
|
170
|
+
labels = [f.alias or name for name, f in cls.model_fields.items()]
|
|
171
171
|
return CounterMetricFamily(cls.name(), cls.__doc__ or "", labels=labels)
|
|
172
172
|
|
|
173
173
|
@classmethod
|
|
@@ -198,7 +198,7 @@ class MetricsContainer:
|
|
|
198
198
|
"""
|
|
199
199
|
Sets the value of the given gauge metric to the given value.
|
|
200
200
|
"""
|
|
201
|
-
label_values = tuple(metric.
|
|
201
|
+
label_values = tuple(metric.model_dump(by_alias=True).values())
|
|
202
202
|
self._gauges[metric.__class__][label_values] = value
|
|
203
203
|
|
|
204
204
|
def set_info(self, metric: InfoMetric) -> None:
|
|
@@ -213,7 +213,7 @@ class MetricsContainer:
|
|
|
213
213
|
Increases the value of the given counter by the given amount.
|
|
214
214
|
"""
|
|
215
215
|
# all label values need to be strings, so lets convert them
|
|
216
|
-
label_values = tuple(str(v) for v in counter.
|
|
216
|
+
label_values = tuple(str(v) for v in counter.model_dump(by_alias=True).values())
|
|
217
217
|
current_value = self._counters[counter.__class__].get(label_values) or 0
|
|
218
218
|
self._counters[counter.__class__][label_values] = current_value + by
|
|
219
219
|
|
|
@@ -270,7 +270,7 @@ class MetricsContainer:
|
|
|
270
270
|
(
|
|
271
271
|
metric_class(**{
|
|
272
272
|
key: labels[i]
|
|
273
|
-
for i, key in enumerate(metric_class.
|
|
273
|
+
for i, key in enumerate(metric_class.model_fields.keys())
|
|
274
274
|
}),
|
|
275
275
|
value,
|
|
276
276
|
)
|