12
12
import tempfile
13
13
from datetime import datetime , timezone
14
14
from pathlib import Path
15
- from subprocess import PIPE , TimeoutExpired , run
16
- from typing import Dict , List , Optional , Tuple
15
+ from subprocess import TimeoutExpired , run
17
16
18
17
import boto3 as boto3
19
18
import botocore
@@ -92,7 +91,7 @@ def _tls_ca_chain_filename(self) -> str:
92
91
return f"{ self .charm ._storage_path } /pgbackrest-tls-ca-chain.crt"
93
92
return ""
94
93
95
- def _are_backup_settings_ok (self ) -> Tuple [bool , Optional [ str ] ]:
94
+ def _are_backup_settings_ok (self ) -> tuple [bool , str | None ]:
96
95
"""Validates whether backup settings are OK."""
97
96
if self .model .get_relation (self .relation_name ) is None :
98
97
return (
@@ -112,17 +111,18 @@ def _can_initialise_stanza(self) -> bool:
112
111
# Don't allow stanza initialisation if this unit hasn't started the database
113
112
# yet and either hasn't joined the peer relation yet or hasn't configured TLS
114
113
# yet while other unit already has TLS enabled.
115
- if not self .charm ._patroni .member_started and (
116
- (len (self .charm ._peers .data .keys ()) == 2 )
117
- or (
118
- "tls" not in self .charm .unit_peer_data
119
- and any ("tls" in unit_data for _ , unit_data in self .charm ._peers .data .items ())
114
+ return not (
115
+ not self .charm ._patroni .member_started
116
+ and (
117
+ (len (self .charm ._peers .data .keys ()) == 2 )
118
+ or (
119
+ "tls" not in self .charm .unit_peer_data
120
+ and any ("tls" in unit_data for _ , unit_data in self .charm ._peers .data .items ())
121
+ )
120
122
)
121
- ):
122
- return False
123
- return True
123
+ )
124
124
125
- def _can_unit_perform_backup (self ) -> Tuple [bool , Optional [ str ] ]:
125
+ def _can_unit_perform_backup (self ) -> tuple [bool , str | None ]:
126
126
"""Validates whether this unit can perform a backup."""
127
127
if self .charm .is_blocked :
128
128
return False , "Unit is in a blocking state"
@@ -154,7 +154,7 @@ def _can_unit_perform_backup(self) -> Tuple[bool, Optional[str]]:
154
154
155
155
return self ._are_backup_settings_ok ()
156
156
157
- def can_use_s3_repository (self ) -> Tuple [bool , Optional [ str ] ]:
157
+ def can_use_s3_repository (self ) -> tuple [bool , str | None ]:
158
158
"""Returns whether the charm was configured to use another cluster repository."""
159
159
# Prevent creating backups and storing in another cluster repository.
160
160
try :
@@ -166,10 +166,8 @@ def can_use_s3_repository(self) -> Tuple[bool, Optional[str]]:
166
166
# Raise an error if the connection timeouts, so the user has the possibility to
167
167
# fix network issues and call juju resolve to re-trigger the hook that calls
168
168
# this method.
169
- logger .error (
170
- f"error: { str (e )} - please fix the error and call juju resolve on this unit"
171
- )
172
- raise TimeoutError
169
+ logger .error (f"error: { e !s} - please fix the error and call juju resolve on this unit" )
170
+ raise TimeoutError from e
173
171
174
172
else :
175
173
if return_code != 0 :
@@ -184,11 +182,11 @@ def can_use_s3_repository(self) -> Tuple[bool, Optional[str]]:
184
182
])
185
183
if return_code != 0 :
186
184
raise Exception (error )
187
- system_identifier_from_instance = [
185
+ system_identifier_from_instance = next (
188
186
line
189
187
for line in system_identifier_from_instance .splitlines ()
190
188
if "Database system identifier" in line
191
- ][ 0 ] .split (" " )[- 1 ]
189
+ ) .split (" " )[- 1 ]
192
190
system_identifier_from_stanza = str (stanza .get ("db" )[0 ]["system-id" ])
193
191
if system_identifier_from_instance != system_identifier_from_stanza or stanza .get (
194
192
"name"
@@ -207,7 +205,7 @@ def _change_connectivity_to_database(self, connectivity: bool) -> None:
207
205
self .charm .unit_peer_data .update ({"connectivity" : "on" if connectivity else "off" })
208
206
self .charm .update_config ()
209
207
210
- def _construct_endpoint (self , s3_parameters : Dict ) -> str :
208
+ def _construct_endpoint (self , s3_parameters : dict ) -> str :
211
209
"""Construct the S3 service endpoint using the region.
212
210
213
211
This is needed when the provided endpoint is from AWS, and it doesn't contain the region.
@@ -260,9 +258,7 @@ def _create_bucket_if_not_exists(self) -> None:
260
258
# Re-raise the error if the connection timeouts, so the user has the possibility to
261
259
# fix network issues and call juju resolve to re-trigger the hook that calls
262
260
# this method.
263
- logger .error (
264
- f"error: { str (e )} - please fix the error and call juju resolve on this unit"
265
- )
261
+ logger .error (f"error: { e !s} - please fix the error and call juju resolve on this unit" )
266
262
raise e
267
263
except ClientError :
268
264
logger .warning ("Bucket %s doesn't exist or you don't have access to it." , bucket_name )
@@ -286,17 +282,17 @@ def _empty_data_files(self) -> bool:
286
282
if path .exists () and path .is_dir ():
287
283
shutil .rmtree (path )
288
284
except OSError as e :
289
- logger .warning (f"Failed to remove contents of the data directory with error: { str ( e ) } " )
285
+ logger .warning (f"Failed to remove contents of the data directory with error: { e !s } " )
290
286
return False
291
287
292
288
return True
293
289
294
290
def _execute_command (
295
291
self ,
296
- command : List [str ],
297
- command_input : bytes = None ,
298
- timeout : int = None ,
299
- ) -> Tuple [int , str , str ]:
292
+ command : list [str ],
293
+ command_input : bytes | None = None ,
294
+ timeout : int | None = None ,
295
+ ) -> tuple [int , str , str ]:
300
296
"""Execute a command in the workload container."""
301
297
302
298
def demote ():
@@ -308,11 +304,11 @@ def result():
308
304
309
305
return result
310
306
311
- process = run (
307
+ # Input is generated by the charm
308
+ process = run ( # noqa: S603
312
309
command ,
313
310
input = command_input ,
314
- stdout = PIPE ,
315
- stderr = PIPE ,
311
+ capture_output = True ,
316
312
preexec_fn = demote (),
317
313
timeout = timeout ,
318
314
)
@@ -349,17 +345,7 @@ def _format_backup_list(self, backup_list) -> str:
349
345
path ,
350
346
) in backup_list :
351
347
backups .append (
352
- "{:<20s} | {:<19s} | {:<8s} | {:<20s} | {:<23s} | {:<20s} | {:<20s} | {:<8s} | {:s}" .format (
353
- backup_id ,
354
- backup_action ,
355
- backup_status ,
356
- reference ,
357
- lsn_start_stop ,
358
- start ,
359
- stop ,
360
- backup_timeline ,
361
- path ,
362
- )
348
+ f"{ backup_id :<20s} | { backup_action :<19s} | { backup_status :<8s} | { reference :<20s} | { lsn_start_stop :<23s} | { start :<20s} | { stop :<20s} | { backup_timeline :<8s} | { path :s} "
363
349
)
364
350
return "\n " .join (backups )
365
351
@@ -414,7 +400,7 @@ def _generate_backup_list_output(self) -> str:
414
400
backup_path ,
415
401
))
416
402
417
- for timeline , (timeline_stanza , timeline_id ) in self ._list_timelines ().items ():
403
+ for timeline , (_ , timeline_id ) in self ._list_timelines ().items ():
418
404
backup_list .append ((
419
405
timeline ,
420
406
"restore" ,
@@ -542,7 +528,7 @@ def _parse_psql_timestamp(self, timestamp: str) -> datetime:
542
528
dt = dt .astimezone (tz = timezone .utc )
543
529
return dt .replace (tzinfo = None )
544
530
545
- def _parse_backup_id (self , label ) -> Tuple [str , str ]:
531
+ def _parse_backup_id (self , label ) -> tuple [str , str ]:
546
532
"""Parse backup ID as a timestamp and its type."""
547
533
if label [- 1 ] == "F" :
548
534
timestamp = label
@@ -751,7 +737,7 @@ def _on_s3_credential_gone(self, _) -> None:
751
737
if self .charm .is_blocked and self .charm .unit .status .message in S3_BLOCK_MESSAGES :
752
738
self .charm .unit .status = ActiveStatus ()
753
739
754
- def _on_create_backup_action (self , event ) -> None : # noqa: C901
740
+ def _on_create_backup_action (self , event ) -> None :
755
741
"""Request that pgBackRest creates a backup."""
756
742
backup_type = event .params .get ("type" , "full" )
757
743
if backup_type not in BACKUP_TYPE_OVERRIDES :
@@ -788,7 +774,7 @@ def _on_create_backup_action(self, event) -> None: # noqa: C901
788
774
Model Name: { self .model .name }
789
775
Application Name: { self .model .app .name }
790
776
Unit Name: { self .charm .unit .name }
791
- Juju Version: { str ( juju_version ) }
777
+ Juju Version: { juju_version !s }
792
778
"""
793
779
if not self ._upload_content_to_s3 (
794
780
metadata ,
@@ -826,7 +812,7 @@ def _on_create_backup_action(self, event) -> None: # noqa: C901
826
812
def _run_backup (
827
813
self ,
828
814
event : ActionEvent ,
829
- s3_parameters : Dict ,
815
+ s3_parameters : dict ,
830
816
datetime_backup_requested : str ,
831
817
backup_type : str ,
832
818
) -> None :
@@ -920,7 +906,7 @@ def _on_list_backups_action(self, event) -> None:
920
906
event .set_results ({"backups" : formatted_list })
921
907
except ListBackupsError as e :
922
908
logger .exception (e )
923
- event .fail (f"Failed to list PostgreSQL backups with error: { str ( e ) } " )
909
+ event .fail (f"Failed to list PostgreSQL backups with error: { e !s } " )
924
910
925
911
def _on_restore_action (self , event ): # noqa: C901
926
912
"""Request that pgBackRest restores a backup."""
@@ -940,10 +926,8 @@ def _on_restore_action(self, event): # noqa: C901
940
926
try :
941
927
backups = self ._list_backups (show_failed = False )
942
928
timelines = self ._list_timelines ()
943
- is_backup_id_real = backup_id and backup_id in backups .keys ()
944
- is_backup_id_timeline = (
945
- backup_id and not is_backup_id_real and backup_id in timelines .keys ()
946
- )
929
+ is_backup_id_real = backup_id and backup_id in backups
930
+ is_backup_id_timeline = backup_id and not is_backup_id_real and backup_id in timelines
947
931
if backup_id and not is_backup_id_real and not is_backup_id_timeline :
948
932
error_message = f"Invalid backup-id: { backup_id } "
949
933
logger .error (f"Restore failed: { error_message } " )
@@ -1145,7 +1129,7 @@ def _render_pgbackrest_conf_file(self) -> bool:
1145
1129
self ._tls_ca_chain_filename , "\n " .join (s3_parameters ["tls-ca-chain" ]), 0o644
1146
1130
)
1147
1131
1148
- with open ("templates/pgbackrest.conf.j2" , "r" ) as file :
1132
+ with open ("templates/pgbackrest.conf.j2" ) as file :
1149
1133
template = Template (file .read ())
1150
1134
# Render the template file with the correct values.
1151
1135
rendered = template .render (
@@ -1177,7 +1161,7 @@ def _restart_database(self) -> None:
1177
1161
self .charm .update_config ()
1178
1162
self .charm ._patroni .start_patroni ()
1179
1163
1180
- def _retrieve_s3_parameters (self ) -> Tuple [ Dict , List [str ]]:
1164
+ def _retrieve_s3_parameters (self ) -> tuple [ dict , list [str ]]:
1181
1165
"""Retrieve S3 parameters from the S3 integrator relation."""
1182
1166
s3_parameters = self .s3_client .get_s3_connection_info ()
1183
1167
required_parameters = [
@@ -1254,7 +1238,7 @@ def _upload_content_to_s3(
1254
1238
self : str ,
1255
1239
content : str ,
1256
1240
s3_path : str ,
1257
- s3_parameters : Dict ,
1241
+ s3_parameters : dict ,
1258
1242
) -> bool :
1259
1243
"""Uploads the provided contents to the provided S3 bucket.
1260
1244
0 commit comments