feat: inventory SMTP infrastructure dependencies
Module Package Release / publish-packages (push) Successful in 12s
Module Package Release / publish-packages (push) Successful in 12s
This commit is contained in:
@@ -5,7 +5,7 @@ from dataclasses import dataclass, replace
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
from sqlalchemy import or_, select
|
||||
from sqlalchemy import func, or_, select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from govoplan_core.core.configuration_packages import (
|
||||
@@ -24,6 +24,8 @@ from govoplan_core.core.configuration_packages import (
|
||||
from govoplan_core.core.infrastructure_capabilities import (
|
||||
InfrastructureCapability,
|
||||
InfrastructureCapabilityReceipt,
|
||||
InfrastructureDependency,
|
||||
InfrastructureDependencyProvider,
|
||||
)
|
||||
from govoplan_core.security.credential_envelopes import (
|
||||
CredentialAccessContext,
|
||||
@@ -51,6 +53,9 @@ from govoplan_mail.backend.server_hierarchy import (
|
||||
|
||||
|
||||
MAIL_CONFIGURATION_CAPABILITY = "mail.configuration"
|
||||
MAIL_INFRASTRUCTURE_DEPENDENCY_CAPABILITY = (
|
||||
"infrastructure.dependency_inventory.mail"
|
||||
)
|
||||
SMTP_PROFILE_FRAGMENT = "smtp_profile"
|
||||
_SYSTEM_CONFIGURATION_SCOPES = frozenset(
|
||||
{"system:settings:write", "system:governance:write"}
|
||||
@@ -132,8 +137,12 @@ class _ProfileTarget:
|
||||
on_conflict: str
|
||||
|
||||
|
||||
class SqlMailConfigurationProvider(ConfigurationProvider):
|
||||
class SqlMailConfigurationProvider(
|
||||
ConfigurationProvider,
|
||||
InfrastructureDependencyProvider,
|
||||
):
|
||||
module_id = "mail"
|
||||
capability_ids = ("mail.smtp",)
|
||||
|
||||
def describe(self) -> ConfigurationProviderDescription:
|
||||
return ConfigurationProviderDescription(
|
||||
@@ -204,6 +213,84 @@ class SqlMailConfigurationProvider(ConfigurationProvider):
|
||||
item for item in import_result.diagnostics if item.severity == "blocker"
|
||||
)
|
||||
|
||||
def infrastructure_dependencies(self) -> tuple[InfrastructureDependency, ...]:
|
||||
with get_database().session() as session:
|
||||
return _smtp_infrastructure_dependencies(session)
|
||||
|
||||
|
||||
def _smtp_infrastructure_dependencies(
|
||||
session: Session,
|
||||
) -> tuple[InfrastructureDependency, ...]:
|
||||
endpoints = tuple(
|
||||
session.execute(
|
||||
select(MailServerEndpoint)
|
||||
.where(MailServerEndpoint.protocol == "smtp")
|
||||
.order_by(MailServerEndpoint.id)
|
||||
).scalars()
|
||||
)
|
||||
binding_counts = {
|
||||
str(server_id): int(count)
|
||||
for server_id, count in session.execute(
|
||||
select(
|
||||
MailServerCredentialBinding.server_id,
|
||||
func.count(MailServerCredentialBinding.id),
|
||||
).group_by(MailServerCredentialBinding.server_id)
|
||||
)
|
||||
}
|
||||
dependencies: list[InfrastructureDependency] = []
|
||||
endpoint_profile_ids: set[str] = set()
|
||||
for endpoint in endpoints:
|
||||
endpoint_profile_ids.add(endpoint.profile_id)
|
||||
dependencies.append(
|
||||
InfrastructureDependency(
|
||||
capability_id="mail.smtp",
|
||||
module_id="mail",
|
||||
dependency_type="smtp_endpoint",
|
||||
dependency_ref=mail_server_ref(endpoint.id) or f"mail:{endpoint.id}",
|
||||
state="active" if endpoint.is_active else "inactive",
|
||||
scope=str(endpoint.scope_type or "tenant"),
|
||||
summary=(
|
||||
"A persisted Mail SMTP endpoint is bound to the deployment relay."
|
||||
),
|
||||
metrics={
|
||||
"credential_binding_count": binding_counts.get(endpoint.id, 0),
|
||||
"default_endpoint": int(bool(endpoint.is_default)),
|
||||
},
|
||||
required_action=(
|
||||
"Rebind, migrate, or explicitly retire this SMTP endpoint and its credential-envelope references before changing the relay capability."
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
legacy_profiles = tuple(
|
||||
session.execute(
|
||||
select(MailServerProfile)
|
||||
.where(MailServerProfile.smtp_config.is_not(None))
|
||||
.order_by(MailServerProfile.id)
|
||||
).scalars()
|
||||
)
|
||||
for profile in legacy_profiles:
|
||||
if profile.id in endpoint_profile_ids or not dict(profile.smtp_config or {}):
|
||||
continue
|
||||
dependencies.append(
|
||||
InfrastructureDependency(
|
||||
capability_id="mail.smtp",
|
||||
module_id="mail",
|
||||
dependency_type="legacy_smtp_profile",
|
||||
dependency_ref=f"mail-profile:{profile.id}",
|
||||
state="active" if profile.is_active else "inactive",
|
||||
scope=str(profile.scope_type or "tenant"),
|
||||
summary=(
|
||||
"A persisted legacy Mail profile still contains SMTP transport configuration."
|
||||
),
|
||||
metrics={"credential_binding_count": 0},
|
||||
required_action=(
|
||||
"Migrate or explicitly retire this legacy profile before changing the relay capability."
|
||||
),
|
||||
)
|
||||
)
|
||||
return tuple(dependencies)
|
||||
|
||||
|
||||
def _preflight_smtp_profile(
|
||||
session: Session,
|
||||
|
||||
Reference in New Issue
Block a user