Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
4f111d1
docs(specs): add INFP-507 retention specification
saltas888 Oct 4, 2026
0219f42
docs(specs): add INFP-507 retention plan, research and contracts
saltas888 Oct 4, 2026
3523e79
docs(specs): add INFP-507 critique and apply its fixes
saltas888 Oct 4, 2026
1b51fa7
docs(specs): add INFP-507 retention tasks
saltas888 Oct 4, 2026
f484319
docs(specs): add INFP-507 alignment check
saltas888 Oct 4, 2026
71134a5
docs(specs): align INFP-507 spec with the updated design doc figures
saltas888 Oct 4, 2026
36f6048
docs(specs): follow the design doc on own-event capping, rewrite cond…
saltas888 Oct 4, 2026
59b76d6
docs(specs): restore Prefect's runs-first delete order in the INFP-50…
saltas888 Oct 4, 2026
9c70aae
docs(specs): add infrahub-private-tests release evidence to the INFP-…
saltas888 Oct 4, 2026
f303a0e
docs(specs): reword the Helm hook risk and run private tests in their…
saltas888 Oct 4, 2026
72666c7
docs(specs): run the Prefect event-type guard on the full stack and a…
saltas888 Oct 4, 2026
ce37f1e
feat(backend): add task manager retention settings
saltas888 Oct 4, 2026
a80413b
feat(backend): derive Prefect retention settings in the task manager
saltas888 Oct 4, 2026
74f50b2
docs(specs): mark the INFP-507 retention settings tasks done
saltas888 Oct 4, 2026
43c9345
fix(backend): pass the retention settings to the Compose task manager
saltas888 Oct 4, 2026
f3cd8e5
docs(specs): drop stale INFP-507 alignment rows and name the event-ty…
saltas888 Oct 4, 2026
1b3d02b
feat(backend): give Prefect's own events their own retention
saltas888 Oct 4, 2026
59b3e04
fix(backend): refuse task manager retentions longer than 36500 days
saltas888 Oct 4, 2026
a0bf3a5
docs(changelog): describe the task manager retention settings
saltas888 Oct 4, 2026
f6dabb8
docs(specs): mark the INFP-507 Prefect event-type tasks done
saltas888 Oct 4, 2026
a352e9f
fix(backend): count the per-type event overrides in the startup log
saltas888 Oct 4, 2026
2044fae
docs(specs): record the 36,500-day retention maximum in the INFP-507 …
saltas888 Oct 4, 2026
44ae08b
fix(backend): refuse an oversized ISO 8601 retention by its maximum
saltas888 Oct 4, 2026
1049c48
docs(specs): call the retention maximum a safety bound and reuse the …
saltas888 Oct 4, 2026
23dba13
fix(backend): report a bare ISO 8601 designator as an unreadable rete…
saltas888 Oct 4, 2026
a8c7fc9
docs(changelog): say finished task runs were not deleted by age before
saltas888 Oct 4, 2026
942b423
fix(backend): read only the task manager section of the configuration…
claude Oct 27, 2026
f3e8e9c
fix(backend): warn when the task manager runs without Prefect's backg…
claude Oct 28, 2026
e7e304a
fix(backend): refuse a prefect_own_events retention longer than activ…
claude Oct 28, 2026
14edafc
fix(backend): treat an empty Prefect retention variable as not set
claude Oct 28, 2026
37dd1c4
fix(backend): report the Prefect cleanups that a preset vacuum switch…
claude Oct 28, 2026
ccfc629
fix(backend): keep a retention set in Prefect's settings files and lo…
claude Oct 28, 2026
7739884
perf(backend): skip per-type event overrides that do not shorten the …
claude Oct 28, 2026
6e889ca
fix(backend): list Prefect's workspace transfer events and check the …
claude Oct 28, 2026
6f9f361
test(backend): run the event retention test on a database of its own
claude Oct 28, 2026
ab2e660
fix(backend): type the Prefect state constructors in the event type t…
claude Oct 28, 2026
393041e
fix(backend): remove only an empty Prefect vacuum switch and say what…
claude Oct 28, 2026
b53290c
fix(backend): say what to change when prefect_own_events is longer th…
claude Oct 28, 2026
4dcb097
docs(backend): list every error the task manager settings loader raises
claude Oct 28, 2026
256d408
test(backend): drop the equal retentions case that the apply test alr…
claude Oct 28, 2026
b141639
docs(specs): state the retention rules only where they hold
claude Oct 28, 2026
b5964c4
docs(changelog): avoid the Infrahub Profile term for Prefect's profile
claude Oct 28, 2026
864cdd0
docs(specs): ship the background-services command in part 1 of INFP-507
claude Oct 28, 2026
d4b0e71
docs(specs): name the stale-runs command and say it catches runs that…
claude Oct 28, 2026
b89d93c
docs(specs): correct INFP-507 spec statements found in review
claude Oct 28, 2026
5a01a6f
test(backend): keep retention variables set before the task manager r…
claude Oct 28, 2026
b815fb7
test(backend): declare the Prefect variable names of the retention te…
claude Oct 28, 2026
60078f4
docs(specs): say US4 ships in part 1 and why E6 still stands
claude Oct 28, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions backend/infrahub/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
from infrahub.constants.database import DatabaseType
from infrahub.exceptions import InitializationError, ProcessingError
from infrahub.log import get_logger
from infrahub.prefect_server.retention_settings import (
TaskManagerRetentionSettings, # noqa: F401 - re-exported with the other settings models
TaskManagerSettings,
print_configuration_errors,
)
from infrahub.tls.bundle import resolve_ca_bundle

