Skip to content

Commit 7159248

Browse files
authored
Merge pull request #4697 from msupply-foundation/4696-Limit-number-of-backups
Add backup cleanup to backup script
2 parents 67a510d + 36b8266 commit 7159248

File tree

6 files changed

+56
-5
lines changed

6 files changed

+56
-5
lines changed

server/cli/src/backup/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ You will need to specify a backup folder in the configuration `.yaml` files - to
3030
3131
Backup will contain a folder with all of the app_data (plugins, static files, etc..) and a folder with either sqlite files or postgres database dump.
3232
33+
`max_number_of_backups` in configuration `.yaml` file can be used to limit number of backups that will be kept in backup folder, this will be checked during each backup and extra backup folder will be deleted
34+
3335
### Restore
3436
3537
To restore run:

server/cli/src/backup/backup.rs

+47-3
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,17 @@ pub(crate) fn backup(settings: &Settings) -> Result<(), BackupError> {
1010
pg_bin_dir,
1111
} = get_dirs_from_settings(settings)?;
1212

13+
let max_number_of_backups = settings
14+
.backup
15+
.as_ref()
16+
.map(|b| b.max_number_of_backups)
17+
.flatten();
18+
1319
let Dirs {
1420
backup_name,
1521
file_dir,
1622
database_dir,
23+
backups_dir,
1724
} = create_backup_dir(backup_dir)?;
1825

1926
copy_files(settings, &file_dir)?;
@@ -25,6 +32,8 @@ pub(crate) fn backup(settings: &Settings) -> Result<(), BackupError> {
2532
copy_sqlite_files(settings, &database_dir)?;
2633
}
2734

35+
cleanup_backups(&backups_dir, max_number_of_backups)?;
36+
2837
println!("Backup completed in folder {backup_name}");
2938
Ok(())
3039
}
@@ -33,6 +42,7 @@ struct Dirs {
3342
backup_name: String,
3443
file_dir: PathBuf,
3544
database_dir: PathBuf,
45+
backups_dir: PathBuf,
3646
}
3747

3848
fn create_backup_dir(output_dir: String) -> Result<Dirs, BackupError> {
@@ -41,9 +51,10 @@ fn create_backup_dir(output_dir: String) -> Result<Dirs, BackupError> {
4151
.format("D%Y_%m_%dT%H_%M_%S")
4252
.to_string();
4353

44-
let base_dir = PathBuf::from_str(&output_dir)
45-
.map_err(|_| BackupError::InvalidPath(output_dir.to_string()))?
46-
.join(&backup_name);
54+
let backups_dir = PathBuf::from_str(&output_dir)
55+
.map_err(|_| BackupError::InvalidPath(output_dir.to_string()))?;
56+
57+
let base_dir = backups_dir.join(&backup_name);
4758

4859
fs::create_dir_all(&base_dir)
4960
.map_err(|e| BackupError::CannotCreateBackupFolder(e, base_dir.clone()))?;
@@ -60,6 +71,7 @@ fn create_backup_dir(output_dir: String) -> Result<Dirs, BackupError> {
6071
backup_name,
6172
file_dir,
6273
database_dir,
74+
backups_dir,
6375
})
6476
}
6577

@@ -137,3 +149,35 @@ fn copy_sqlite_files(
137149

138150
Ok(())
139151
}
152+
153+
fn cleanup_backups(
154+
backups_dir: &PathBuf,
155+
max_number_of_backups: Option<u32>,
156+
) -> Result<(), BackupError> {
157+
let Some(max_number_of_backups) = &max_number_of_backups else {
158+
return Ok(());
159+
};
160+
161+
let mut paths: Vec<PathBuf> = fs::read_dir(backups_dir)?
162+
.into_iter()
163+
.filter_map(Result::ok)
164+
.map(|e| e.path())
165+
.filter(|f| f.is_dir())
166+
.collect();
167+
168+
paths.sort();
169+
170+
let max_number_of_backups = *max_number_of_backups as usize;
171+
let number_of_backups_to_delete = if paths.len() <= max_number_of_backups {
172+
0
173+
} else {
174+
paths.len() - max_number_of_backups
175+
};
176+
177+
for path in paths.iter().take(number_of_backups_to_delete) {
178+
println!("Deleting old backup: {:?}", path);
179+
let _ = fs::remove_dir_all(path);
180+
}
181+
182+
Ok(())
183+
}

server/cli/src/backup/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ fn get_dirs_from_settings(settings: &Settings) -> Result<DirSettings, BackupErro
7575
let Some(BackupSettings {
7676
backup_dir,
7777
pg_bin_dir,
78+
..
7879
}) = settings.backup.clone()
7980
else {
8081
return Err(BackupError::BackupConfigurationMissing);

server/cli/src/cli.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,9 @@ enum Action {
134134
#[clap(short, long)]
135135
sub_context: Option<String>,
136136
},
137-
/// Will back up database to a generated folder (the name of which will be returned). Folder will be generated in the backup directory specified by configuration file
137+
/// Will back up database to a generated folder (the name of which will be returned).
138+
/// Folder will be generated in the backup directory specified by configuration file.
139+
/// User can specify max number of backup to keep, see example configuration file
138140
Backup,
139141
Restore(RestoreArguments),
140142
}

server/configuration/example.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,4 @@
4040
# backup: # see cli backup/restore
4141
# backup_dir: "~/Documents/omSupply_backup"
4242
# pg_bin_dir: "/Applications/Postgres.app/Contents/Versions/16/bin" # Optional
43-
43+
# max_number_of_backups: 10 # Optional, defaults to unlimited

server/service/src/settings.rs

+2
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ pub struct BackupSettings {
4848
pub backup_dir: String,
4949
// Directory containing postgres binaries (in case pg_dump and pg_restore are not in PATH)
5050
pub pg_bin_dir: Option<String>,
51+
// Number of backups to keep
52+
pub max_number_of_backups: Option<u32>,
5153
}
5254

5355
pub fn is_develop() -> bool {

0 commit comments

Comments
 (0)