backup-docker-to-local 1.0.0__tar.gz → 1.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {backup_docker_to_local-1.0.0/src/backup_docker_to_local.egg-info → backup_docker_to_local-1.1.1}/PKG-INFO +1 -1
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/pyproject.toml +1 -1
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1/src/backup_docker_to_local.egg-info}/PKG-INFO +1 -1
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/backup/app.py +35 -19
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/backup/cli.py +3 -19
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/backup/compose.py +3 -1
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/backup/db.py +26 -9
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/backup/docker.py +3 -1
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/backup/volume.py +6 -2
- backup_docker_to_local-1.1.1/src/baudolo/restore/__init__.py +1 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/restore/__main__.py +3 -1
- backup_docker_to_local-1.1.1/src/baudolo/restore/db/__init__.py +1 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/restore/db/mariadb.py +22 -4
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/restore/files.py +3 -1
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/seed/__main__.py +29 -11
- backup_docker_to_local-1.0.0/src/baudolo/restore/__init__.py +0 -1
- backup_docker_to_local-1.0.0/src/baudolo/restore/db/__init__.py +0 -1
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/LICENSE +0 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/README.md +0 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/setup.cfg +0 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/backup_docker_to_local.egg-info/SOURCES.txt +0 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/backup_docker_to_local.egg-info/dependency_links.txt +0 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/backup_docker_to_local.egg-info/entry_points.txt +0 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/backup_docker_to_local.egg-info/requires.txt +0 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/backup_docker_to_local.egg-info/top_level.txt +0 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/__init__.py +0 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/backup/__init__.py +0 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/backup/__main__.py +0 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/backup/shell.py +0 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/restore/db/postgres.py +0 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/restore/paths.py +0 -0
- {backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/restore/run.py +0 -0
|
@@ -51,7 +51,9 @@ def is_image_ignored(container: str, images_no_backup_required: list[str]) -> bo
|
|
|
51
51
|
return any(pat in img for pat in images_no_backup_required)
|
|
52
52
|
|
|
53
53
|
|
|
54
|
-
def volume_is_fully_ignored(
|
|
54
|
+
def volume_is_fully_ignored(
|
|
55
|
+
containers: list[str], images_no_backup_required: list[str]
|
|
56
|
+
) -> bool:
|
|
55
57
|
"""
|
|
56
58
|
Skip file backup only if all containers linked to the volume are ignored.
|
|
57
59
|
"""
|
|
@@ -70,28 +72,27 @@ def requires_stop(containers: list[str], images_no_stop_required: list[str]) ->
|
|
|
70
72
|
return True
|
|
71
73
|
return False
|
|
72
74
|
|
|
73
|
-
|
|
74
75
|
def backup_mariadb_or_postgres(
|
|
75
76
|
*,
|
|
76
77
|
container: str,
|
|
77
78
|
volume_dir: str,
|
|
78
79
|
databases_df: "pandas.DataFrame",
|
|
79
80
|
database_containers: list[str],
|
|
80
|
-
) -> bool:
|
|
81
|
+
) -> tuple[bool, bool]:
|
|
81
82
|
"""
|
|
82
|
-
Returns
|
|
83
|
+
Returns (is_db_container, dumped_any)
|
|
83
84
|
"""
|
|
84
85
|
for img in ["mariadb", "postgres"]:
|
|
85
86
|
if has_image(container, img):
|
|
86
|
-
backup_database(
|
|
87
|
+
dumped = backup_database(
|
|
87
88
|
container=container,
|
|
88
89
|
volume_dir=volume_dir,
|
|
89
90
|
db_type=img,
|
|
90
91
|
databases_df=databases_df,
|
|
91
92
|
database_containers=database_containers,
|
|
92
93
|
)
|
|
93
|
-
return True
|
|
94
|
-
return False
|
|
94
|
+
return True, dumped
|
|
95
|
+
return False, False
|
|
95
96
|
|
|
96
97
|
|
|
97
98
|
def _backup_dumps_for_volume(
|
|
@@ -100,21 +101,26 @@ def _backup_dumps_for_volume(
|
|
|
100
101
|
vol_dir: str,
|
|
101
102
|
databases_df: "pandas.DataFrame",
|
|
102
103
|
database_containers: list[str],
|
|
103
|
-
) -> bool:
|
|
104
|
+
) -> tuple[bool, bool]:
|
|
104
105
|
"""
|
|
105
|
-
|
|
106
|
-
Returns True if at least one dump was produced.
|
|
106
|
+
Returns (found_db_container, dumped_any)
|
|
107
107
|
"""
|
|
108
|
+
found_db = False
|
|
108
109
|
dumped_any = False
|
|
110
|
+
|
|
109
111
|
for c in containers:
|
|
110
|
-
|
|
112
|
+
is_db, dumped = backup_mariadb_or_postgres(
|
|
111
113
|
container=c,
|
|
112
114
|
volume_dir=vol_dir,
|
|
113
115
|
databases_df=databases_df,
|
|
114
116
|
database_containers=database_containers,
|
|
115
|
-
)
|
|
117
|
+
)
|
|
118
|
+
if is_db:
|
|
119
|
+
found_db = True
|
|
120
|
+
if dumped:
|
|
116
121
|
dumped_any = True
|
|
117
|
-
|
|
122
|
+
|
|
123
|
+
return found_db, dumped_any
|
|
118
124
|
|
|
119
125
|
|
|
120
126
|
def main() -> int:
|
|
@@ -135,18 +141,26 @@ def main() -> int:
|
|
|
135
141
|
containers = containers_using_volume(volume_name)
|
|
136
142
|
|
|
137
143
|
vol_dir = create_volume_directory(version_dir, volume_name)
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
_backup_dumps_for_volume(
|
|
144
|
+
|
|
145
|
+
found_db, dumped_any = _backup_dumps_for_volume(
|
|
141
146
|
containers=containers,
|
|
142
147
|
vol_dir=vol_dir,
|
|
143
148
|
databases_df=databases_df,
|
|
144
149
|
database_containers=args.database_containers,
|
|
145
150
|
)
|
|
146
151
|
|
|
147
|
-
# dump-only:
|
|
152
|
+
# dump-only logic:
|
|
148
153
|
if args.dump_only:
|
|
149
|
-
|
|
154
|
+
if found_db and not dumped_any:
|
|
155
|
+
print(
|
|
156
|
+
f"WARNING: dump-only requested but no DB dump was produced for DB volume '{volume_name}'. Falling back to file backup.",
|
|
157
|
+
flush=True,
|
|
158
|
+
)
|
|
159
|
+
# continue to file backup below
|
|
160
|
+
else:
|
|
161
|
+
# keep old behavior: skip file backups
|
|
162
|
+
continue
|
|
163
|
+
|
|
150
164
|
|
|
151
165
|
# skip file backup if all linked containers are ignored
|
|
152
166
|
if volume_is_fully_ignored(containers, args.images_no_backup_required):
|
|
@@ -178,6 +192,8 @@ def main() -> int:
|
|
|
178
192
|
print("Finished volume backups.", flush=True)
|
|
179
193
|
|
|
180
194
|
print("Handling Docker Compose services...", flush=True)
|
|
181
|
-
handle_docker_compose_services(
|
|
195
|
+
handle_docker_compose_services(
|
|
196
|
+
args.compose_dir, args.docker_compose_hard_restart_required
|
|
197
|
+
)
|
|
182
198
|
|
|
183
199
|
return 0
|
|
@@ -2,22 +2,6 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import argparse
|
|
4
4
|
import os
|
|
5
|
-
from pathlib import Path
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def _default_repo_name() -> str:
|
|
9
|
-
"""
|
|
10
|
-
Derive the repository name from the folder that contains `src/`.
|
|
11
|
-
|
|
12
|
-
Expected layout:
|
|
13
|
-
<repo-root>/src/baudolo/backup/cli.py
|
|
14
|
-
|
|
15
|
-
=> parents[0]=backup, [1]=baudolo, [2]=src, [3]=repo-root
|
|
16
|
-
"""
|
|
17
|
-
try:
|
|
18
|
-
return Path(__file__).resolve().parents[3].name
|
|
19
|
-
except Exception:
|
|
20
|
-
return "backup-docker-to-local"
|
|
21
5
|
|
|
22
6
|
|
|
23
7
|
def parse_args() -> argparse.Namespace:
|
|
@@ -41,7 +25,7 @@ def parse_args() -> argparse.Namespace:
|
|
|
41
25
|
|
|
42
26
|
p.add_argument(
|
|
43
27
|
"--repo-name",
|
|
44
|
-
default=
|
|
28
|
+
default="backup-docker-to-local",
|
|
45
29
|
help="Backup repo folder name under <backups-dir>/<machine-id>/ (default: git repo folder name)",
|
|
46
30
|
)
|
|
47
31
|
p.add_argument(
|
|
@@ -51,8 +35,8 @@ def parse_args() -> argparse.Namespace:
|
|
|
51
35
|
)
|
|
52
36
|
p.add_argument(
|
|
53
37
|
"--backups-dir",
|
|
54
|
-
default="/
|
|
55
|
-
help="Backup root directory (default: /
|
|
38
|
+
default="/var/lib/backup/",
|
|
39
|
+
help="Backup root directory (default: /var/lib/backup/)",
|
|
56
40
|
)
|
|
57
41
|
|
|
58
42
|
p.add_argument(
|
|
@@ -10,7 +10,9 @@ def hard_restart_docker_services(dir_path: str) -> None:
|
|
|
10
10
|
subprocess.run(["docker-compose", "up", "-d"], cwd=dir_path, check=True)
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def handle_docker_compose_services(
|
|
13
|
+
def handle_docker_compose_services(
|
|
14
|
+
parent_directory: str, hard_restart_required: list[str]
|
|
15
|
+
) -> None:
|
|
14
16
|
for entry in os.scandir(parent_directory):
|
|
15
17
|
if not entry.is_dir():
|
|
16
18
|
continue
|
|
@@ -3,11 +3,13 @@ from __future__ import annotations
|
|
|
3
3
|
import os
|
|
4
4
|
import pathlib
|
|
5
5
|
import re
|
|
6
|
-
|
|
6
|
+
import logging
|
|
7
7
|
import pandas
|
|
8
8
|
|
|
9
9
|
from .shell import BackupException, execute_shell_command
|
|
10
10
|
|
|
11
|
+
log = logging.getLogger(__name__)
|
|
12
|
+
|
|
11
13
|
|
|
12
14
|
def get_instance(container: str, database_containers: list[str]) -> str:
|
|
13
15
|
if container in database_containers:
|
|
@@ -30,19 +32,25 @@ def backup_database(
|
|
|
30
32
|
db_type: str,
|
|
31
33
|
databases_df: "pandas.DataFrame",
|
|
32
34
|
database_containers: list[str],
|
|
33
|
-
) ->
|
|
35
|
+
) -> bool:
|
|
36
|
+
"""
|
|
37
|
+
Returns True if at least one dump file was produced, else False.
|
|
38
|
+
"""
|
|
34
39
|
instance_name = get_instance(container, database_containers)
|
|
35
40
|
entries = databases_df.loc[databases_df["instance"] == instance_name]
|
|
36
41
|
if entries.empty:
|
|
37
|
-
|
|
42
|
+
log.warning("No entry found for instance '%s' (skipping DB dump)", instance_name)
|
|
43
|
+
return False
|
|
38
44
|
|
|
39
45
|
out_dir = os.path.join(volume_dir, "sql")
|
|
40
46
|
pathlib.Path(out_dir).mkdir(parents=True, exist_ok=True)
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
produced = False
|
|
49
|
+
|
|
50
|
+
for row in entries.itertuples(index=False):
|
|
51
|
+
db_name = row.database
|
|
52
|
+
user = row.username
|
|
53
|
+
password = row.password
|
|
46
54
|
|
|
47
55
|
dump_file = os.path.join(out_dir, f"{db_name}.backup.sql")
|
|
48
56
|
|
|
@@ -52,13 +60,15 @@ def backup_database(
|
|
|
52
60
|
f"-u {user} -p{password} {db_name} > {dump_file}"
|
|
53
61
|
)
|
|
54
62
|
execute_shell_command(cmd)
|
|
63
|
+
produced = True
|
|
55
64
|
continue
|
|
56
65
|
|
|
57
66
|
if db_type == "postgres":
|
|
58
67
|
cluster_file = os.path.join(out_dir, f"{instance_name}.cluster.backup.sql")
|
|
68
|
+
|
|
59
69
|
if not db_name:
|
|
60
70
|
fallback_pg_dumpall(container, user, password, cluster_file)
|
|
61
|
-
return
|
|
71
|
+
return True
|
|
62
72
|
|
|
63
73
|
try:
|
|
64
74
|
cmd = (
|
|
@@ -66,8 +76,15 @@ def backup_database(
|
|
|
66
76
|
f"pg_dump -U {user} -d {db_name} -h localhost > {dump_file}"
|
|
67
77
|
)
|
|
68
78
|
execute_shell_command(cmd)
|
|
79
|
+
produced = True
|
|
69
80
|
except BackupException as e:
|
|
70
81
|
print(f"pg_dump failed: {e}", flush=True)
|
|
71
|
-
print(
|
|
82
|
+
print(
|
|
83
|
+
f"Falling back to pg_dumpall for instance '{instance_name}'",
|
|
84
|
+
flush=True,
|
|
85
|
+
)
|
|
72
86
|
fallback_pg_dumpall(container, user, password, cluster_file)
|
|
87
|
+
produced = True
|
|
73
88
|
continue
|
|
89
|
+
|
|
90
|
+
return produced
|
|
@@ -37,7 +37,9 @@ def change_containers_status(containers: list[str], status: str) -> None:
|
|
|
37
37
|
def docker_volume_exists(volume: str) -> bool:
|
|
38
38
|
# Avoid throwing exceptions for exists checks.
|
|
39
39
|
try:
|
|
40
|
-
execute_shell_command(
|
|
40
|
+
execute_shell_command(
|
|
41
|
+
f"docker volume inspect {volume} >/dev/null 2>&1 && echo OK"
|
|
42
|
+
)
|
|
41
43
|
return True
|
|
42
44
|
except Exception:
|
|
43
45
|
return False
|
|
@@ -13,7 +13,9 @@ def get_storage_path(volume_name: str) -> str:
|
|
|
13
13
|
return f"{path}/"
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
def get_last_backup_dir(
|
|
16
|
+
def get_last_backup_dir(
|
|
17
|
+
versions_dir: str, volume_name: str, current_backup_dir: str
|
|
18
|
+
) -> str | None:
|
|
17
19
|
versions = sorted(os.listdir(versions_dir), reverse=True)
|
|
18
20
|
for version in versions:
|
|
19
21
|
candidate = os.path.join(versions_dir, version, volume_name, "files", "")
|
|
@@ -37,6 +39,8 @@ def backup_volume(versions_dir: str, volume_name: str, volume_dir: str) -> None:
|
|
|
37
39
|
execute_shell_command(cmd)
|
|
38
40
|
except BackupException as e:
|
|
39
41
|
if "file has vanished" in str(e):
|
|
40
|
-
print(
|
|
42
|
+
print(
|
|
43
|
+
"Warning: Some files vanished before transfer. Continuing.", flush=True
|
|
44
|
+
)
|
|
41
45
|
else:
|
|
42
46
|
raise
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__all__ = ["main"]
|
{backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/restore/__main__.py
RENAMED
|
@@ -66,7 +66,9 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
66
66
|
# ------------------------------------------------------------------
|
|
67
67
|
# mariadb
|
|
68
68
|
# ------------------------------------------------------------------
|
|
69
|
-
p_mdb = sub.add_parser(
|
|
69
|
+
p_mdb = sub.add_parser(
|
|
70
|
+
"mariadb", help="Restore a single MariaDB/MySQL-compatible dump"
|
|
71
|
+
)
|
|
70
72
|
_add_common_backup_args(p_mdb)
|
|
71
73
|
p_mdb.add_argument("--container", required=True)
|
|
72
74
|
p_mdb.add_argument("--db-name", required=True)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Database restore handlers (Postgres, MariaDB/MySQL)."""
|
{backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/restore/db/mariadb.py
RENAMED
|
@@ -23,7 +23,9 @@ exit 42
|
|
|
23
23
|
raise RuntimeError("empty client detection output")
|
|
24
24
|
return out
|
|
25
25
|
except Exception as e:
|
|
26
|
-
print(
|
|
26
|
+
print(
|
|
27
|
+
"ERROR: neither 'mariadb' nor 'mysql' found in container.", file=sys.stderr
|
|
28
|
+
)
|
|
27
29
|
raise e
|
|
28
30
|
|
|
29
31
|
|
|
@@ -47,7 +49,14 @@ def restore_mariadb_sql(
|
|
|
47
49
|
# MariaDB 11 images may not contain the mysql binary at all.
|
|
48
50
|
docker_exec(
|
|
49
51
|
container,
|
|
50
|
-
[
|
|
52
|
+
[
|
|
53
|
+
client,
|
|
54
|
+
"-u",
|
|
55
|
+
user,
|
|
56
|
+
f"--password={password}",
|
|
57
|
+
"-e",
|
|
58
|
+
"SET FOREIGN_KEY_CHECKS=0;",
|
|
59
|
+
],
|
|
51
60
|
)
|
|
52
61
|
|
|
53
62
|
result = docker_exec(
|
|
@@ -80,10 +89,19 @@ def restore_mariadb_sql(
|
|
|
80
89
|
|
|
81
90
|
docker_exec(
|
|
82
91
|
container,
|
|
83
|
-
[
|
|
92
|
+
[
|
|
93
|
+
client,
|
|
94
|
+
"-u",
|
|
95
|
+
user,
|
|
96
|
+
f"--password={password}",
|
|
97
|
+
"-e",
|
|
98
|
+
"SET FOREIGN_KEY_CHECKS=1;",
|
|
99
|
+
],
|
|
84
100
|
)
|
|
85
101
|
|
|
86
102
|
with open(sql_path, "rb") as f:
|
|
87
|
-
docker_exec(
|
|
103
|
+
docker_exec(
|
|
104
|
+
container, [client, "-u", user, f"--password={password}", db_name], stdin=f
|
|
105
|
+
)
|
|
88
106
|
|
|
89
107
|
print(f"MariaDB/MySQL restore complete for db '{db_name}'.")
|
|
@@ -6,7 +6,9 @@ import sys
|
|
|
6
6
|
from .run import run, docker_volume_exists
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def restore_volume_files(
|
|
9
|
+
def restore_volume_files(
|
|
10
|
+
volume_name: str, backup_files_dir: str, *, rsync_image: str
|
|
11
|
+
) -> int:
|
|
10
12
|
if not os.path.isdir(backup_files_dir):
|
|
11
13
|
print(f"ERROR: backup files dir not found: {backup_files_dir}", file=sys.stderr)
|
|
12
14
|
return 2
|
|
@@ -2,21 +2,24 @@ import pandas as pd
|
|
|
2
2
|
import argparse
|
|
3
3
|
import os
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
def check_and_add_entry(file_path, instance, database, username, password):
|
|
6
7
|
# Check if the file exists and is not empty
|
|
7
8
|
if os.path.exists(file_path) and os.path.getsize(file_path) > 0:
|
|
8
9
|
# Read the existing CSV file with header
|
|
9
|
-
df = pd.read_csv(file_path, sep=
|
|
10
|
+
df = pd.read_csv(file_path, sep=";")
|
|
10
11
|
else:
|
|
11
12
|
# Create a new DataFrame with columns if file does not exist
|
|
12
|
-
df = pd.DataFrame(columns=[
|
|
13
|
+
df = pd.DataFrame(columns=["instance", "database", "username", "password"])
|
|
13
14
|
|
|
14
15
|
# Check if the entry exists and remove it
|
|
15
16
|
mask = (
|
|
16
|
-
(df[
|
|
17
|
-
(
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
(df["instance"] == instance)
|
|
18
|
+
& (
|
|
19
|
+
(df["database"] == database)
|
|
20
|
+
| (((df["database"].isna()) | (df["database"] == "")) & (database == ""))
|
|
21
|
+
)
|
|
22
|
+
& (df["username"] == username)
|
|
20
23
|
)
|
|
21
24
|
|
|
22
25
|
if not df[mask].empty:
|
|
@@ -26,25 +29,40 @@ def check_and_add_entry(file_path, instance, database, username, password):
|
|
|
26
29
|
print("Adding new entry.")
|
|
27
30
|
|
|
28
31
|
# Create a new DataFrame for the new entry
|
|
29
|
-
new_entry = pd.DataFrame(
|
|
32
|
+
new_entry = pd.DataFrame(
|
|
33
|
+
[
|
|
34
|
+
{
|
|
35
|
+
"instance": instance,
|
|
36
|
+
"database": database,
|
|
37
|
+
"username": username,
|
|
38
|
+
"password": password,
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
)
|
|
30
42
|
|
|
31
43
|
# Add (or replace) the entry using concat
|
|
32
44
|
df = pd.concat([df, new_entry], ignore_index=True)
|
|
33
45
|
|
|
34
46
|
# Save the updated CSV file
|
|
35
|
-
df.to_csv(file_path, sep=
|
|
47
|
+
df.to_csv(file_path, sep=";", index=False)
|
|
48
|
+
|
|
36
49
|
|
|
37
50
|
def main():
|
|
38
|
-
parser = argparse.ArgumentParser(
|
|
51
|
+
parser = argparse.ArgumentParser(
|
|
52
|
+
description="Check and replace (or add) a database entry in a CSV file."
|
|
53
|
+
)
|
|
39
54
|
parser.add_argument("file_path", help="Path to the CSV file")
|
|
40
55
|
parser.add_argument("instance", help="Database instance")
|
|
41
56
|
parser.add_argument("database", help="Database name")
|
|
42
57
|
parser.add_argument("username", help="Username")
|
|
43
|
-
parser.add_argument("password", nargs=
|
|
58
|
+
parser.add_argument("password", nargs="?", default="", help="Password (optional)")
|
|
44
59
|
|
|
45
60
|
args = parser.parse_args()
|
|
46
61
|
|
|
47
|
-
check_and_add_entry(
|
|
62
|
+
check_and_add_entry(
|
|
63
|
+
args.file_path, args.instance, args.database, args.username, args.password
|
|
64
|
+
)
|
|
65
|
+
|
|
48
66
|
|
|
49
67
|
if __name__ == "__main__":
|
|
50
68
|
main()
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__all__ = ["main"]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
"""Database restore handlers (Postgres, MariaDB/MySQL)."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/backup/__init__.py
RENAMED
|
File without changes
|
{backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/backup/__main__.py
RENAMED
|
File without changes
|
|
File without changes
|
{backup_docker_to_local-1.0.0 → backup_docker_to_local-1.1.1}/src/baudolo/restore/db/postgres.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|