if TYPE_CHECKING:
Expand Down Expand Up @@ -2100,6 +2105,10 @@ def trace(self) -> TraceSettings:
def experimental_features(self) -> ExperimentalFeaturesSettings:
return self.active_settings.experimental_features

@property
def task_manager(self) -> TaskManagerSettings:
return self.active_settings.task_manager

@property
def enterprise_features(self) -> list[EnterpriseFeatures]:
"""Returns a list of enterprise features that are enabled based on the settings."""
Expand Down Expand Up @@ -2130,6 +2139,8 @@ class Settings(BaseSettings):
trace: TraceSettings = TraceSettings()
experimental_features: ExperimentalFeaturesSettings = ExperimentalFeaturesSettings()
log_forwarding: LogForwardingSettings = LogForwardingSettings()
# Built when the configuration loads, so an invalid retention fails the load instead of the module import.
task_manager: TaskManagerSettings = Field(default_factory=TaskManagerSettings)

@model_validator(mode="after")
def validate_git_branch_deletion_requires_branch_deletion(self) -> Self:
Expand Down Expand Up @@ -2210,10 +2221,7 @@ def load_and_exit(config_file_name: Path | str = "infrahub.toml", config_data: d
try:
SETTINGS.settings = load(config_file_name=config_file_name, config_data=config_data)
except ValidationError as err:
print(f"Configuration not valid, found {len(err.errors())} error(s)")
for error in err.errors():
error_locations = [str(location) for location in error["loc"]]
print(f" {'/'.join(error_locations)} | {error['msg']} ({error['type']})")
print_configuration_errors(error=err)
sys.exit(1)


Expand Down
63 changes: 58 additions & 5 deletions backend/infrahub/prefect_server/app.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,30 @@
from __future__ import annotations

import asyncio
import logging
import os
import sys
from pathlib import Path

from fastapi import APIRouter, FastAPI
from prefect.context import refresh_global_settings_context
from prefect.server.api.server import create_app
from prefect.settings import get_current_settings
from pydantic import ValidationError

from . import events
from .bootstrap import init_prefect
from .retention import (
apply_prefect_retention_env,
background_services_warning,
prefect_retention_already_set,
prefect_retention_in_effect,
remove_blank_vacuum_switch,
turned_off_cleanup_warnings,
)
from .retention_settings import load_task_manager_settings, print_configuration_errors

log = logging.getLogger(__name__)

router = APIRouter(prefix="/infrahub")

Expand All @@ -29,7 +46,47 @@ async def _init_prefect() -> None:
await init_prefect()


def apply_infrahub_settings_to_prefect() -> None:
"""Read the task manager section of Infrahub's configuration into Prefect's retention settings and log them, exiting when it is invalid."""
# Sets up the log handler and level that the retention lines need, and is imported here because the stock
# Prefect container of some test suites has no infrahub package.
import infrahub.log # noqa: F401, PLC0415

config_file = Path(os.environ.get("INFRAHUB_CONFIG", "infrahub.toml")).resolve()
try:
retention = load_task_manager_settings(config_file=config_file).retention
except ValidationError as error:
print_configuration_errors(error=error)
sys.exit(1)
remove_blank_vacuum_switch(environ=os.environ)
# Read before any write, since a variable written here would count as one the operator set.
already_set = prefect_retention_already_set()
for warning in apply_prefect_retention_env(environ=os.environ, settings=retention, already_set=already_set):
log.warning(warning)
# Prefect reads its settings from the environment once at import, so new values apply only after a refresh.
refresh_global_settings_context()
prefect_settings = get_current_settings()
for warning in turned_off_cleanup_warnings(vacuum=prefect_settings.server.services.db_vacuum):
log.warning(warning)
for name, value in prefect_retention_in_effect(settings=prefect_settings).items():
log.info(f"Task manager retention: {name}={value}")


def create_prefect_app_with_infrahub_routes() -> FastAPI:
"""Build Prefect's server app with Infrahub's routes, leaving Prefect's settings as the environment sets them."""
app = create_app()
api_app: FastAPI = app.__dict__["api_app"]
api_app.include_router(router=router)

return app


def create_infrahub_prefect() -> FastAPI:
"""Build the task manager app with Prefect's settings derived from Infrahub's configuration."""
apply_infrahub_settings_to_prefect()
Comment thread
saltas888 marked this conversation as resolved.
Resolved
if warning := background_services_warning(environ=os.environ):
log.warning(warning)

if (
os.getenv("PREFECT_API_BLOCKS_REGISTER_ON_START") == "false"
and os.getenv("PREFECT_API_DATABASE_MIGRATE_ON_START") == "false"
Expand All @@ -40,8 +97,4 @@ def create_infrahub_prefect() -> FastAPI:
config.SETTINGS.initialize_and_exit()
asyncio.run(_init_prefect())

app = create_app()
api_app: FastAPI = app.__dict__["api_app"]
api_app.include_router(router=router)

return app
return create_prefect_app_with_infrahub_routes()
Loading
Loading