issue-7-config-cleanup-legacy-removal #18
@@ -70,7 +70,7 @@ automation/debugging path rather than the normal UI workflow:
|
|||||||
|
|
||||||
```
|
```
|
||||||
pobsync django configure_pobsync_host <host> --address <host.example>
|
pobsync django configure_pobsync_host <host> --address <host.example>
|
||||||
pobsync django configure_pobsync_schedule <host> --cron "15 2 * * *"
|
pobsync django configure_pobsync_schedule <host> --schedule-expression "15 2 * * *"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Installer Development
|
## Installer Development
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ from pobsync_backend.models import GlobalConfig
|
|||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = "Create or update the SQL-backed global pobsync configuration."
|
help = "Create or update the default global backup configuration."
|
||||||
|
|
||||||
def add_arguments(self, parser) -> None:
|
def add_arguments(self, parser) -> None:
|
||||||
parser.add_argument("--name", default="default")
|
parser.add_argument("--name", default="default")
|
||||||
@@ -48,8 +48,8 @@ class Command(BaseCommand):
|
|||||||
}
|
}
|
||||||
|
|
||||||
if GlobalConfig.objects.filter(name=options["name"]).exists() and not options["force"]:
|
if GlobalConfig.objects.filter(name=options["name"]).exists() and not options["force"]:
|
||||||
raise CommandError(f"GlobalConfig {options['name']!r} already exists; use --force to update")
|
raise CommandError(f"Global config {options['name']!r} already exists; use --force to update")
|
||||||
|
|
||||||
_obj, created = GlobalConfig.objects.update_or_create(name=options["name"], defaults=defaults)
|
_obj, created = GlobalConfig.objects.update_or_create(name=options["name"], defaults=defaults)
|
||||||
action = "Created" if created else "Updated"
|
action = "Created" if created else "Updated"
|
||||||
self.stdout.write(self.style.SUCCESS(f"{action} GlobalConfig {options['name']!r}."))
|
self.stdout.write(self.style.SUCCESS(f"{action} global config {options['name']!r}."))
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from pobsync_backend.models import GlobalConfig, HostConfig
|
|||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = "Create or update a SQL-backed host pobsync configuration."
|
help = "Create or update a host backup configuration."
|
||||||
|
|
||||||
def add_arguments(self, parser) -> None:
|
def add_arguments(self, parser) -> None:
|
||||||
parser.add_argument("host")
|
parser.add_argument("host")
|
||||||
@@ -29,7 +29,7 @@ class Command(BaseCommand):
|
|||||||
def handle(self, *args: Any, **options: Any) -> None:
|
def handle(self, *args: Any, **options: Any) -> None:
|
||||||
host = sanitize_host(options["host"])
|
host = sanitize_host(options["host"])
|
||||||
if HostConfig.objects.filter(host=host).exists() and not options["force"]:
|
if HostConfig.objects.filter(host=host).exists() and not options["force"]:
|
||||||
raise CommandError(f"HostConfig {host!r} already exists; use --force to update")
|
raise CommandError(f"Host {host!r} already exists; use --force to update")
|
||||||
|
|
||||||
retention = self._retention(options["retention"])
|
retention = self._retention(options["retention"])
|
||||||
defaults = {
|
defaults = {
|
||||||
@@ -49,7 +49,7 @@ class Command(BaseCommand):
|
|||||||
}
|
}
|
||||||
_obj, created = HostConfig.objects.update_or_create(host=host, defaults=defaults)
|
_obj, created = HostConfig.objects.update_or_create(host=host, defaults=defaults)
|
||||||
action = "Created" if created else "Updated"
|
action = "Created" if created else "Updated"
|
||||||
self.stdout.write(self.style.SUCCESS(f"{action} HostConfig {host!r}."))
|
self.stdout.write(self.style.SUCCESS(f"{action} host {host!r}."))
|
||||||
|
|
||||||
def _retention(self, value: str | None) -> dict[str, int]:
|
def _retention(self, value: str | None) -> dict[str, int]:
|
||||||
if value:
|
if value:
|
||||||
|
|||||||
@@ -9,11 +9,16 @@ from pobsync_backend.scheduler import parse_cron_expr
|
|||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = "Create, update, disable, or remove a SQL-backed pobsync schedule."
|
help = "Create, update, disable, or remove a scheduler-managed host schedule."
|
||||||
|
|
||||||
def add_arguments(self, parser) -> None:
|
def add_arguments(self, parser) -> None:
|
||||||
parser.add_argument("host")
|
parser.add_argument("host")
|
||||||
parser.add_argument("--cron", help='Cron expression, e.g. "15 2 * * *"')
|
parser.add_argument(
|
||||||
|
"--schedule-expression",
|
||||||
|
"--cron",
|
||||||
|
dest="schedule_expression",
|
||||||
|
help='Five-field schedule expression, e.g. "15 2 * * *"',
|
||||||
|
)
|
||||||
parser.add_argument("--prune", action="store_true")
|
parser.add_argument("--prune", action="store_true")
|
||||||
parser.add_argument("--prune-max-delete", type=int, default=10)
|
parser.add_argument("--prune-max-delete", type=int, default=10)
|
||||||
parser.add_argument("--prune-protect-bases", action="store_true")
|
parser.add_argument("--prune-protect-bases", action="store_true")
|
||||||
@@ -24,24 +29,25 @@ class Command(BaseCommand):
|
|||||||
try:
|
try:
|
||||||
host = HostConfig.objects.get(host=options["host"])
|
host = HostConfig.objects.get(host=options["host"])
|
||||||
except HostConfig.DoesNotExist as exc:
|
except HostConfig.DoesNotExist as exc:
|
||||||
raise CommandError(f"Missing HostConfig {options['host']!r}") from exc
|
raise CommandError(f"Missing host {options['host']!r}") from exc
|
||||||
|
|
||||||
if options["delete"]:
|
if options["delete"]:
|
||||||
deleted, _details = ScheduleConfig.objects.filter(host=host).delete()
|
deleted, _details = ScheduleConfig.objects.filter(host=host).delete()
|
||||||
self.stdout.write(self.style.SUCCESS(f"Deleted {deleted} schedule row(s) for {host.host!r}."))
|
self.stdout.write(self.style.SUCCESS(f"Deleted {deleted} schedule row(s) for {host.host!r}."))
|
||||||
return
|
return
|
||||||
|
|
||||||
if not options["cron"]:
|
schedule_expression = options["schedule_expression"]
|
||||||
raise CommandError("--cron is required unless --delete is used")
|
if not schedule_expression:
|
||||||
|
raise CommandError("--schedule-expression is required unless --delete is used")
|
||||||
try:
|
try:
|
||||||
parse_cron_expr(options["cron"])
|
parse_cron_expr(schedule_expression)
|
||||||
except ValueError as exc:
|
except ValueError as exc:
|
||||||
raise CommandError(str(exc)) from exc
|
raise CommandError(str(exc)) from exc
|
||||||
|
|
||||||
schedule, created = ScheduleConfig.objects.update_or_create(
|
schedule, created = ScheduleConfig.objects.update_or_create(
|
||||||
host=host,
|
host=host,
|
||||||
defaults={
|
defaults={
|
||||||
"cron_expr": options["cron"],
|
"cron_expr": schedule_expression,
|
||||||
"enabled": not options["disabled"],
|
"enabled": not options["disabled"],
|
||||||
"prune": bool(options["prune"]),
|
"prune": bool(options["prune"]),
|
||||||
"prune_max_delete": int(options["prune_max_delete"]),
|
"prune_max_delete": int(options["prune_max_delete"]),
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ class Command(BaseCommand):
|
|||||||
try:
|
try:
|
||||||
global_config = GlobalConfig.objects.get(name="default")
|
global_config = GlobalConfig.objects.get(name="default")
|
||||||
except GlobalConfig.DoesNotExist as exc:
|
except GlobalConfig.DoesNotExist as exc:
|
||||||
raise CommandError("Missing GlobalConfig 'default'") from exc
|
raise CommandError("Missing default global config") from exc
|
||||||
|
|
||||||
host = None
|
host = None
|
||||||
if options["host"]:
|
if options["host"]:
|
||||||
try:
|
try:
|
||||||
host = HostConfig.objects.get(host=options["host"], enabled=True)
|
host = HostConfig.objects.get(host=options["host"], enabled=True)
|
||||||
except HostConfig.DoesNotExist as exc:
|
except HostConfig.DoesNotExist as exc:
|
||||||
raise CommandError(f"Missing enabled HostConfig {options['host']!r}") from exc
|
raise CommandError(f"Missing enabled host {options['host']!r}") from exc
|
||||||
|
|
||||||
kind = normalize_kind(options["kind"])
|
kind = normalize_kind(options["kind"])
|
||||||
kinds = ["scheduled", "manual", "incomplete"] if kind == "all" else [kind]
|
kinds = ["scheduled", "manual", "incomplete"] if kind == "all" else [kind]
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class Command(BaseCommand):
|
|||||||
try:
|
try:
|
||||||
host = HostConfig.objects.get(host=host_name, enabled=True)
|
host = HostConfig.objects.get(host=host_name, enabled=True)
|
||||||
except HostConfig.DoesNotExist as exc:
|
except HostConfig.DoesNotExist as exc:
|
||||||
raise CommandError(f"Missing enabled HostConfig {host_name!r}") from exc
|
raise CommandError(f"Missing enabled host {host_name!r}") from exc
|
||||||
|
|
||||||
run = BackupRun.objects.create(
|
run = BackupRun.objects.create(
|
||||||
host=host,
|
host=host,
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from pobsync_backend.retention import run_sql_retention_apply, run_sql_retention
|
|||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = "Plan or apply retention using SQL-backed pobsync configuration."
|
help = "Plan or apply retention using the Django backup configuration."
|
||||||
|
|
||||||
def add_arguments(self, parser) -> None:
|
def add_arguments(self, parser) -> None:
|
||||||
parser.add_argument("host")
|
parser.add_argument("host")
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class ConfigureCommandsTests(TestCase):
|
|||||||
config = GlobalConfig.objects.get(name="default")
|
config = GlobalConfig.objects.get(name="default")
|
||||||
self.assertEqual(config.backup_root, "/backups")
|
self.assertEqual(config.backup_root, "/backups")
|
||||||
self.assertEqual(config.retention_daily, 3)
|
self.assertEqual(config.retention_daily, 3)
|
||||||
self.assertIn("Created GlobalConfig", out.getvalue())
|
self.assertIn("Created global config", out.getvalue())
|
||||||
|
|
||||||
def test_configure_host_uses_global_retention_defaults(self) -> None:
|
def test_configure_host_uses_global_retention_defaults(self) -> None:
|
||||||
GlobalConfig.objects.create(
|
GlobalConfig.objects.create(
|
||||||
@@ -61,7 +61,7 @@ class ConfigureCommandsTests(TestCase):
|
|||||||
call_command(
|
call_command(
|
||||||
"configure_pobsync_schedule",
|
"configure_pobsync_schedule",
|
||||||
host.host,
|
host.host,
|
||||||
cron="15 2 * * *",
|
schedule_expression="15 2 * * *",
|
||||||
prune=True,
|
prune=True,
|
||||||
stdout=out,
|
stdout=out,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user