Skip to content

Commit

Permalink
WIP Refactoring of open and close commands
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxG87 committed Jul 23, 2024
1 parent bfc92cb commit fdaf1d5
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 56 deletions.
67 changes: 24 additions & 43 deletions src/root_subvol_snapshot/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
import os
import sys
import typing as t
from pathlib import Path
from tempfile import mkdtemp
from typing import Any, Callable, Optional
Expand Down Expand Up @@ -55,63 +56,43 @@ def setup_logging(verbosity: int) -> None:

CONFIG_OPTION = typer.Option(get_default_config_path(), exists=True, dir_okay=False)
VERBOSITY_OPTION = typer.Option(0, "--verbose", "-v", count=True)
DEVICE_OPTION = t.Annotated[
Path, typer.Option(..., exists=True, dir_okay=False, readable=True)
]


@app.command()
def open( # noqa: A001
config: Path = CONFIG_OPTION, verbose: int = VERBOSITY_OPTION
) -> None:
def open(device: DEVICE_OPTION, verbose: int = VERBOSITY_OPTION) -> None: # noqa: A001
"""
Öffne alle in der Konfiguration gelisteten Speichermedien
Das Kommando `open` öffnet alle Speichermedien, deren UUID in der
Konfiguration erwähnt wird. Für jedes geöffnete Speichermedium wird die
UUID und der Mount-Zielordner angegeben.
Dies Kommando ist besonders nützlich um Sicherheitskopien
wiederherzustellen. Dafür wird das Speichermedium, auf dem sich die
Sicherheitskopien befinden, mittels `butter-backup open` geöffnet. Dann
kann mit den Daten interagiert werden, z.B. durch Öffnen im Dateibrowser
oder durch Verwendung von `restic`. Nach erfolgreicher Wiederherstellung
kann das Speichermedium mit `butter-backup close` wieder entfernt werden.
Open snapshot subvolume of a Btrfs device
The command `open` mounts the subvolume `@snapshot` of a Btrfs device. If
no device is provided, the one that hosts `/` will be used.
"""
setup_logging(verbose)
configurations = cp.parse_configuration(config.read_text())
for cfg in configurations:
if cfg.device().exists():
mount_dir = Path(mkdtemp())
decrypted = sdm.open_encrypted_device(cfg.device(), cfg.DevicePassCmd)
sdm.mount_btrfs_device(
decrypted, mount_dir=mount_dir, compression=cfg.Compression
)
typer.echo(f"Speichermedium {cfg.UUID} wurde in {mount_dir} geöffnet.")
mount_dir = Path(mkdtemp())
sdm.mount_btrfs_device(device, mount_dir=mount_dir)
typer.echo(f"Device {device} was mounted in {mount_dir}.")


@app.command()
def close(config: Path = CONFIG_OPTION, verbose: int = VERBOSITY_OPTION) -> None:
def close(device: DEVICE_OPTION, verbose: int = VERBOSITY_OPTION) -> None:
"""
Schließe alle geöffneten Speichermedien
Unmounts a Btrfs device
Das Kommando `close` schließt alle gemounteten Speichermedien, deren UUIDs
in der Konfiguration erwähnt werden. Es ist das Gegenstück des Kommandos
`open`. Weitere Erklärungen finden sich dort.
The command `close` unmounts the provided Btrfs device. If no device is
provided, the one that hosts `/` will be used. It is the counterpart of the
`open` command. Further explanations can be found there.
"""
setup_logging(verbose)
configurations = cp.parse_configuration(config.read_text())
mounted_devices = sdm.get_mounted_devices()
for cfg in configurations:
mapped_device = f"/dev/mapper/{cfg.UUID}"
if cfg.device().exists() and mapped_device in mounted_devices:
mount_dirs = mounted_devices[mapped_device]
if len(mount_dirs) != 1:
# TODO introduce custom exception
raise ValueError(
"Got several possible mount points. Expected exactly 1!"
)
mount_dir = mount_dirs.pop()
sdm.unmount_device(mount_dir)
sdm.close_decrypted_device(Path(mapped_device))
mount_dir.rmdir()
mount_dirs = mounted_devices[str(device)]
if len(mount_dirs) != 1:
# TODO introduce custom exception
raise ValueError("Got several possible mount points. Expected exactly 1!")
mount_dir = mount_dirs.pop()
sdm.unmount_device(mount_dir)
mount_dir.rmdir()


@app.command()
Expand Down
19 changes: 6 additions & 13 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,10 +91,7 @@ def test_setup_logging_clamps_level(capsys) -> None:
assert tracemsg in err


@pytest.mark.parametrize(
"subprogram",
["backup", "close", "open"],
)
@pytest.mark.parametrize("subprogram", ["backup"])
def test_subprograms_refuse_missing_config(subprogram, runner) -> None:
config_file = Path(get_random_filename())
result = runner.invoke(app, [subprogram, "--config", str(config_file)])
Expand All @@ -103,10 +100,7 @@ def test_subprograms_refuse_missing_config(subprogram, runner) -> None:


@pytest.mark.skipif(in_docker_container(), reason="All files are readable for root")
@pytest.mark.parametrize(
"subprogram",
["backup", "close", "open"],
)
@pytest.mark.parametrize("subprogram", ["backup"])
def test_subprograms_refuse_unreadable_file(subprogram, runner) -> None:
with NamedTemporaryFile() as fh:
config_file = Path(fh.name)
Expand All @@ -116,10 +110,7 @@ def test_subprograms_refuse_unreadable_file(subprogram, runner) -> None:
assert result.exit_code != 0


@pytest.mark.parametrize(
"subprogram",
["backup", "close", "open"],
)
@pytest.mark.parametrize("subprogram", ["backup"])
def test_subprograms_refuse_directories(subprogram, runner) -> None:
with TemporaryDirectory() as tmp_dir:
result = runner.invoke(app, [subprogram, "--config", tmp_dir])
Expand All @@ -135,7 +126,9 @@ def test_close_does_not_close_unopened_device(runner, encrypted_btrfs_device) ->
with NamedTemporaryFile() as tempf:
config_file = Path(tempf.name)
config_file.write_text(f"[{config.model_dump_json()}]")
close_result = runner.invoke(app, ["close", "--config", str(config_file)])
close_result = runner.invoke(
app, ["close", "--device", str(encrypted_btrfs_device)]
)
assert close_result.stdout == ""
assert close_result.exit_code == 0

Expand Down

0 comments on commit fdaf1d5

Please sign in to comment.