Skip to content

Commit

Permalink
Fix additional folders and dated folder (#367)
Browse files Browse the repository at this point in the history
* Fix additional folders and dated folder

* Add tests

* Add docs
  • Loading branch information
Minituff authored Nov 4, 2024
1 parent ab3c636 commit 81ba0b8
Show file tree
Hide file tree
Showing 5 changed files with 81 additions and 6 deletions.
27 changes: 21 additions & 6 deletions app/backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,6 +479,19 @@ def _get_dest_dir(self, c: Container, src_dir_name: str) -> Tuple[Path, str]:

return dest_dir_full, dest_dir_no_path

def _format_dated_folder(self, base_dest_dir: Path, folder: str) -> Path:
"""Format the destination folder with the date"""
time_format = str(time.strftime(self.env.DEST_DATE_FORMAT))

if str(self.env.DEST_DATE_PATH_FORMAT) == "container/date":
dest_dir: Path = base_dest_dir / folder / time_format
elif str(self.env.DEST_DATE_PATH_FORMAT) == "date/container":
dest_dir: Path = base_dest_dir / time_format / folder

return dest_dir

return base_dest_dir

def _backup_additional_folders_standalone(self, when: BeforeOrAfter, base_dest_dir: Path):
"""Backup folders that are not associated with a container."""
additional_folders = str(self.env.ADDITIONAL_FOLDERS)
Expand Down Expand Up @@ -507,6 +520,13 @@ def _backup_additional_folders_standalone(self, when: BeforeOrAfter, base_dest_d

src_dir = base_src_dir / folder
dest_dir = base_dest_dir / folder

if str(self.env.ADDITIONAL_FOLDERS_USE_DEST_DATE_FOLDER).lower() == "true":
dest_dir = self._format_dated_folder(base_dest_dir, folder)

if not os.path.exists(dest_dir):
os.makedirs(dest_dir, exist_ok=True)

self.log_this(f"Backing up standalone additional folder '{folder}'")
self._run_rsync(None, rsync_args, src_dir, dest_dir)

Expand All @@ -524,12 +544,7 @@ def _backup_additional_folders(self, c: Container, base_dest_dir: Path):
dest_dir = base_dest_dir / folder

if str(self.env.USE_DEST_DATE_FOLDER).lower() == "true":
time_format = str(time.strftime(self.env.DEST_DATE_FORMAT))

if str(self.env.DEST_DATE_PATH_FORMAT) == "container/date":
dest_dir: Path = base_dest_dir / folder / time_format
elif str(self.env.DEST_DATE_PATH_FORMAT) == "date/container":
dest_dir: Path = base_dest_dir / time_format / folder
dest_dir = self._format_dated_folder(base_dest_dir, folder)

if not os.path.exists(dest_dir):
os.makedirs(dest_dir, exist_ok=True)
Expand Down
3 changes: 3 additions & 0 deletions app/defaults.env
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ HTTP_REST_API_PASSWORD=password
# When do backup the additional folders? "before", "after", or "both" the container backups
ADDITIONAL_FOLDERS_WHEN=before

# Use the destination date folder for the additional folders
ADDITIONAL_FOLDERS_USE_DEST_DATE_FOLDER=false

# Path to the Nautical database.
NAUTICAL_DB_PATH=/config
NAUTICAL_DB_NAME=nautical-db.json
Expand Down
1 change: 1 addition & 0 deletions app/nautical_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ def __init__(self) -> None:
# Not associated with containers
self.ADDITIONAL_FOLDERS = os.environ.get("ADDITIONAL_FOLDERS", "")
self.ADDITIONAL_FOLDERS_WHEN = os.environ.get("ADDITIONAL_FOLDERS_WHEN", "before")
self.ADDITIONAL_FOLDERS_USE_DEST_DATE_FOLDER = os.environ.get("ADDITIONAL_FOLDERS_USE_DEST_DATE_FOLDER", "")

self.SECONDARY_DEST_DIRS: List[Path] = []
for dir in os.environ.get("SECONDARY_DEST_DIRS", "").split(","):
Expand Down
10 changes: 10 additions & 0 deletions docs/arguments.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,16 @@ ADDITIONAL_FOLDERS_WHEN=after

<small>🔄 This is the same action as the [Additional Folders](./labels.md#additional-folders) label, but applied globally.</small>

### Additional folders date format
To enable the [Dated Destination](#create-a-dated-destination-folder) folder systax for Global Additional folders <small>(not folders tied to containers)</small>, then use this variable.

> **Default**: false <small>(use the base destination folder)</small>
```properties
ADDITIONAL_FOLDERS_USE_DEST_DATE_FOLDER=true
```
!!! note "The [destination folder format](#destination-folder-format) and [destination folder path](#destination-folder-path) enviornment variables will be respected."

## Secondary Destination Locations
Tell Nautical to backup folders to more destination locations--in addition to the normal destination folder <small>(/app/destination)</small>.

Expand Down
46 changes: 46 additions & 0 deletions pytest/test_backup.py
Original file line number Diff line number Diff line change
Expand Up @@ -1437,6 +1437,52 @@ def test_custom_destination_folders_are_used_in_rsync(
)
rm_tree(Path(self.dest_location) / time_format / "container1")

@mock.patch("subprocess.run")
@pytest.mark.parametrize("mock_container1", [{"name": "container1", "id": "123456789"}], indirect=True)
def test_additional_folders_and_DEST_DATE_PATH_FORMAT(
self,
mock_subprocess_run: MagicMock,
mock_docker_client: MagicMock,
mock_container1: MagicMock,
monkeypatch: pytest.MonkeyPatch,
):
"""Test additional folders env before"""

# Folders must be created before the backup is called
nautical_env = NauticalEnv()
create_folder(Path(nautical_env.SOURCE_LOCATION) / "container1-override", and_file=True)

# Skip stopping container1 and container2 (by name and id)
monkeypatch.setenv("ADDITIONAL_FOLDERS", "add1,add2")
monkeypatch.setenv("ADDITIONAL_FOLDERS_USE_DEST_DATE_FOLDER", "true")

mock_docker_client.containers.list.return_value = [mock_container1]
nb = NauticalBackup(mock_docker_client)
nb.backup()

time_format = time.strftime("%Y-%m-%d")

# Additional folder 1
assert mock_subprocess_run.call_args_list[0][0][0] == [
"-raq",
f"{self.src_location}/add1/",
f"{self.dest_location}/{time_format}/add1/",
]

# Additional folder 2
assert mock_subprocess_run.call_args_list[1][0][0] == [
"-raq",
f"{self.src_location}/add2/",
f"{self.dest_location}/{time_format}/add2/",
]

# Container
assert mock_subprocess_run.call_args_list[2][0][0] == [
"-raq",
f"{self.src_location}/container1/",
f"{self.dest_location}/container1/",
]

@mock.patch("subprocess.run")
@pytest.mark.parametrize("mock_container1", [{"name": "container1", "id": "123456789"}], indirect=True)
def test_additional_folders_env_before(
Expand Down

0 comments on commit 81ba0b8

Please sign in to comment.