|
5 | 5 | import time |
6 | 6 | import sys |
7 | 7 |
|
| 8 | +import pyAesCrypt |
8 | 9 | import humanize |
9 | 10 |
|
10 | 11 | from src.database import Database, DatabaseType |
|
47 | 48 |
|
48 | 49 | for i, container in enumerate(containers): |
49 | 50 | database = Database(container, global_labels) |
| 51 | + dump_file = f"/dump/{container.name}.sql" |
| 52 | + failed = False |
50 | 53 |
|
51 | 54 | logging.info( |
52 | 55 | "[{}/{}] Processing container {} {} ({})".format( |
|
58 | 61 | ) |
59 | 62 | ) |
60 | 63 |
|
| 64 | + if database.type == DatabaseType.unknown: |
| 65 | + logging.error( |
| 66 | + "FAILED: Cannot read database type. Please specify via label." |
| 67 | + ) |
| 68 | + failed = True |
| 69 | + |
61 | 70 | logging.debug( |
62 | 71 | "Login {}@host:{} using Password: {}".format( |
63 | 72 | database.username, |
64 | 73 | database.port, |
65 | 74 | "YES" if len(database.password) > 0 else "NO", |
66 | 75 | ) |
67 | 76 | ) |
68 | | - if database.compress: |
69 | | - logging.debug("Compressing backup") |
70 | | - |
71 | | - if database.type == DatabaseType.unknown: |
72 | | - logging.error( |
73 | | - "FAILED: Cannot read database type. Please specify via label." |
74 | | - ) |
75 | 77 |
|
| 78 | + # Create dump |
76 | 79 | network.connect(container, aliases=[config.docker_target_name]) |
77 | | - dumpFile = f"/dump/{container.name}.sql" |
78 | | - error_code = 0 |
79 | | - error_text = "" |
80 | 80 |
|
81 | 81 | try: |
82 | 82 | env = os.environ.copy() |
|
95 | 95 | f" --ignore-database=mysql" |
96 | 96 | f" --ignore-database=information_schema" |
97 | 97 | f" --ignore-database=performance_schema" |
98 | | - f" > {dumpFile}" |
| 98 | + f" > {dump_file}" |
99 | 99 | ), |
100 | 100 | shell=True, |
101 | 101 | text=True, |
|
109 | 109 | f"pg_dumpall" |
110 | 110 | f" --host={config.docker_target_name}" |
111 | 111 | f" --username={database.username}" |
112 | | - f" > {dumpFile}" |
| 112 | + f" > {dump_file}" |
113 | 113 | ), |
114 | 114 | shell=True, |
115 | 115 | text=True, |
116 | 116 | capture_output=True, |
117 | 117 | env=env, |
118 | 118 | ).check_returncode() |
119 | 119 | except subprocess.CalledProcessError as e: |
120 | | - error_code = e.returncode |
121 | 120 | error_text = f"\n{e.stderr.strip()}".replace("\n", "\n> ").strip() |
| 121 | + logging.error( |
| 122 | + f"FAILED. Error while crating dump. Return Code: {e.returncode}; Error Output:" |
| 123 | + ) |
| 124 | + logging.error(f"{error_text}") |
| 125 | + failed = True |
| 126 | + |
| 127 | + if not failed and not os.path.exists(dump_file): |
| 128 | + logging.error( |
| 129 | + f"FAILED: Dump cannot be created due to an unknown error!" |
| 130 | + ) |
| 131 | + failed = True |
122 | 132 |
|
123 | 133 | network.disconnect(container) |
124 | 134 |
|
125 | | - if error_code > 0: |
126 | | - logging.error(f"FAILED. Return Code: {error_code}; Error Output:") |
127 | | - logging.error(f"{error_text}") |
128 | | - elif os.path.exists(dumpFile): |
129 | | - dump_size = os.path.getsize(dumpFile) |
| 135 | + dump_size = os.path.getsize(dump_file) |
| 136 | + |
| 137 | + # Compress pump |
| 138 | + if not failed and database.compress and dump_size > 0: |
| 139 | + logging.debug(f"Compressing dump (level: {database.compression_level})") |
| 140 | + compressed_dump_file = f"{dump_file}.gz" |
| 141 | + |
| 142 | + try: |
| 143 | + if os.path.exists(compressed_dump_file): |
| 144 | + os.remove(compressed_dump_file) |
130 | 145 |
|
131 | | - # Compress pump |
132 | | - if database.compress and dump_size > 0: |
133 | | - if os.path.exists(dumpFile + ".gz"): |
134 | | - os.remove(dumpFile + ".gz") |
135 | 146 | subprocess.check_output( |
136 | | - f'gzip -{database.compression_level} "{dumpFile}"', shell=True |
| 147 | + f'gzip -{database.compression_level} "{dump_file}"', shell=True |
| 148 | + ) |
| 149 | + except Exception as e: |
| 150 | + logging.error(f"FAILED: Error while compressing: {e}") |
| 151 | + failed = True |
| 152 | + |
| 153 | + processed_dump_size = os.path.getsize(compressed_dump_file) |
| 154 | + dump_file = compressed_dump_file |
| 155 | + else: |
| 156 | + database.compress = False |
| 157 | + |
| 158 | + # Encrypt dump |
| 159 | + if not failed and database.encrypt and dump_size > 0: |
| 160 | + logging.debug(f"Encrypting dump") |
| 161 | + encrypted_dump_file = f"{dump_file}.aes" |
| 162 | + |
| 163 | + try: |
| 164 | + if os.path.exists(encrypted_dump_file): |
| 165 | + os.remove(encrypted_dump_file) |
| 166 | + |
| 167 | + pyAesCrypt.encryptFile( |
| 168 | + dump_file, encrypted_dump_file, database.encryption_key |
137 | 169 | ) |
138 | | - dumpFile = dumpFile + ".gz" |
139 | | - compressed_size = os.path.getsize(dumpFile) |
140 | | - else: |
141 | | - database.compress = False |
| 170 | + os.remove(dump_file) |
| 171 | + except Exception as e: |
| 172 | + logging.error(f"FAILED: Error while encrypting: {e}") |
| 173 | + failed = True |
| 174 | + |
| 175 | + processed_dump_size = os.path.getsize(encrypted_dump_file) |
| 176 | + dump_file = encrypted_dump_file |
142 | 177 |
|
| 178 | + else: |
| 179 | + database.encrypt = False |
| 180 | + |
| 181 | + if not failed: |
143 | 182 | # Change Owner of dump |
144 | 183 | os.chown( |
145 | | - dumpFile, config.dump_uid, config.dump_gid |
| 184 | + dump_file, config.dump_uid, config.dump_gid |
146 | 185 | ) # pylint: disable=maybe-no-member |
147 | 186 |
|
148 | 187 | successful_count += 1 |
149 | 188 | logging.info( |
150 | 189 | "SUCCESS. Size: {}{}".format( |
151 | 190 | humanize.naturalsize(dump_size), |
152 | | - " (" + humanize.naturalsize(compressed_size) + " compressed)" |
153 | | - if database.compress |
| 191 | + " (" |
| 192 | + + humanize.naturalsize(processed_dump_size) |
| 193 | + + " compressed/encrypted)" |
| 194 | + if database.compress or database.encrypt |
154 | 195 | else "", |
155 | 196 | ) |
156 | 197 | ) |
157 | | - else: |
158 | | - logging.error("Dump file not found!") |
159 | 198 |
|
160 | 199 | network.disconnect(own_container.id) |
161 | 200 | network.remove() |
|
0 commit comments