s3_rotate 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.rspec +1 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +55 -0
- data/LICENSE +21 -0
- data/README.md +241 -0
- data/examples/example.rb +7 -0
- data/lib/s3_rotate.rb +4 -0
- data/lib/s3_rotate/aws/s3_client.rb +93 -0
- data/lib/s3_rotate/core/backup_manager.rb +62 -0
- data/lib/s3_rotate/core/backup_rotator.rb +239 -0
- data/lib/s3_rotate/core/backup_uploader.rb +60 -0
- data/lib/s3_rotate/utils/file_utils.rb +65 -0
- data/s3_rotate.gemspec +15 -0
- data/spec/s3_rotate/aws/s3_client_spec.rb +128 -0
- data/spec/s3_rotate/core/backup_manager_spec.rb +56 -0
- data/spec/s3_rotate/core/backup_rotator_spec.rb +681 -0
- data/spec/s3_rotate/core/backup_uploader_spec.rb +145 -0
- data/spec/s3_rotate/utils/file_utils_spec.rb +61 -0
- data/spec/s3_rotate/utils/mock/backup-2020-01-02.test +0 -0
- data/spec/s3_rotate/utils/mock/backup-2020-01-03.test +0 -0
- data/spec/s3_rotate/utils/mock/backup-2020-02-01.test +0 -0
- data/spec/s3_rotate/utils/mock/backup-2021-01-01.test +0 -0
- data/spec/spec_helper.rb +15 -0
- metadata +76 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
require 's3_rotate/core/backup_uploader'
|
2
|
+
require 's3_rotate/core/backup_rotator'
|
3
|
+
|
4
|
+
module S3Rotate
|
5
|
+
|
6
|
+
class BackupManager
|
7
|
+
|
8
|
+
# attributes
|
9
|
+
attr_accessor :s3_client
|
10
|
+
attr_accessor :uploader
|
11
|
+
attr_accessor :rotator
|
12
|
+
|
13
|
+
#
|
14
|
+
# Initialize a new BackupManager instance.
|
15
|
+
#
|
16
|
+
# @param key String representing the AWS ACCESS KEY ID.
|
17
|
+
# @param secret String representing the AWS ACCESS KEY SECRET.
|
18
|
+
# @param bucket String representing the name of the bucket ot use.
|
19
|
+
# @param region String representing the region to conect to.
|
20
|
+
#
|
21
|
+
# @return the newly instanciated object.
|
22
|
+
#
|
23
|
+
def initialize(key, secret, bucket, region)
|
24
|
+
@s3_client = S3Client.new(key, secret, bucket, region)
|
25
|
+
@uploader = BackupUploader.new(@s3_client)
|
26
|
+
@rotator = BackupRotator.new(@s3_client)
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Upload local backup files to AWS S3
|
31
|
+
# Only uploads new backups
|
32
|
+
# Only uploads backups as daily backups: use `rotate` to generate the weekly & monthly files
|
33
|
+
#
|
34
|
+
# @param backup_name String containing the name of the backup to upload
|
35
|
+
# @param local_backups_path String containing the path to the directory containing the backups
|
36
|
+
# @param date_regex Regex returning the date contained in the filename of each backup
|
37
|
+
#
|
38
|
+
# @return nothing
|
39
|
+
#
|
40
|
+
def upload(backup_name, local_backups_path, date_regex=/\d{4}-\d{2}-\d{2}/)
|
41
|
+
@uploader.upload(backup_name, local_backups_path, date_regex)
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Rotate files (local, daily, weekly, monthly) and apply maximum limits for each type
|
46
|
+
#
|
47
|
+
# @param backup_name String containing the name of the backup to rotate
|
48
|
+
# @param local_backups_path String containing the path to the directory containing the backups
|
49
|
+
# @param max_local Integer specifying the maximum number of local backups to keep
|
50
|
+
# @param max_daily Integer specifying the maximum number of daily backups to keep
|
51
|
+
# @param max_weekly Integer specifying the maximum number of weekly backups to keep
|
52
|
+
# @param max_monthly Integer specifying the maximum number of monthly backups to keep
|
53
|
+
#
|
54
|
+
# @return nothing
|
55
|
+
#
|
56
|
+
def rotate(backup_name, local_backups_dir, max_local=3, max_daily=7, max_weekly=4, max_monthly=3)
|
57
|
+
@rotator.rotate(backup_name, local_backups_dir, max_local, max_daily, max_weekly, max_monthly)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
# s3_rotate
|
2
|
+
require 's3_rotate/utils/file_utils'
|
3
|
+
|
4
|
+
module S3Rotate
|
5
|
+
|
6
|
+
#
|
7
|
+
# BackupRotator Class
|
8
|
+
# Handles backup rotation locally and on S3
|
9
|
+
#
|
10
|
+
class BackupRotator
|
11
|
+
|
12
|
+
# attributes
|
13
|
+
attr_accessor :s3_client
|
14
|
+
|
15
|
+
#
|
16
|
+
# Initialize a new BackupRotator instance.
|
17
|
+
#
|
18
|
+
# @param s3_client S3Client instance
|
19
|
+
#
|
20
|
+
# @return the newly instanciated object.
|
21
|
+
#
|
22
|
+
def initialize(s3_client)
|
23
|
+
@s3_client = s3_client
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Rotate files (local, daily, weekly, monthly) and apply maximum limits for each type
|
28
|
+
#
|
29
|
+
# @param backup_name String containing the name of the backup to rotate
|
30
|
+
# @param local_backups_path String containing the path to the directory containing the backups
|
31
|
+
# @param max_local Integer specifying the maximum number of local backups to keep
|
32
|
+
# @param max_daily Integer specifying the maximum number of daily backups to keep
|
33
|
+
# @param max_weekly Integer specifying the maximum number of weekly backups to keep
|
34
|
+
# @param max_monthly Integer specifying the maximum number of monthly backups to keep
|
35
|
+
#
|
36
|
+
# @return nothing
|
37
|
+
#
|
38
|
+
def rotate(backup_name, local_backups_dir, max_local=3, max_daily=7, max_weekly=4, max_monthly=3)
|
39
|
+
rotate_local(local_backups_dir, max_local)
|
40
|
+
rotate_daily(backup_name, max_daily)
|
41
|
+
rotate_weekly(backup_name, max_weekly)
|
42
|
+
rotate_monthly(backup_name, max_monthly)
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Rotate daily files
|
47
|
+
#
|
48
|
+
# @param backup_name String containing the name of the backup being rotated
|
49
|
+
# @param max_daily Integer specifying the maximum number of daily backups to keep
|
50
|
+
# - If there are less than `max_daily` daily files: do nothing
|
51
|
+
# - If there are more than `max_daily` daily files: delete the oldest files to leave `max_daily` files
|
52
|
+
#
|
53
|
+
# The rotation works as follows:
|
54
|
+
# - Less than 7 days datediff between the oldest daily file and the most recent weekly file: do nothing
|
55
|
+
# - More than 7 days datediff between the oldest daily file and the most recent weekly file: promote the oldest daily file to weekly file
|
56
|
+
# - In both cases, apply the `max_daily`
|
57
|
+
#
|
58
|
+
# @return nothing
|
59
|
+
#
|
60
|
+
def rotate_daily(backup_name, max_daily)
|
61
|
+
# get backup files
|
62
|
+
daily_backups = @s3_client.remote_backups(backup_name, "daily").files
|
63
|
+
weekly_backups = @s3_client.remote_backups(backup_name, "weekly").files
|
64
|
+
|
65
|
+
# get most recent weekly file
|
66
|
+
recent_weekly_file = weekly_backups.last
|
67
|
+
|
68
|
+
# look through daily backups to find which oness should be promoted
|
69
|
+
daily_backups.each do |backup|
|
70
|
+
# promote to weekly if applicable
|
71
|
+
if should_promote_daily_to_weekly?(backup.key, recent_weekly_file&.key)
|
72
|
+
recent_weekly_file = promote(backup_name, backup.key, backup.body, "weekly")
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# cleanup old files
|
77
|
+
if daily_backups.length > max_daily
|
78
|
+
daily_backups.each_with_index { |backup, i| backup.destroy if i < daily_backups.length - max_daily }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# Rotate weekly files
|
84
|
+
#
|
85
|
+
# @param backup_name String containing the name of the backup being rotated
|
86
|
+
# @param max_weekly Integer specifying the maximum number of weekly backups to keep
|
87
|
+
# - If there are less than `max_weekly` weekly files: do nothing
|
88
|
+
# - If there are more than `max_weekly` weekly files: delete the oldest files to leave `max_weekly` files
|
89
|
+
#
|
90
|
+
# The rotation works as follows:
|
91
|
+
# - Less than 1 month datediff between the oldest weekly file and the most recent monthly file: do nothing
|
92
|
+
# - More than 1 month datediff between the oldest weekly file and the most recent monthly file: promote the oldest daily file to weekly file
|
93
|
+
# - In both cases, apply the `max_weekly`
|
94
|
+
#
|
95
|
+
# @return nothing
|
96
|
+
#
|
97
|
+
def rotate_weekly(backup_name, max_weekly)
|
98
|
+
# get backup files
|
99
|
+
weekly_backups = @s3_client.remote_backups(backup_name, "weekly").files
|
100
|
+
monthly_backups = @s3_client.remote_backups(backup_name, "monthly").files
|
101
|
+
|
102
|
+
# get most recent monthly file
|
103
|
+
recent_monthly_file = monthly_backups.last
|
104
|
+
|
105
|
+
# look through weekly backups to find which oness should be promoted
|
106
|
+
weekly_backups.each do |backup|
|
107
|
+
# promote to monthly if applicable
|
108
|
+
if should_promote_weekly_to_monthly?(backup.key, recent_monthly_file&.key)
|
109
|
+
recent_monthly_file = promote(backup_name, backup.key, backup.body, "monthly")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# cleanup old files
|
114
|
+
if weekly_backups.length > max_weekly
|
115
|
+
weekly_backups.each_with_index { |backup, i| backup.destroy if i < weekly_backups.length - max_weekly }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# Rotate monthly files
|
121
|
+
#
|
122
|
+
# @param backup_name String containing the name of the backup being rotated
|
123
|
+
# @param max_monthly Integer specifying the maximum number of month backups to keep
|
124
|
+
# - If there are less than `max_monthly` monthly files: do nothing
|
125
|
+
# - If there are more than `max_monthly` monthly files: delete the oldest files to leave `max_monthly` files
|
126
|
+
#
|
127
|
+
# @return nothing
|
128
|
+
#
|
129
|
+
def rotate_monthly(backup_name, max_monthly)
|
130
|
+
# get backup files
|
131
|
+
monthly_backups = @s3_client.remote_backups(backup_name, "monthly").files
|
132
|
+
|
133
|
+
# cleanup old files
|
134
|
+
if monthly_backups.length > max_monthly
|
135
|
+
monthly_backups.each_with_index { |backup, i| backup.destroy if i < monthly_backups.length - max_monthly }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# Rotate local files
|
141
|
+
#
|
142
|
+
# @param local_backups_path String containing the path to the directory containing the backups
|
143
|
+
# @param max_local Integer specifying the maximum number of local backups to keep
|
144
|
+
# - If there are less than `max_local` local files: do nothing
|
145
|
+
# - If there are more than `max_local` local files: delete the oldest files to leave `max_local` files
|
146
|
+
#
|
147
|
+
# @return nothing
|
148
|
+
#
|
149
|
+
def rotate_local(local_backups_path, max_local)
|
150
|
+
# get backup files
|
151
|
+
local_backups = FileUtils::files_in_directory(local_backups_path)
|
152
|
+
|
153
|
+
# cleanup old files
|
154
|
+
if local_backups.length > max_local
|
155
|
+
local_backups[0..(local_backups.length - max_local - 1)].each { |backup| File.delete("#{local_backups_path}/#{backup}") }
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
#
|
160
|
+
# Check whether `daily_file` should be promoted into a weekly file
|
161
|
+
# Only promote a daily file if the most recent weekly backup is one week old
|
162
|
+
#
|
163
|
+
# @param daily_file String, filename of the daily backup to be checked for promotion
|
164
|
+
# @param weekly_file String, filename of the most recent weekly backup
|
165
|
+
#
|
166
|
+
# @return Boolean, True or False, whether the file should be promoted
|
167
|
+
#
|
168
|
+
def should_promote_daily_to_weekly?(daily_file, weekly_file)
|
169
|
+
# never promote if no daily file
|
170
|
+
return false if not daily_file
|
171
|
+
|
172
|
+
# always promote if no weekly file
|
173
|
+
return true if not weekly_file
|
174
|
+
|
175
|
+
# retrieve the date of each file
|
176
|
+
begin
|
177
|
+
date_daily_file = FileUtils::date_from_filename(daily_file)
|
178
|
+
date_weekly_file = FileUtils::date_from_filename(weekly_file)
|
179
|
+
rescue
|
180
|
+
print "Wrong date (Date.parse in should_promote_daily_to_weekly)."
|
181
|
+
return false
|
182
|
+
end
|
183
|
+
|
184
|
+
# perform date comparison
|
185
|
+
return (date_daily_file - date_weekly_file).abs >= 7
|
186
|
+
end
|
187
|
+
|
188
|
+
#
|
189
|
+
# Check whether `weekly_file` should be promoted into a monthly file
|
190
|
+
# Only promote a weekly file if the most recent monthly backup is one month old
|
191
|
+
#
|
192
|
+
# @param weekly_file String, filename of the weekly backup to be checked for promotion
|
193
|
+
# @param monthly_file String, filename of the most recent monthly backup
|
194
|
+
#
|
195
|
+
# @return Boolean, True or False, whether the file should be promoted
|
196
|
+
#
|
197
|
+
def should_promote_weekly_to_monthly?(weekly_file, monthly_file)
|
198
|
+
# never promote if no weekly file
|
199
|
+
return false if not weekly_file
|
200
|
+
|
201
|
+
# always promote if no monthly file
|
202
|
+
return true if not monthly_file
|
203
|
+
|
204
|
+
# retrieve the date of each file
|
205
|
+
begin
|
206
|
+
date_weekly_file = FileUtils::date_from_filename(weekly_file)
|
207
|
+
date_monthly_file = FileUtils::date_from_filename(monthly_file)
|
208
|
+
rescue
|
209
|
+
print "Wrong date (Date.parse in should_promote_weekly_to_monthly)."
|
210
|
+
return false
|
211
|
+
end
|
212
|
+
|
213
|
+
# perform date comparison
|
214
|
+
return date_weekly_file.prev_month >= date_monthly_file
|
215
|
+
end
|
216
|
+
|
217
|
+
#
|
218
|
+
# Promote a daily backup into a weekly backup
|
219
|
+
# This operation keeps the original daily file, and creates a new weekly backup
|
220
|
+
#
|
221
|
+
# @param backup_name String containing the name of the backup being updated
|
222
|
+
# @param filename String, filename of the backup you want to promote
|
223
|
+
# @param body String, body of the file you want to promote
|
224
|
+
# @param type String representing the type of backup being uploaded, one of "daily", "weekly" or "monthly"
|
225
|
+
#
|
226
|
+
# @return created S3 Bucket File
|
227
|
+
#
|
228
|
+
def promote(backup_name, filename, body, type)
|
229
|
+
# parse the date & extension
|
230
|
+
backup_date = FileUtils::date_from_filename(filename)
|
231
|
+
backup_extension = FileUtils::extension_from_filename(filename)
|
232
|
+
|
233
|
+
# upload
|
234
|
+
@s3_client.upload(backup_name, backup_date, type, backup_extension, body)
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# s3_rotate
|
2
|
+
require 's3_rotate/utils/file_utils'
|
3
|
+
|
4
|
+
module S3Rotate
|
5
|
+
|
6
|
+
#
|
7
|
+
# BackupUploader Class
|
8
|
+
# Handles backup uploads with the right format
|
9
|
+
#
|
10
|
+
class BackupUploader
|
11
|
+
|
12
|
+
# attributes
|
13
|
+
attr_accessor :s3_client
|
14
|
+
|
15
|
+
#
|
16
|
+
# Initialize a new BackupUploader instance.
|
17
|
+
#
|
18
|
+
# @param s3_client S3Client instance
|
19
|
+
#
|
20
|
+
# @return the newly instanciated object.
|
21
|
+
#
|
22
|
+
def initialize(s3_client)
|
23
|
+
@s3_client = s3_client
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Upload local backup files to AWS S3
|
28
|
+
# Only uploads new backups
|
29
|
+
# Only uploads backups as daily backups: use `rotate` to generate the weekly & monthly files
|
30
|
+
#
|
31
|
+
# @param backup_name String containing the name of the backup to upload
|
32
|
+
# @param local_backups_path String containing the path to the directory containing the backups
|
33
|
+
# @param date_regex Regex returning the date contained in the filename of each backup
|
34
|
+
#
|
35
|
+
# @return nothing
|
36
|
+
#
|
37
|
+
def upload(backup_name, local_backups_path, date_regex=/\d{4}-\d{2}-\d{2}/)
|
38
|
+
# get backup files
|
39
|
+
local_backups = FileUtils::files_in_directory(local_backups_path).reverse
|
40
|
+
|
41
|
+
# upload local backups until we find one backup already uploaded
|
42
|
+
local_backups.each do |local_backup|
|
43
|
+
# parse the date & extension
|
44
|
+
backup_date = FileUtils::date_from_filename(local_backup, date_regex)
|
45
|
+
backup_extension = FileUtils::extension_from_filename(local_backup)
|
46
|
+
|
47
|
+
# skip invalid files
|
48
|
+
next if not backup_date
|
49
|
+
|
50
|
+
# stop uploading once we reach a file already uploaded
|
51
|
+
break if @s3_client.exists?(backup_name, backup_date, "daily", extension=backup_extension)
|
52
|
+
|
53
|
+
# upload file
|
54
|
+
@s3_client.upload_local_backup_to_s3(backup_name, backup_date, "daily", backup_extension, File.open(local_backup))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module S3Rotate
|
4
|
+
|
5
|
+
module FileUtils
|
6
|
+
|
7
|
+
#
|
8
|
+
# Parse the date in a filename
|
9
|
+
# Date can be any format recognize by Date.parse, or be a timestamp
|
10
|
+
#
|
11
|
+
# @param filename String containing the filename to be parsed.
|
12
|
+
# @param date_regex Regex returning the date contained in the filename
|
13
|
+
#
|
14
|
+
# @return Date instance, representing the parsed date
|
15
|
+
#
|
16
|
+
def FileUtils.date_from_filename(filename, date_regex=/\d{4}-\d{2}-\d{2}/)
|
17
|
+
# match the date in the filename
|
18
|
+
match = filename.match(date_regex)
|
19
|
+
date_str = match&.captures&.first || match&.to_s
|
20
|
+
|
21
|
+
# if nothing could be match, immediately fail
|
22
|
+
raise "Invalid date_regex or filename format" if not date_str
|
23
|
+
|
24
|
+
# regular date
|
25
|
+
begin
|
26
|
+
if date_str.include?("-")
|
27
|
+
Date.parse(date_str)
|
28
|
+
# timestamp
|
29
|
+
else
|
30
|
+
DateTime.strptime(date_str, "%s").to_date
|
31
|
+
end
|
32
|
+
rescue
|
33
|
+
raise "Date format not supported"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Parse the extension in a filename
|
39
|
+
#
|
40
|
+
# @param filename String containing the filename to be parsed
|
41
|
+
#
|
42
|
+
# @return String containing the extension of the filename if relevant, None otherwise
|
43
|
+
#
|
44
|
+
def FileUtils.extension_from_filename(filename)
|
45
|
+
if filename.include?('.')
|
46
|
+
'.' + filename.split('/').last.split('.')[1..-1].join('.')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Get the list of files in the specified directory
|
52
|
+
#
|
53
|
+
# @param directory String containing the path to the directory
|
54
|
+
#
|
55
|
+
# @return array of filenames, in ascending date order
|
56
|
+
#
|
57
|
+
def FileUtils.files_in_directory(directory)
|
58
|
+
Dir.entries(directory).select { |f| !File.directory? f }.sort
|
59
|
+
rescue
|
60
|
+
raise "Invalid directory #{directory}"
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
data/s3_rotate.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = 's3_rotate'
|
5
|
+
s.version = '1.0.0'
|
6
|
+
s.homepage = 'https://github.com/Whova/s3_rotate'
|
7
|
+
s.date = Date.today.to_s
|
8
|
+
s.summary = "AWS S3 upload with rotation mechanism"
|
9
|
+
s.description = s.summary
|
10
|
+
s.authors = ["Simon Ninon"]
|
11
|
+
s.email = 'simon.ninon@gmail.com'
|
12
|
+
s.files = `git ls-files`.split("\n")
|
13
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
s.license = 'MIT'
|
15
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
# standard
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
# 3rd part
|
5
|
+
require 'fog-aws'
|
6
|
+
|
7
|
+
# s3_rotate
|
8
|
+
require File.expand_path("../../../../lib/s3_rotate/aws/s3_client", __FILE__)
|
9
|
+
|
10
|
+
describe S3Rotate::S3Client do
|
11
|
+
|
12
|
+
before :each do
|
13
|
+
@client = S3Rotate::S3Client.new('key', 'secret', 'bucket', 'region')
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#initialize' do
|
17
|
+
|
18
|
+
it 'sets the access_key' do
|
19
|
+
expect(@client.access_key).to eq 'key'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'sets the access_secret' do
|
23
|
+
expect(@client.access_secret).to eq 'secret'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'sets the bucket_name' do
|
27
|
+
expect(@client.bucket_name).to eq 'bucket'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'sets the region' do
|
31
|
+
expect(@client.region).to eq 'region'
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#connection' do
|
37
|
+
|
38
|
+
it 'sets the connection when unset' do
|
39
|
+
# mock
|
40
|
+
@client.connection = nil
|
41
|
+
|
42
|
+
# perform test
|
43
|
+
expect(@client.connection).not_to eq nil
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'does not set the connection when set' do
|
47
|
+
# mock
|
48
|
+
@client.connection = "some connection"
|
49
|
+
|
50
|
+
# perform test
|
51
|
+
expect(@client.connection).to eq "some connection"
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
describe '#bucket' do
|
57
|
+
|
58
|
+
it 'gets the bucket' do
|
59
|
+
expect(@client.bucket).not_to eq nil
|
60
|
+
expect(@client.bucket.key).to eq 'bucket'
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
describe '#remote_backups' do
|
66
|
+
|
67
|
+
before do
|
68
|
+
@client.connection.directories.get('bucket').files.create(key: '/backup_name/daily/2020-01-01.tgz', body: 'some data')
|
69
|
+
@client.connection.directories.get('bucket').files.create(key: '/backup_name/daily/2020-01-02.tgz', body: 'some data')
|
70
|
+
@client.connection.directories.get('bucket').files.create(key: '/backup_name/daily/2020-02-03.tgz', body: 'some data')
|
71
|
+
@client.connection.directories.get('bucket').files.create(key: '/backup_name/daily/2021-02-04.tgz', body: 'some data')
|
72
|
+
@client.connection.directories.get('bucket').files.create(key: '/backup_name/weekly/2020-01-03.tgz', body: 'some data')
|
73
|
+
@client.connection.directories.get('bucket').files.create(key: '/backup_name/monthly/2020-01-04.tgz', body: 'some data')
|
74
|
+
@client.connection.directories.get('bucket').files.create(key: '/other_backup_name/daily/2020-01-05.tgz', body: 'some data')
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'gets the remote backups' do
|
78
|
+
expect(@client.remote_backups('backup_name', 'daily')).not_to eq nil
|
79
|
+
expect(@client.remote_backups('backup_name', 'daily').files).not_to eq nil
|
80
|
+
expect(@client.remote_backups('backup_name', 'daily').files.length).to eq 4
|
81
|
+
expect(@client.remote_backups('backup_name', 'daily').files[0].key).to eq "/backup_name/daily/2020-01-01.tgz"
|
82
|
+
expect(@client.remote_backups('backup_name', 'daily').files[1].key).to eq "/backup_name/daily/2020-01-02.tgz"
|
83
|
+
expect(@client.remote_backups('backup_name', 'daily').files[2].key).to eq "/backup_name/daily/2020-02-03.tgz"
|
84
|
+
expect(@client.remote_backups('backup_name', 'daily').files[3].key).to eq "/backup_name/daily/2021-02-04.tgz"
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
describe '#exists?' do
|
90
|
+
|
91
|
+
before do
|
92
|
+
@client.connection.directories.get('bucket').files.create(key: '/backup_name/daily/2020-01-01.tgz', body: 'some data')
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'returns true for existing backups' do
|
96
|
+
expect(@client.exists?('backup_name', Date.new(2020, 1, 1), 'daily', '.tgz')).to eq true
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'returns false for wrong extension' do
|
100
|
+
expect(@client.exists?('backup_name', Date.new(2020, 1, 1), 'daily', '.tar.gz')).to eq false
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'returns false for wrong type' do
|
104
|
+
expect(@client.exists?('backup_name', Date.new(2020, 1, 1), 'weekly', '.tgz')).to eq false
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'returns false for wrong date' do
|
108
|
+
expect(@client.exists?('backup_name', Date.new(2020, 1, 2), 'daily', '.tgz')).to eq false
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'returns false for backup name' do
|
112
|
+
expect(@client.exists?('other_backup_name', Date.new(2020, 1, 1), 'daily', '.tgz')).to eq false
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
describe '#upload' do
|
118
|
+
|
119
|
+
it 'uploads files' do
|
120
|
+
@client.upload('backup_name', Date.new(2020, 1, 1), 'daily', '.tgz', 'hello world')
|
121
|
+
expect(@client.connection.directories.get('bucket', prefix: '/backup_name/daily/2020-01-01.tgz').files.length).to eq 1
|
122
|
+
expect(@client.connection.directories.get('bucket', prefix: '/backup_name/daily/2020-01-01.tgz').files.first.key).to eq '/backup_name/daily/2020-01-01.tgz'
|
123
|
+
expect(@client.connection.directories.get('bucket', prefix: '/backup_name/daily/2020-01-01.tgz').files.first.body).to eq 'hello world'
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|