src/backup/prune.rs: implement --keep-hourly
This commit is contained in:
		| @ -425,12 +425,16 @@ following retention options: | ||||
| ``--keep-last <N>`` | ||||
|   Keep the last ``<N>`` backup snapshots. | ||||
|  | ||||
| ``--keep-hourly <N>`` | ||||
|   Keep backups for the last ``<N>`` different hours. If there is more than one | ||||
|   backup for a single hour, only the latest one is kept. | ||||
|  | ||||
| ``--keep-daily <N>`` | ||||
|   Keep backups for ``<N>`` different days. If there is more than one | ||||
|   Keep backups for the last ``<N>`` different days. If there is more than one | ||||
|   backup for a single day, only the latest one is kept. | ||||
|  | ||||
| ``--keep-weekly <N>`` | ||||
|   Keep backups for ``<N>`` different weeks. If there is more than one | ||||
|   Keep backups for the last ``<N>`` different weeks. If there is more than one | ||||
|   backup for a single week, only the latest one is kept. | ||||
|  | ||||
|   .. note:: The weeks start on Monday and end on Sunday. The software | ||||
| @ -438,11 +442,11 @@ following retention options: | ||||
|      the end of the year. | ||||
|  | ||||
| ``--keep-monthly <N>`` | ||||
|   Keep backups for ``<N>`` different months. If there is more than one | ||||
|   Keep backups for the last ``<N>`` different months. If there is more than one | ||||
|   backup for a single month, only the latest one is kept. | ||||
|  | ||||
| ``--keep-yearly <N>`` | ||||
|   Keep backups for ``<N>`` different year. If there is more than one | ||||
|   Keep backups for the last ``<N>`` different years. If there is more than one | ||||
|   backup for a single year, only the latest one is kept. | ||||
|  | ||||
|  | ||||
|  | ||||
| @ -247,6 +247,13 @@ macro_rules! add_common_prune_prameters { | ||||
|                     .minimum(1) | ||||
|                     .schema() | ||||
|             ), | ||||
|             ( | ||||
|                 "keep-hourly", | ||||
|                 true, | ||||
|                 &IntegerSchema::new("Number of hourly backups to keep.") | ||||
|                     .minimum(1) | ||||
|                     .schema() | ||||
|             ), | ||||
|             ( | ||||
|                 "keep-last", | ||||
|                 true, | ||||
| @ -310,6 +317,7 @@ fn test_prune( | ||||
|  | ||||
|     let prune_options = PruneOptions { | ||||
|         keep_last: param["keep-last"].as_u64(), | ||||
|         keep_hourly: param["keep-hourly"].as_u64(), | ||||
|         keep_daily: param["keep-daily"].as_u64(), | ||||
|         keep_weekly: param["keep-weekly"].as_u64(), | ||||
|         keep_monthly: param["keep-monthly"].as_u64(), | ||||
| @ -368,6 +376,7 @@ fn prune( | ||||
|  | ||||
|     let prune_options = PruneOptions { | ||||
|         keep_last: param["keep-last"].as_u64(), | ||||
|         keep_hourly: param["keep-hourly"].as_u64(), | ||||
|         keep_daily: param["keep-daily"].as_u64(), | ||||
|         keep_weekly: param["keep-weekly"].as_u64(), | ||||
|         keep_monthly: param["keep-monthly"].as_u64(), | ||||
|  | ||||
| @ -2,7 +2,7 @@ use failure::*; | ||||
| use std::collections::{HashMap, HashSet}; | ||||
| use std::path::PathBuf; | ||||
|  | ||||
| use chrono::{DateTime, Datelike, Local}; | ||||
| use chrono::{DateTime, Timelike, Datelike, Local}; | ||||
|  | ||||
| use super::{BackupDir, BackupInfo}; | ||||
|  | ||||
| @ -71,6 +71,7 @@ fn remove_incomplete_snapshots( | ||||
|  | ||||
| pub struct PruneOptions { | ||||
|     pub keep_last: Option<u64>, | ||||
|     pub keep_hourly: Option<u64>, | ||||
|     pub keep_daily: Option<u64>, | ||||
|     pub keep_weekly: Option<u64>, | ||||
|     pub keep_monthly: Option<u64>, | ||||
| @ -82,6 +83,7 @@ impl PruneOptions { | ||||
|     pub fn new() -> Self { | ||||
|         Self { | ||||
|             keep_last: None, | ||||
|             keep_hourly: None, | ||||
|             keep_daily: None, | ||||
|             keep_weekly: None, | ||||
|             keep_monthly: None, | ||||
| @ -89,6 +91,11 @@ impl PruneOptions { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn keep_hourly(mut self, value: Option<u64>) -> Self { | ||||
|         self.keep_hourly = value; | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     pub fn keep_last(mut self, value: Option<u64>) -> Self { | ||||
|         self.keep_last = value; | ||||
|         self | ||||
| @ -117,6 +124,7 @@ impl PruneOptions { | ||||
|     pub fn keeps_something(&self) -> bool { | ||||
|         let mut keep_something = false; | ||||
|         if let Some(count) = self.keep_last { if count > 0 { keep_something = true; } } | ||||
|         if let Some(count) = self.keep_hourly { if count > 0 { keep_something = true; } } | ||||
|         if let Some(count) = self.keep_daily { if count > 0 { keep_something = true; } } | ||||
|         if let Some(count) = self.keep_weekly { if count > 0 { keep_something = true; } } | ||||
|         if let Some(count) = self.keep_monthly { if count > 0 { keep_something = true; } } | ||||
| @ -142,6 +150,13 @@ pub fn compute_prune_info( | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     if let Some(keep_hourly) = options.keep_hourly { | ||||
|         mark_selections(&mut mark, &list, keep_hourly as usize, |local_time, _info| { | ||||
|             format!("{}/{}/{}/{}", local_time.year(), local_time.month(), | ||||
|                     local_time.day(), local_time.hour()) | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     if let Some(keep_daily) = options.keep_daily { | ||||
|         mark_selections(&mut mark, &list, keep_daily as usize, |local_time, _info| { | ||||
|             format!("{}/{}/{}", local_time.year(), local_time.month(), local_time.day()) | ||||
| @ -154,7 +169,7 @@ pub fn compute_prune_info( | ||||
|             let week = iso_week.week(); | ||||
|             // Note: This year number might not match the calendar year number. | ||||
|             let iso_week_year = iso_week.year(); | ||||
|             format!("{}/{}", iso_week_year, week); | ||||
|             format!("{}/{}", iso_week_year, week) | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|  | ||||
| @ -41,6 +41,40 @@ fn create_info( | ||||
|     BackupInfo { backup_dir, files } | ||||
| } | ||||
|  | ||||
| #[test] | ||||
| fn test_prune_hourly() -> Result<(), Error> { | ||||
|  | ||||
|     let mut orig_list = Vec::new(); | ||||
|  | ||||
|     orig_list.push(create_info("host/elsa/2019-11-15T09:39:15Z", false)); | ||||
|     orig_list.push(create_info("host/elsa/2019-11-15T10:49:15Z", false)); | ||||
|     orig_list.push(create_info("host/elsa/2019-11-15T10:59:15Z", false)); | ||||
|     orig_list.push(create_info("host/elsa/2019-11-15T11:39:15Z", false)); | ||||
|     orig_list.push(create_info("host/elsa/2019-11-15T11:49:15Z", false)); | ||||
|     orig_list.push(create_info("host/elsa/2019-11-15T11:59:15Z", false)); | ||||
|  | ||||
|     let list = orig_list.clone(); | ||||
|     let options = PruneOptions::new().keep_hourly(Some(3)); | ||||
|     let remove_list = get_prune_list(list, false, &options); | ||||
|     let expect: Vec<PathBuf> = vec![ | ||||
|         PathBuf::from("host/elsa/2019-11-15T10:49:15Z"), | ||||
|         PathBuf::from("host/elsa/2019-11-15T11:39:15Z"), | ||||
|         PathBuf::from("host/elsa/2019-11-15T11:49:15Z"), | ||||
|     ]; | ||||
|     assert_eq!(remove_list, expect); | ||||
|  | ||||
|     let list = orig_list.clone(); | ||||
|     let options = PruneOptions::new().keep_hourly(Some(2)); | ||||
|     let remove_list = get_prune_list(list, true, &options); | ||||
|     let expect: Vec<PathBuf> = vec![ | ||||
|         PathBuf::from("host/elsa/2019-11-15T10:59:15Z"), | ||||
|         PathBuf::from("host/elsa/2019-11-15T11:59:15Z"), | ||||
|     ]; | ||||
|     assert_eq!(remove_list, expect); | ||||
|  | ||||
|     Ok(()) | ||||
| } | ||||
|  | ||||
|     #[test] | ||||
| fn test_prune_simple2() -> Result<(), Error> { | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user