qontract-reconcile 0.10.2.dev394__py3-none-any.whl → 0.10.2.dev414__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/METADATA +4 -3
- {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/RECORD +308 -308
- reconcile/acs_rbac.py +2 -2
- reconcile/aus/advanced_upgrade_service.py +15 -12
- reconcile/aus/base.py +9 -13
- reconcile/aus/cluster_version_data.py +15 -5
- reconcile/aus/models.py +1 -1
- reconcile/automated_actions/config/integration.py +15 -3
- reconcile/aws_account_manager/integration.py +6 -6
- reconcile/aws_account_manager/reconciler.py +3 -3
- reconcile/aws_ami_cleanup/integration.py +2 -5
- reconcile/aws_ami_share.py +69 -62
- reconcile/aws_saml_idp/integration.py +5 -3
- reconcile/aws_saml_roles/integration.py +23 -22
- reconcile/aws_version_sync/integration.py +6 -12
- reconcile/change_owners/bundle.py +3 -3
- reconcile/change_owners/change_log_tracking.py +3 -2
- reconcile/change_owners/change_owners.py +1 -1
- reconcile/dashdotdb_dora.py +1 -1
- reconcile/dashdotdb_slo.py +1 -1
- reconcile/database_access_manager.py +8 -9
- 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 +1 -1
- reconcile/external_resources/integration.py +1 -1
- reconcile/external_resources/manager.py +19 -7
- reconcile/external_resources/metrics.py +1 -1
- reconcile/external_resources/model.py +6 -6
- reconcile/external_resources/reconciler.py +7 -4
- reconcile/external_resources/secrets_sync.py +2 -2
- reconcile/external_resources/state.py +56 -14
- reconcile/fleet_labeler/integration.py +1 -1
- reconcile/gcp_image_mirror.py +2 -2
- reconcile/github_org.py +1 -1
- reconcile/github_owners.py +4 -0
- reconcile/gitlab_members.py +6 -12
- reconcile/gitlab_permissions.py +8 -12
- reconcile/glitchtip_project_alerts/integration.py +3 -1
- reconcile/gql_definitions/acs/acs_instances.py +5 -5
- reconcile/gql_definitions/acs/acs_policies.py +5 -5
- reconcile/gql_definitions/acs/acs_rbac.py +5 -5
- reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +5 -5
- reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +5 -5
- reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py +5 -5
- reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py +5 -5
- reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py +5 -5
- reconcile/gql_definitions/automated_actions/instance.py +46 -7
- reconcile/gql_definitions/aws_account_manager/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_ami_cleanup/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_cloudwatch_log_retention/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_saml_idp/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_saml_roles/aws_accounts.py +5 -5
- reconcile/gql_definitions/aws_saml_roles/roles.py +5 -5
- reconcile/gql_definitions/aws_version_sync/clusters.py +5 -5
- reconcile/gql_definitions/aws_version_sync/namespaces.py +5 -5
- reconcile/gql_definitions/change_owners/queries/change_types.py +5 -5
- reconcile/gql_definitions/change_owners/queries/self_service_roles.py +5 -5
- reconcile/gql_definitions/cluster_auth_rhidp/clusters.py +5 -5
- reconcile/gql_definitions/common/alerting_services_settings.py +5 -5
- reconcile/gql_definitions/common/app_code_component_repos.py +5 -5
- reconcile/gql_definitions/common/app_interface_custom_messages.py +5 -5
- reconcile/gql_definitions/common/app_interface_dms_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_repo_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_roles.py +5 -5
- reconcile/gql_definitions/common/app_interface_state_settings.py +5 -5
- reconcile/gql_definitions/common/app_interface_vault_settings.py +5 -5
- reconcile/gql_definitions/common/app_quay_repos_escalation_policies.py +5 -5
- reconcile/gql_definitions/common/apps.py +5 -5
- reconcile/gql_definitions/common/aws_vpc_requests.py +5 -5
- reconcile/gql_definitions/common/aws_vpcs.py +5 -5
- reconcile/gql_definitions/common/clusters.py +5 -5
- reconcile/gql_definitions/common/clusters_minimal.py +5 -5
- reconcile/gql_definitions/common/clusters_with_dms.py +5 -5
- reconcile/gql_definitions/common/clusters_with_peering.py +5 -5
- reconcile/gql_definitions/common/github_orgs.py +5 -5
- reconcile/gql_definitions/common/jira_settings.py +5 -5
- reconcile/gql_definitions/common/jiralert_settings.py +5 -5
- reconcile/gql_definitions/common/ldap_settings.py +5 -5
- reconcile/gql_definitions/common/namespaces.py +5 -5
- reconcile/gql_definitions/common/namespaces_minimal.py +7 -5
- reconcile/gql_definitions/common/ocm_env_telemeter.py +5 -5
- reconcile/gql_definitions/common/ocm_environments.py +5 -5
- reconcile/gql_definitions/common/pagerduty_instances.py +5 -5
- reconcile/gql_definitions/common/pgp_reencryption_settings.py +5 -5
- reconcile/gql_definitions/common/pipeline_providers.py +5 -5
- reconcile/gql_definitions/common/quay_instances.py +5 -5
- reconcile/gql_definitions/common/quay_orgs.py +5 -5
- reconcile/gql_definitions/common/reserved_networks.py +5 -5
- reconcile/gql_definitions/common/rhcs_provider_settings.py +5 -5
- reconcile/gql_definitions/common/saas_files.py +5 -5
- reconcile/gql_definitions/common/saas_target_namespaces.py +5 -5
- reconcile/gql_definitions/common/saasherder_settings.py +5 -5
- reconcile/gql_definitions/common/slack_workspaces.py +5 -5
- reconcile/gql_definitions/common/smtp_client_settings.py +5 -5
- reconcile/gql_definitions/common/state_aws_account.py +5 -5
- reconcile/gql_definitions/common/users.py +5 -5
- reconcile/gql_definitions/common/users_with_paths.py +5 -5
- reconcile/gql_definitions/cost_report/app_names.py +5 -5
- reconcile/gql_definitions/cost_report/cost_namespaces.py +5 -5
- reconcile/gql_definitions/cost_report/settings.py +5 -5
- reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py +5 -5
- reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py +5 -5
- reconcile/gql_definitions/dynatrace_token_provider/token_specs.py +5 -5
- reconcile/gql_definitions/email_sender/apps.py +5 -5
- reconcile/gql_definitions/email_sender/emails.py +5 -5
- reconcile/gql_definitions/email_sender/users.py +5 -5
- reconcile/gql_definitions/endpoints_discovery/apps.py +5 -5
- reconcile/gql_definitions/external_resources/aws_accounts.py +5 -5
- reconcile/gql_definitions/external_resources/external_resources_modules.py +5 -5
- reconcile/gql_definitions/external_resources/external_resources_namespaces.py +5 -5
- reconcile/gql_definitions/external_resources/external_resources_settings.py +5 -5
- reconcile/gql_definitions/external_resources/fragments/external_resources_module_overrides.py +5 -5
- reconcile/gql_definitions/fleet_labeler/fleet_labels.py +5 -5
- reconcile/gql_definitions/fragments/aus_organization.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_common.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_managed.py +5 -5
- reconcile/gql_definitions/fragments/aws_account_sso.py +5 -5
- reconcile/gql_definitions/fragments/aws_infra_management_account.py +5 -5
- reconcile/gql_definitions/fragments/aws_organization.py +5 -5
- reconcile/gql_definitions/fragments/aws_vpc.py +5 -5
- reconcile/gql_definitions/fragments/aws_vpc_request.py +5 -5
- reconcile/gql_definitions/fragments/container_image_mirror.py +5 -5
- reconcile/gql_definitions/fragments/deploy_resources.py +5 -5
- reconcile/gql_definitions/fragments/disable.py +5 -5
- reconcile/gql_definitions/fragments/email_service.py +5 -5
- reconcile/gql_definitions/fragments/email_user.py +5 -5
- reconcile/gql_definitions/fragments/jumphost_common_fields.py +5 -5
- reconcile/gql_definitions/fragments/membership_source.py +5 -5
- reconcile/gql_definitions/fragments/minimal_ocm_organization.py +5 -5
- reconcile/gql_definitions/fragments/oc_connection_cluster.py +5 -5
- reconcile/gql_definitions/fragments/ocm_environment.py +5 -5
- reconcile/gql_definitions/fragments/pipeline_provider_retention.py +5 -5
- reconcile/gql_definitions/fragments/prometheus_instance.py +5 -5
- reconcile/gql_definitions/fragments/resource_limits_requirements.py +5 -5
- reconcile/gql_definitions/fragments/resource_requests_requirements.py +5 -5
- reconcile/gql_definitions/fragments/resource_values.py +5 -5
- reconcile/gql_definitions/fragments/saas_slo_document.py +5 -5
- reconcile/gql_definitions/fragments/saas_target_namespace.py +5 -5
- reconcile/gql_definitions/fragments/serviceaccount_token.py +5 -5
- reconcile/gql_definitions/fragments/terraform_state.py +5 -5
- reconcile/gql_definitions/fragments/upgrade_policy.py +5 -5
- reconcile/gql_definitions/fragments/user.py +5 -5
- reconcile/gql_definitions/fragments/vault_secret.py +5 -5
- reconcile/gql_definitions/gcp/gcp_docker_repos.py +5 -5
- reconcile/gql_definitions/gcp/gcp_projects.py +5 -5
- reconcile/gql_definitions/gitlab_members/gitlab_instances.py +5 -5
- reconcile/gql_definitions/gitlab_members/permissions.py +5 -5
- reconcile/gql_definitions/glitchtip/glitchtip_instance.py +5 -5
- reconcile/gql_definitions/glitchtip/glitchtip_project.py +5 -5
- reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +5 -5
- reconcile/gql_definitions/integrations/integrations.py +5 -5
- reconcile/gql_definitions/introspection.json +231 -0
- reconcile/gql_definitions/jenkins_configs/jenkins_configs.py +5 -5
- reconcile/gql_definitions/jenkins_configs/jenkins_instances.py +5 -5
- reconcile/gql_definitions/jira/jira_servers.py +5 -5
- reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py +5 -5
- reconcile/gql_definitions/jumphosts/jumphosts.py +5 -5
- reconcile/gql_definitions/ldap_groups/roles.py +5 -5
- reconcile/gql_definitions/ldap_groups/settings.py +5 -5
- reconcile/gql_definitions/maintenance/maintenances.py +5 -5
- reconcile/gql_definitions/membershipsources/roles.py +5 -5
- reconcile/gql_definitions/ocm_labels/clusters.py +5 -5
- reconcile/gql_definitions/ocm_labels/organizations.py +5 -5
- reconcile/gql_definitions/openshift_cluster_bots/clusters.py +5 -5
- reconcile/gql_definitions/openshift_groups/managed_groups.py +5 -5
- reconcile/gql_definitions/openshift_groups/managed_roles.py +5 -5
- reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py +5 -5
- reconcile/gql_definitions/quay_membership/quay_membership.py +5 -5
- reconcile/gql_definitions/rhcs/certs.py +5 -5
- reconcile/gql_definitions/rhidp/organizations.py +5 -5
- reconcile/gql_definitions/service_dependencies/jenkins_instance_fragment.py +5 -5
- reconcile/gql_definitions/service_dependencies/service_dependencies.py +5 -5
- reconcile/gql_definitions/sharding/aws_accounts.py +5 -5
- reconcile/gql_definitions/sharding/ocm_organization.py +5 -5
- reconcile/gql_definitions/skupper_network/site_controller_template.py +5 -5
- reconcile/gql_definitions/skupper_network/skupper_networks.py +5 -5
- reconcile/gql_definitions/slack_usergroups/clusters.py +5 -5
- reconcile/gql_definitions/slack_usergroups/permissions.py +5 -5
- reconcile/gql_definitions/slack_usergroups/users.py +5 -5
- reconcile/gql_definitions/slo_documents/slo_documents.py +5 -5
- reconcile/gql_definitions/status_board/status_board.py +5 -5
- reconcile/gql_definitions/statuspage/statuspages.py +5 -5
- reconcile/gql_definitions/templating/template_collection.py +5 -5
- reconcile/gql_definitions/templating/templates.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_dns/app_interface_cloudflare_dns_settings.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_dns/terraform_cloudflare_zones.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_accounts.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_resources/terraform_cloudflare_resources.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_users/app_interface_setting_cloudflare_and_vault.py +5 -5
- reconcile/gql_definitions/terraform_cloudflare_users/terraform_cloudflare_roles.py +5 -5
- reconcile/gql_definitions/terraform_init/aws_accounts.py +5 -5
- reconcile/gql_definitions/terraform_repo/terraform_repo.py +5 -5
- reconcile/gql_definitions/terraform_resources/database_access_manager.py +5 -5
- reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +5 -5
- reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py +5 -5
- reconcile/gql_definitions/unleash_feature_toggles/feature_toggles.py +5 -5
- reconcile/gql_definitions/vault_instances/vault_instances.py +5 -5
- reconcile/gql_definitions/vault_policies/vault_policies.py +5 -5
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator.py +5 -5
- reconcile/gql_definitions/vpc_peerings_validator/vpc_peerings_validator_peered_cluster_fragment.py +5 -5
- reconcile/integrations_manager.py +3 -3
- reconcile/jenkins_worker_fleets.py +9 -8
- reconcile/jira_permissions_validator.py +2 -2
- reconcile/ldap_groups/integration.py +1 -1
- reconcile/ocm/types.py +35 -57
- reconcile/ocm_aws_infrastructure_access.py +1 -1
- reconcile/ocm_clusters.py +4 -4
- reconcile/ocm_labels/integration.py +3 -2
- reconcile/ocm_machine_pools.py +23 -23
- reconcile/openshift_base.py +53 -2
- reconcile/openshift_cluster_bots.py +1 -1
- reconcile/openshift_namespace_labels.py +1 -1
- reconcile/openshift_namespaces.py +97 -101
- reconcile/openshift_resources_base.py +6 -2
- reconcile/openshift_rhcs_certs.py +5 -5
- reconcile/openshift_rolebindings.py +7 -11
- reconcile/openshift_saas_deploy.py +4 -5
- reconcile/openshift_saas_deploy_change_tester.py +9 -7
- reconcile/openshift_serviceaccount_tokens.py +2 -2
- reconcile/openshift_upgrade_watcher.py +1 -1
- reconcile/oum/labelset.py +5 -3
- reconcile/oum/models.py +1 -4
- reconcile/prometheus_rules_tester/integration.py +3 -3
- reconcile/quay_mirror.py +1 -1
- reconcile/queries.py +6 -0
- reconcile/rhidp/common.py +3 -5
- reconcile/rhidp/sso_client/base.py +1 -1
- reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
- reconcile/skupper_network/integration.py +2 -2
- reconcile/slack_usergroups.py +31 -11
- reconcile/status_board.py +6 -6
- reconcile/statuspage/atlassian.py +7 -7
- reconcile/statuspage/page.py +4 -9
- reconcile/templating/lib/rendering.py +3 -3
- reconcile/templating/renderer.py +2 -2
- reconcile/terraform_cloudflare_dns.py +3 -3
- reconcile/terraform_cloudflare_resources.py +5 -5
- reconcile/terraform_cloudflare_users.py +3 -2
- reconcile/terraform_init/integration.py +2 -2
- reconcile/terraform_repo.py +16 -12
- reconcile/terraform_resources.py +6 -6
- reconcile/terraform_tgw_attachments.py +20 -18
- reconcile/terraform_vpc_resources/integration.py +3 -1
- 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 +11 -11
- reconcile/typed_queries/status_board.py +2 -2
- reconcile/unleash_feature_toggles/integration.py +4 -2
- reconcile/utils/acs/base.py +6 -3
- reconcile/utils/acs/policies.py +2 -2
- reconcile/utils/aws_api.py +51 -20
- reconcile/utils/aws_api_typed/organization.py +4 -2
- reconcile/utils/deadmanssnitch_api.py +1 -1
- reconcile/utils/early_exit_cache.py +8 -10
- reconcile/utils/gitlab_api.py +7 -5
- reconcile/utils/glitchtip/client.py +6 -2
- reconcile/utils/glitchtip/models.py +25 -28
- reconcile/utils/gql.py +4 -7
- reconcile/utils/instrumented_wrappers.py +1 -1
- reconcile/utils/internal_groups/client.py +2 -2
- reconcile/utils/internal_groups/models.py +8 -17
- reconcile/utils/jinja2/utils.py +2 -5
- reconcile/utils/jobcontroller/controller.py +1 -1
- reconcile/utils/jobcontroller/models.py +17 -1
- reconcile/utils/json.py +39 -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 +1 -1
- reconcile/utils/merge_request_manager/parser.py +4 -4
- reconcile/utils/metrics.py +5 -5
- reconcile/utils/models.py +304 -82
- reconcile/utils/mr/notificator.py +1 -1
- reconcile/utils/mr/user_maintenance.py +3 -2
- reconcile/utils/oc.py +112 -92
- reconcile/utils/ocm/addons.py +0 -1
- reconcile/utils/ocm/base.py +17 -20
- reconcile/utils/ocm/cluster_groups.py +1 -1
- reconcile/utils/ocm/identity_providers.py +2 -2
- reconcile/utils/ocm/labels.py +1 -1
- reconcile/utils/ocm/products.py +8 -8
- reconcile/utils/ocm/service_log.py +1 -1
- reconcile/utils/ocm/sre_capability_labels.py +20 -13
- reconcile/utils/openshift_resource.py +5 -0
- reconcile/utils/pagerduty_api.py +5 -2
- reconcile/utils/promotion_state.py +6 -11
- reconcile/utils/raw_github_api.py +1 -1
- reconcile/utils/rhcsv2_certs.py +1 -4
- reconcile/utils/runtime/integration.py +1 -1
- reconcile/utils/saasherder/interfaces.py +13 -20
- reconcile/utils/saasherder/models.py +23 -20
- reconcile/utils/saasherder/saasherder.py +26 -17
- reconcile/utils/slack_api.py +2 -2
- reconcile/utils/structs.py +1 -1
- reconcile/utils/terraform_client.py +1 -1
- reconcile/utils/terrascript_aws_client.py +47 -43
- reconcile/utils/unleash/server.py +2 -8
- reconcile/utils/vault.py +4 -11
- reconcile/utils/vcs.py +8 -8
- reconcile/vault_replication.py +1 -1
- tools/cli_commands/cost_report/cost_management_api.py +3 -3
- tools/cli_commands/cost_report/view.py +7 -6
- tools/cli_commands/erv2.py +3 -1
- tools/qontract_cli.py +6 -5
- tools/template_validation.py +3 -1
- {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev394.dist-info → qontract_reconcile-0.10.2.dev414.dist-info}/entry_points.txt +0 -0
|
@@ -76,7 +76,7 @@ class RawGithubApi:
|
|
|
76
76
|
if login is not None
|
|
77
77
|
]
|
|
78
78
|
|
|
79
|
-
def team_invitations(self, org_id: str, team_id: str) -> list[str]:
|
|
79
|
+
def team_invitations(self, org_id: str | int, team_id: str | int) -> list[str]:
|
|
80
80
|
invitations = self.query(f"/organizations/{org_id}/team/{team_id}/invitations")
|
|
81
81
|
|
|
82
82
|
return [
|
reconcile/utils/rhcsv2_certs.py
CHANGED
|
@@ -9,15 +9,12 @@ from cryptography.x509.oid import NameOID
|
|
|
9
9
|
from pydantic import BaseModel, Field
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
class RhcsV2Cert(BaseModel):
|
|
12
|
+
class RhcsV2Cert(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
13
13
|
certificate: str = Field(alias="tls.crt")
|
|
14
14
|
private_key: str = Field(alias="tls.key")
|
|
15
15
|
ca_cert: str = Field(alias="ca.crt")
|
|
16
16
|
expiration_timestamp: int
|
|
17
17
|
|
|
18
|
-
class Config:
|
|
19
|
-
allow_population_by_field_name = True
|
|
20
|
-
|
|
21
18
|
|
|
22
19
|
def extract_cert(text: str) -> re.Match:
|
|
23
20
|
# The CA webform returns an HTML page with inline JS that builds an array of “outputList”
|
|
@@ -119,7 +119,7 @@ class PydanticRunParams(RunParams, BaseModel):
|
|
|
119
119
|
def copy_and_update(
|
|
120
120
|
self: PydanticRunParamsSelfTypeVar, update: dict[str, Any]
|
|
121
121
|
) -> PydanticRunParamsSelfTypeVar:
|
|
122
|
-
return self.
|
|
122
|
+
return self.model_copy(update=update)
|
|
123
123
|
|
|
124
124
|
def get(self, field: str) -> Any:
|
|
125
125
|
return getattr(self, field)
|
|
@@ -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,10 +7,7 @@ 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,
|
|
@@ -207,7 +204,12 @@ TriggerSpecUnion = (
|
|
|
207
204
|
)
|
|
208
205
|
|
|
209
206
|
|
|
210
|
-
class Namespace(
|
|
207
|
+
class Namespace(
|
|
208
|
+
BaseModel,
|
|
209
|
+
validate_by_name=True,
|
|
210
|
+
validate_by_alias=True,
|
|
211
|
+
arbitrary_types_allowed=True,
|
|
212
|
+
):
|
|
211
213
|
name: str
|
|
212
214
|
environment: SaasEnvironment
|
|
213
215
|
app: SaasApp
|
|
@@ -217,29 +219,19 @@ class Namespace(BaseModel):
|
|
|
217
219
|
..., alias="managedResourceNames"
|
|
218
220
|
)
|
|
219
221
|
|
|
220
|
-
class Config:
|
|
221
|
-
arbitrary_types_allowed = True
|
|
222
|
-
allow_population_by_field_name = True
|
|
223
|
-
|
|
224
222
|
|
|
225
|
-
class PromotionChannelData(BaseModel):
|
|
223
|
+
class PromotionChannelData(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
226
224
|
q_type: str = Field(..., alias="type")
|
|
227
225
|
|
|
228
|
-
class Config:
|
|
229
|
-
allow_population_by_field_name = True
|
|
230
226
|
|
|
231
|
-
|
|
232
|
-
class ParentSaasPromotion(BaseModel):
|
|
227
|
+
class ParentSaasPromotion(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
233
228
|
q_type: str = Field(..., alias="type")
|
|
234
|
-
parent_saas: str | None
|
|
235
|
-
target_config_hash: str | None
|
|
236
|
-
|
|
237
|
-
class Config:
|
|
238
|
-
allow_population_by_field_name = True
|
|
229
|
+
parent_saas: str | None = None
|
|
230
|
+
target_config_hash: str | None = None
|
|
239
231
|
|
|
240
232
|
|
|
241
233
|
class PromotionData(BaseModel):
|
|
242
|
-
channel: str | None
|
|
234
|
+
channel: str | None = None
|
|
243
235
|
data: list[ParentSaasPromotion | PromotionChannelData] | None = None
|
|
244
236
|
|
|
245
237
|
|
|
@@ -264,6 +256,17 @@ class Promotion(BaseModel):
|
|
|
264
256
|
saas_file_paths: list[str] | None = None
|
|
265
257
|
target_paths: list[str] | None = None
|
|
266
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
|
+
|
|
267
270
|
|
|
268
271
|
@dataclass
|
|
269
272
|
class ImageAuth:
|
|
@@ -765,7 +765,8 @@ class SaasHerder:
|
|
|
765
765
|
case "gitlab":
|
|
766
766
|
if not self.gitlab:
|
|
767
767
|
raise Exception("gitlab is not initialized")
|
|
768
|
-
project
|
|
768
|
+
if not (project := self.gitlab.get_project(url)):
|
|
769
|
+
raise Exception(f"Could not find gitlab project for {url}")
|
|
769
770
|
content = self.gitlab.get_raw_file(
|
|
770
771
|
project=project,
|
|
771
772
|
path=path,
|
|
@@ -801,7 +802,8 @@ class SaasHerder:
|
|
|
801
802
|
case "gitlab":
|
|
802
803
|
if not self.gitlab:
|
|
803
804
|
raise Exception("gitlab is not initialized")
|
|
804
|
-
project
|
|
805
|
+
if not (project := self.gitlab.get_project(url)):
|
|
806
|
+
raise Exception(f"Could not find gitlab project for {url}")
|
|
805
807
|
dir_contents = self.gitlab.get_directory_contents(
|
|
806
808
|
project,
|
|
807
809
|
ref=commit_sha,
|
|
@@ -826,7 +828,8 @@ class SaasHerder:
|
|
|
826
828
|
case "gitlab":
|
|
827
829
|
if not self.gitlab:
|
|
828
830
|
raise Exception("gitlab is not initialized")
|
|
829
|
-
project
|
|
831
|
+
if not (project := self.gitlab.get_project(url)):
|
|
832
|
+
raise Exception(f"Could not find gitlab project for {url}")
|
|
830
833
|
commits = project.commits.list(ref_name=ref, per_page=1, page=1)
|
|
831
834
|
return commits[0].id
|
|
832
835
|
case _:
|
|
@@ -1179,13 +1182,13 @@ class SaasHerder:
|
|
|
1179
1182
|
images_list = threaded.run(
|
|
1180
1183
|
self._collect_images, resources, self.available_thread_pool_size
|
|
1181
1184
|
)
|
|
1182
|
-
|
|
1183
|
-
self.images.update(
|
|
1184
|
-
if not
|
|
1185
|
+
images_set = set(itertools.chain.from_iterable(images_list))
|
|
1186
|
+
self.images.update(images_set)
|
|
1187
|
+
if not images_set:
|
|
1185
1188
|
return False # no errors
|
|
1186
1189
|
images = threaded.run(
|
|
1187
1190
|
self._get_image,
|
|
1188
|
-
|
|
1191
|
+
images_set,
|
|
1189
1192
|
self.available_thread_pool_size,
|
|
1190
1193
|
image_patterns=spec.image_patterns,
|
|
1191
1194
|
image_auth=spec.image_auth,
|
|
@@ -1250,7 +1253,9 @@ class SaasHerder:
|
|
|
1250
1253
|
self.saas_files,
|
|
1251
1254
|
self.thread_pool_size,
|
|
1252
1255
|
)
|
|
1253
|
-
desired_state_specs = list(
|
|
1256
|
+
desired_state_specs: list[TargetSpec] = list(
|
|
1257
|
+
itertools.chain.from_iterable(results)
|
|
1258
|
+
)
|
|
1254
1259
|
promotions = threaded.run(
|
|
1255
1260
|
self.populate_desired_state_saas_file,
|
|
1256
1261
|
desired_state_specs,
|
|
@@ -1898,21 +1903,23 @@ class SaasHerder:
|
|
|
1898
1903
|
name=target.name,
|
|
1899
1904
|
ref=target.ref,
|
|
1900
1905
|
promotion=(
|
|
1901
|
-
target.promotion.
|
|
1906
|
+
target.promotion.model_dump(by_alias=True) if target.promotion else None
|
|
1902
1907
|
),
|
|
1903
1908
|
secretParameters=(
|
|
1904
|
-
[p.
|
|
1909
|
+
[p.model_dump(by_alias=True) for p in target.secret_parameters]
|
|
1905
1910
|
if target.secret_parameters
|
|
1906
1911
|
else None
|
|
1907
1912
|
),
|
|
1908
1913
|
slos=(
|
|
1909
|
-
[slo.
|
|
1914
|
+
[slo.model_dump(by_alias=True) for slo in target.slos]
|
|
1910
1915
|
if target.slos
|
|
1911
1916
|
else None
|
|
1912
1917
|
),
|
|
1913
|
-
upstream=(
|
|
1918
|
+
upstream=(
|
|
1919
|
+
target.upstream.model_dump(by_alias=True) if target.upstream else None
|
|
1920
|
+
),
|
|
1914
1921
|
images=(
|
|
1915
|
-
[i.
|
|
1922
|
+
[i.model_dump(by_alias=True) for i in target.images]
|
|
1916
1923
|
if target.images
|
|
1917
1924
|
else None
|
|
1918
1925
|
),
|
|
@@ -1948,16 +1955,16 @@ class SaasHerder:
|
|
|
1948
1955
|
)
|
|
1949
1956
|
if saas_file.managed_resource_names:
|
|
1950
1957
|
state_content["saas_file_managed_resource_names"] = [
|
|
1951
|
-
m.
|
|
1958
|
+
m.model_dump() for m in saas_file.managed_resource_names
|
|
1952
1959
|
]
|
|
1953
1960
|
# include secret parameters from resource template and saas file
|
|
1954
1961
|
if resource_template.secret_parameters:
|
|
1955
1962
|
state_content["rt_secretparameters"] = [
|
|
1956
|
-
p.
|
|
1963
|
+
p.model_dump() for p in resource_template.secret_parameters
|
|
1957
1964
|
]
|
|
1958
1965
|
if saas_file.secret_parameters:
|
|
1959
1966
|
state_content["saas_file_secretparameters"] = [
|
|
1960
|
-
p.
|
|
1967
|
+
p.model_dump() for p in saas_file.secret_parameters
|
|
1961
1968
|
]
|
|
1962
1969
|
return state_content
|
|
1963
1970
|
|
|
@@ -2240,7 +2247,9 @@ class SaasHerder:
|
|
|
2240
2247
|
for rt in saas_file.resource_templates:
|
|
2241
2248
|
for target in rt.targets:
|
|
2242
2249
|
template_vars = {
|
|
2243
|
-
"resource": {
|
|
2250
|
+
"resource": {
|
|
2251
|
+
"namespace": target.namespace.model_dump(by_alias=True)
|
|
2252
|
+
}
|
|
2244
2253
|
}
|
|
2245
2254
|
if target.parameters:
|
|
2246
2255
|
for param in target.parameters:
|
reconcile/utils/slack_api.py
CHANGED
|
@@ -71,14 +71,14 @@ class HasClientGlobalConfig(Protocol):
|
|
|
71
71
|
max_retries: int | None
|
|
72
72
|
timeout: int | None
|
|
73
73
|
|
|
74
|
-
def
|
|
74
|
+
def model_dump(self) -> dict[str, int | None]: ...
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
class HasClientMethodConfig(Protocol):
|
|
78
78
|
name: str
|
|
79
79
|
args: Any
|
|
80
80
|
|
|
81
|
-
def
|
|
81
|
+
def model_dump(self) -> dict[str, str]: ...
|
|
82
82
|
|
|
83
83
|
|
|
84
84
|
class HasClientConfig(Protocol):
|
reconcile/utils/structs.py
CHANGED
|
@@ -223,7 +223,7 @@ class TerraformClient:
|
|
|
223
223
|
if disable_deletions_detected:
|
|
224
224
|
raise RuntimeError("Terraform plan has disabled deletions detected")
|
|
225
225
|
|
|
226
|
-
@retry(no_retry_exceptions=RdsUpgradeValidationError)
|
|
226
|
+
@retry(no_retry_exceptions=(RdsUpgradeValidationError,))
|
|
227
227
|
def terraform_plan(
|
|
228
228
|
self, spec: TerraformSpec, enable_deletion: bool
|
|
229
229
|
) -> tuple[bool, list[AccountUser], bool]:
|
|
@@ -1058,7 +1058,9 @@ class TerrascriptClient:
|
|
|
1058
1058
|
ignore_changes = (
|
|
1059
1059
|
"all" if "all" in lifecycle.ignore_changes else lifecycle.ignore_changes
|
|
1060
1060
|
)
|
|
1061
|
-
return lifecycle.
|
|
1061
|
+
return lifecycle.model_dump(by_alias=True) | {
|
|
1062
|
+
"ignore_changes": ignore_changes
|
|
1063
|
+
}
|
|
1062
1064
|
return None
|
|
1063
1065
|
|
|
1064
1066
|
def populate_additional_providers(
|
|
@@ -1424,7 +1426,7 @@ class TerrascriptClient:
|
|
|
1424
1426
|
req_account_name = req_account.name
|
|
1425
1427
|
# Accepter's side of the connection - the cluster's account
|
|
1426
1428
|
acc_account = accepter.account
|
|
1427
|
-
acc_alias = self.get_provider_alias(acc_account.
|
|
1429
|
+
acc_alias = self.get_provider_alias(acc_account.model_dump(by_alias=True))
|
|
1428
1430
|
acc_uid = acc_account.uid
|
|
1429
1431
|
if acc_account.assume_role:
|
|
1430
1432
|
acc_uid = awsh.get_account_uid_from_arn(acc_account.assume_role)
|
|
@@ -2210,6 +2212,43 @@ class TerrascriptClient:
|
|
|
2210
2212
|
letters_and_digits = string.ascii_letters + string.digits
|
|
2211
2213
|
return "".join(random.choice(letters_and_digits) for i in range(string_length))
|
|
2212
2214
|
|
|
2215
|
+
@staticmethod
|
|
2216
|
+
def _build_tf_resource_s3_lifecycle_rules(
|
|
2217
|
+
versioning: bool,
|
|
2218
|
+
common_values: Mapping[str, Any],
|
|
2219
|
+
) -> list[dict]:
|
|
2220
|
+
lifecycle_rules = common_values.get("lifecycle_rules") or []
|
|
2221
|
+
if versioning and not any(
|
|
2222
|
+
"noncurrent_version_expiration" in lr for lr in lifecycle_rules
|
|
2223
|
+
):
|
|
2224
|
+
# Add a default noncurrent object expiration rule
|
|
2225
|
+
# if one isn't already set
|
|
2226
|
+
rule = {
|
|
2227
|
+
"id": "expire_noncurrent_versions",
|
|
2228
|
+
"enabled": True,
|
|
2229
|
+
"noncurrent_version_expiration": {"days": 30},
|
|
2230
|
+
"expiration": {"expired_object_delete_marker": True},
|
|
2231
|
+
"abort_incomplete_multipart_upload_days": 3,
|
|
2232
|
+
}
|
|
2233
|
+
lifecycle_rules.append(rule)
|
|
2234
|
+
|
|
2235
|
+
if storage_class := common_values.get("storage_class"):
|
|
2236
|
+
sc = storage_class.upper()
|
|
2237
|
+
days = "1"
|
|
2238
|
+
if sc.endswith("_IA"):
|
|
2239
|
+
# Infrequent Access storage class has minimum 30 days
|
|
2240
|
+
# before transition
|
|
2241
|
+
days = "30"
|
|
2242
|
+
rule = {
|
|
2243
|
+
"id": sc + "_storage_class",
|
|
2244
|
+
"enabled": True,
|
|
2245
|
+
"transition": {"days": days, "storage_class": sc},
|
|
2246
|
+
"noncurrent_version_transition": {"days": days, "storage_class": sc},
|
|
2247
|
+
}
|
|
2248
|
+
lifecycle_rules.append(rule)
|
|
2249
|
+
|
|
2250
|
+
return lifecycle_rules
|
|
2251
|
+
|
|
2213
2252
|
def populate_tf_resource_s3(self, spec: ExternalResourceSpec) -> aws_s3_bucket:
|
|
2214
2253
|
account = spec.provisioner_name
|
|
2215
2254
|
identifier = spec.identifier
|
|
@@ -2249,47 +2288,11 @@ class TerrascriptClient:
|
|
|
2249
2288
|
request_payer = common_values.get("request_payer")
|
|
2250
2289
|
if request_payer:
|
|
2251
2290
|
values["request_payer"] = request_payer
|
|
2252
|
-
lifecycle_rules
|
|
2253
|
-
|
|
2254
|
-
|
|
2291
|
+
if lifecycle_rules := self._build_tf_resource_s3_lifecycle_rules(
|
|
2292
|
+
versioning=versioning,
|
|
2293
|
+
common_values=common_values,
|
|
2294
|
+
):
|
|
2255
2295
|
values["lifecycle_rule"] = lifecycle_rules
|
|
2256
|
-
if versioning:
|
|
2257
|
-
lrs = values.get("lifecycle_rule", [])
|
|
2258
|
-
expiration_rule = False
|
|
2259
|
-
for lr in lrs:
|
|
2260
|
-
if "noncurrent_version_expiration" in lr:
|
|
2261
|
-
expiration_rule = True
|
|
2262
|
-
break
|
|
2263
|
-
if not expiration_rule:
|
|
2264
|
-
# Add a default noncurrent object expiration rule if
|
|
2265
|
-
# if one isn't already set
|
|
2266
|
-
rule = {
|
|
2267
|
-
"id": "expire_noncurrent_versions",
|
|
2268
|
-
"enabled": "true",
|
|
2269
|
-
"noncurrent_version_expiration": {"days": 30},
|
|
2270
|
-
}
|
|
2271
|
-
if len(lrs) > 0:
|
|
2272
|
-
lrs.append(rule)
|
|
2273
|
-
else:
|
|
2274
|
-
lrs = rule
|
|
2275
|
-
sc = common_values.get("storage_class")
|
|
2276
|
-
if sc:
|
|
2277
|
-
sc = sc.upper()
|
|
2278
|
-
days = "1"
|
|
2279
|
-
if sc.endswith("_IA"):
|
|
2280
|
-
# Infrequent Access storage class has minimum 30 days
|
|
2281
|
-
# before transition
|
|
2282
|
-
days = "30"
|
|
2283
|
-
rule = {
|
|
2284
|
-
"id": sc + "_storage_class",
|
|
2285
|
-
"enabled": "true",
|
|
2286
|
-
"transition": {"days": days, "storage_class": sc},
|
|
2287
|
-
"noncurrent_version_transition": {"days": days, "storage_class": sc},
|
|
2288
|
-
}
|
|
2289
|
-
if values.get("lifecycle_rule"):
|
|
2290
|
-
values["lifecycle_rule"].append(rule)
|
|
2291
|
-
else:
|
|
2292
|
-
values["lifecycle_rule"] = rule
|
|
2293
2296
|
cors_rules = common_values.get("cors_rules")
|
|
2294
2297
|
if cors_rules:
|
|
2295
2298
|
# common_values['cors_rules'] is a list of cors_rules
|
|
@@ -5907,7 +5910,8 @@ class TerrascriptClient:
|
|
|
5907
5910
|
return commit.sha
|
|
5908
5911
|
case "gitlab":
|
|
5909
5912
|
gitlab = self.init_gitlab()
|
|
5910
|
-
project
|
|
5913
|
+
if not (project := gitlab.get_project(url)):
|
|
5914
|
+
raise ValueError(f"could not find gitlab project for url {url}")
|
|
5911
5915
|
commits = project.commits.list(ref_name=ref, per_page=1, page=1)
|
|
5912
5916
|
return commits[0].id
|
|
5913
5917
|
case _:
|
|
@@ -24,30 +24,24 @@ class Environment(BaseModel):
|
|
|
24
24
|
return self.name == other
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
class FeatureToggle(BaseModel):
|
|
27
|
+
class FeatureToggle(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
28
28
|
name: str
|
|
29
29
|
type: FeatureToggleType = FeatureToggleType.release
|
|
30
30
|
description: str | None = None
|
|
31
31
|
impression_data: bool = Field(False, alias="impressionData")
|
|
32
32
|
environments: list[Environment]
|
|
33
33
|
|
|
34
|
-
class Config:
|
|
35
|
-
allow_population_by_field_name = True
|
|
36
|
-
|
|
37
34
|
def __eq__(self, other: object) -> bool:
|
|
38
35
|
if isinstance(other, FeatureToggle):
|
|
39
36
|
return self.name == other.name
|
|
40
37
|
return self.name == other
|
|
41
38
|
|
|
42
39
|
|
|
43
|
-
class Project(BaseModel):
|
|
40
|
+
class Project(BaseModel, validate_by_name=True, validate_by_alias=True):
|
|
44
41
|
pk: str = Field(alias="id")
|
|
45
42
|
name: str
|
|
46
43
|
feature_toggles: list[FeatureToggle] = []
|
|
47
44
|
|
|
48
|
-
class Config:
|
|
49
|
-
allow_population_by_field_name = True
|
|
50
|
-
|
|
51
45
|
|
|
52
46
|
class TokenAuth(BearerTokenAuth):
|
|
53
47
|
def __call__(self, r: requests.PreparedRequest) -> requests.PreparedRequest:
|
reconcile/utils/vault.py
CHANGED
|
@@ -6,7 +6,7 @@ import threading
|
|
|
6
6
|
import time
|
|
7
7
|
from collections.abc import Mapping
|
|
8
8
|
from functools import lru_cache
|
|
9
|
-
from typing import Any, Self
|
|
9
|
+
from typing import Any, Self
|
|
10
10
|
|
|
11
11
|
import hvac
|
|
12
12
|
import requests
|
|
@@ -48,13 +48,6 @@ class VaultConnectionError(Exception):
|
|
|
48
48
|
pass
|
|
49
49
|
|
|
50
50
|
|
|
51
|
-
class Secret(TypedDict):
|
|
52
|
-
path: str
|
|
53
|
-
field: str
|
|
54
|
-
format: str | None
|
|
55
|
-
version: str | None
|
|
56
|
-
|
|
57
|
-
|
|
58
51
|
SECRET_VERSION_LATEST = "LATEST"
|
|
59
52
|
|
|
60
53
|
|
|
@@ -197,7 +190,7 @@ class VaultClient:
|
|
|
197
190
|
self._client.auth_approle(self.role_id, self.secret_id)
|
|
198
191
|
|
|
199
192
|
@retry()
|
|
200
|
-
def read_all_with_version(self, secret: Mapping) -> tuple[
|
|
193
|
+
def read_all_with_version(self, secret: Mapping) -> tuple[dict, int | None]:
|
|
201
194
|
"""Returns a dictionary of keys and values in a Vault secret and the
|
|
202
195
|
version of the secret, for V1 secrets, version will be None.
|
|
203
196
|
|
|
@@ -250,7 +243,7 @@ class VaultClient:
|
|
|
250
243
|
|
|
251
244
|
def __read_all_v2(
|
|
252
245
|
self, path: str, version: str | None
|
|
253
|
-
) -> tuple[dict[str, Any],
|
|
246
|
+
) -> tuple[dict[str, Any], int]:
|
|
254
247
|
path_split = path.split("/")
|
|
255
248
|
mount_point = path_split[0]
|
|
256
249
|
read_path = "/".join(path_split[1:])
|
|
@@ -294,7 +287,7 @@ class VaultClient:
|
|
|
294
287
|
return secret["data"]
|
|
295
288
|
|
|
296
289
|
@retry()
|
|
297
|
-
def read(self, secret:
|
|
290
|
+
def read(self, secret: Mapping[str, Any]) -> Any:
|
|
298
291
|
"""Returns a value of a key in a Vault secret.
|
|
299
292
|
|
|
300
293
|
The input secret is a dictionary which contains the following fields:
|
reconcile/utils/vcs.py
CHANGED
|
@@ -140,7 +140,7 @@ class VCS:
|
|
|
140
140
|
gitlab_instances: Iterable[GitlabInstanceV1],
|
|
141
141
|
) -> GitLabApi:
|
|
142
142
|
return GitLabApi(
|
|
143
|
-
next(iter(gitlab_instances)).
|
|
143
|
+
next(iter(gitlab_instances)).model_dump(by_alias=True),
|
|
144
144
|
secret_reader=self._secret_reader,
|
|
145
145
|
)
|
|
146
146
|
|
|
@@ -150,7 +150,7 @@ class VCS:
|
|
|
150
150
|
app_interface_repo_url: str,
|
|
151
151
|
) -> GitLabApi:
|
|
152
152
|
return GitLabApi(
|
|
153
|
-
next(iter(gitlab_instances)).
|
|
153
|
+
next(iter(gitlab_instances)).model_dump(by_alias=True),
|
|
154
154
|
secret_reader=self._secret_reader,
|
|
155
155
|
project_url=app_interface_repo_url,
|
|
156
156
|
)
|
|
@@ -221,26 +221,26 @@ class VCS:
|
|
|
221
221
|
match repo_info.platform:
|
|
222
222
|
case "github":
|
|
223
223
|
github = self._init_github(repo_url=repo_url, auth_code=auth_code)
|
|
224
|
-
data = github.compare(commit_from=commit_from, commit_to=commit_to)
|
|
225
224
|
return [
|
|
226
225
|
Commit(
|
|
227
226
|
repo=repo_url,
|
|
228
227
|
sha=gh_commit.sha,
|
|
229
228
|
date=gh_commit.commit.committer.date,
|
|
230
229
|
)
|
|
231
|
-
for gh_commit in
|
|
230
|
+
for gh_commit in github.compare(
|
|
231
|
+
commit_from=commit_from, commit_to=commit_to
|
|
232
|
+
)
|
|
232
233
|
]
|
|
233
234
|
case "gitlab":
|
|
234
|
-
data = self._gitlab_instance.repository_compare(
|
|
235
|
-
repo_url=repo_url, ref_from=commit_from, ref_to=commit_to
|
|
236
|
-
)
|
|
237
235
|
return [
|
|
238
236
|
Commit(
|
|
239
237
|
repo=repo_url,
|
|
240
238
|
sha=gl_commit["id"],
|
|
241
239
|
date=datetime.fromisoformat(gl_commit["committed_date"]),
|
|
242
240
|
)
|
|
243
|
-
for gl_commit in
|
|
241
|
+
for gl_commit in self._gitlab_instance.repository_compare(
|
|
242
|
+
repo_url=repo_url, ref_from=commit_from, ref_to=commit_to
|
|
243
|
+
)
|
|
244
244
|
]
|
|
245
245
|
case _:
|
|
246
246
|
raise ValueError(f"Unsupported repository URL: {repo_url}")
|
reconcile/vault_replication.py
CHANGED
|
@@ -217,7 +217,7 @@ def copy_vault_secret(
|
|
|
217
217
|
return
|
|
218
218
|
|
|
219
219
|
# If we reach here, we successfully read the destination secret
|
|
220
|
-
if dest_version is None
|
|
220
|
+
if dest_version is None or version is None:
|
|
221
221
|
# v1 secrets don't have version
|
|
222
222
|
if source_data == dest_data:
|
|
223
223
|
# If the secret is the same in both vaults, we don't need
|
|
@@ -74,7 +74,7 @@ class CostManagementApi(ApiBase):
|
|
|
74
74
|
timeout=self.read_timeout,
|
|
75
75
|
)
|
|
76
76
|
response.raise_for_status()
|
|
77
|
-
return AwsReportCostResponse.
|
|
77
|
+
return AwsReportCostResponse.model_validate(response.json())
|
|
78
78
|
|
|
79
79
|
def get_openshift_costs_report(
|
|
80
80
|
self,
|
|
@@ -97,7 +97,7 @@ class CostManagementApi(ApiBase):
|
|
|
97
97
|
timeout=self.read_timeout,
|
|
98
98
|
)
|
|
99
99
|
response.raise_for_status()
|
|
100
|
-
return OpenShiftReportCostResponse.
|
|
100
|
+
return OpenShiftReportCostResponse.model_validate(response.json())
|
|
101
101
|
|
|
102
102
|
def get_openshift_cost_optimization_report(
|
|
103
103
|
self,
|
|
@@ -120,7 +120,7 @@ class CostManagementApi(ApiBase):
|
|
|
120
120
|
response.raise_for_status()
|
|
121
121
|
|
|
122
122
|
data = self._get_paginated(response)
|
|
123
|
-
return OpenShiftCostOptimizationReportResponse.
|
|
123
|
+
return OpenShiftCostOptimizationReportResponse.model_validate(data)
|
|
124
124
|
|
|
125
125
|
def _get_paginated(
|
|
126
126
|
self,
|