backup_mongo_s3 0.0.4 → 0.0.5
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 +4 -4
- data/.gitignore +24 -24
- data/Gemfile +3 -3
- data/README.md +32 -17
- data/backup_mongo_s3.gemspec +19 -17
- data/bin/backup_mongo_s3 +2 -2
- data/lib/backup_mongo_s3/application.rb +277 -283
- data/lib/backup_mongo_s3/db.rb +19 -10
- data/lib/backup_mongo_s3/scheduler.rb +96 -101
- data/lib/backup_mongo_s3/storage.rb +4 -4
- data/lib/backup_mongo_s3.rb +28 -28
- data/lib/helpers/config.yml.template +17 -1
- data/lib/helpers/time.rb +24 -24
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b383cf9b1ceae0117bca48c8ed36c22a1caaec2
|
4
|
+
data.tar.gz: 6ae335bfcbc2dfbcebd74f34f9f9381c373e5e53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 539a39a4fd62c1f5c1c076ebcc07d09a30389434eb74847984a7b292a88c5e88ad0d383460b5e20a3c624beb0585199b69918b088d21797d10adf1b00715e534
|
7
|
+
data.tar.gz: 5da5b7edfe97fd07084fe458eba1f049c74a049756a9dc8e029266d53f510b1a86341ca9ddf9f4613d74daa17f6598ef6da738b24297d27623009f5d9c37d3dc
|
data/.gitignore
CHANGED
@@ -1,24 +1,24 @@
|
|
1
|
-
*.gem
|
2
|
-
*.rbc
|
3
|
-
*.idea
|
4
|
-
.bundle
|
5
|
-
.config
|
6
|
-
.yardoc
|
7
|
-
Gemfile.lock
|
8
|
-
InstalledFiles
|
9
|
-
_yardoc
|
10
|
-
coverage
|
11
|
-
doc/
|
12
|
-
lib/bundler/man
|
13
|
-
pkg
|
14
|
-
rdoc
|
15
|
-
spec/reports
|
16
|
-
test/tmp
|
17
|
-
test/version_tmp
|
18
|
-
tmp
|
19
|
-
*.bundle
|
20
|
-
*.so
|
21
|
-
*.o
|
22
|
-
*.a
|
23
|
-
mkmf.log
|
24
|
-
*.yml
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
*.idea
|
4
|
+
.bundle
|
5
|
+
.config
|
6
|
+
.yardoc
|
7
|
+
Gemfile.lock
|
8
|
+
InstalledFiles
|
9
|
+
_yardoc
|
10
|
+
coverage
|
11
|
+
doc/
|
12
|
+
lib/bundler/man
|
13
|
+
pkg
|
14
|
+
rdoc
|
15
|
+
spec/reports
|
16
|
+
test/tmp
|
17
|
+
test/version_tmp
|
18
|
+
tmp
|
19
|
+
*.bundle
|
20
|
+
*.so
|
21
|
+
*.o
|
22
|
+
*.a
|
23
|
+
mkmf.log
|
24
|
+
*.yml
|
data/Gemfile
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
source 'https://rubygems.org'
|
2
|
-
|
3
|
-
gem 'aws-sdk'
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'aws-sdk'
|
data/README.md
CHANGED
@@ -1,17 +1,32 @@
|
|
1
|
-
# BackupMongoS3
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
## Installation
|
6
|
-
|
7
|
-
Add this line to your application's Gemfile:
|
8
|
-
|
9
|
-
gem 'backup_mongo_s3'
|
10
|
-
|
11
|
-
And then execute:
|
12
|
-
|
13
|
-
$ bundle
|
14
|
-
|
15
|
-
Or install it yourself as:
|
16
|
-
|
17
|
-
$ gem install backup_mongo_s3
|
1
|
+
# BackupMongoS3
|
2
|
+
|
3
|
+
Command-line application for MongoDB backup(mongodump/mongorestore) to Amazon S3
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'backup_mongo_s3'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install backup_mongo_s3
|
18
|
+
|
19
|
+
## Help:
|
20
|
+
|
21
|
+
Usage: backup_mongo_s3 [options]
|
22
|
+
|
23
|
+
--backup_all Backup databases specified in config.yml and upload to S3 bucket
|
24
|
+
--backup DB_NAME Backup database and upload to S3 bucket
|
25
|
+
-r, --restore DB_NAME Restore database from BACKUP_DATE backup
|
26
|
+
-d, --date BACKUP_DATE Restore date YYYYMMDD
|
27
|
+
-l, --list_backups [DB_NAME] Show list of available backups
|
28
|
+
--write_cron Add/update backup_all job
|
29
|
+
--clear_cron Clear backup_all job
|
30
|
+
-c, --config PATH Path to config *.yml. Default: ./config.yml
|
31
|
+
--create_config [PATH] Create template config.yml in current/PATH directory
|
32
|
+
-h, --help Show help
|
data/backup_mongo_s3.gemspec
CHANGED
@@ -1,17 +1,19 @@
|
|
1
|
-
Gem::Specification.new do |spec|
|
2
|
-
spec.name = 'backup_mongo_s3'
|
3
|
-
spec.version = '0.0.
|
4
|
-
spec.authors = ['Yakupov Dima']
|
5
|
-
spec.email = ['yakupov.dima@mail.ru']
|
6
|
-
spec.summary = "Some summary"
|
7
|
-
spec.description = "Command-line application for MongoDB backup(mongodump/mongorestore) to Amazon S3"
|
8
|
-
spec.homepage = ''
|
9
|
-
spec.license = 'MIT'
|
10
|
-
|
11
|
-
spec.files = `git ls-files -z`.split("\x0")
|
12
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
13
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
14
|
-
spec.require_paths = ["lib"]
|
15
|
-
|
16
|
-
spec.add_development_dependency 'bundler', '~> 1.6'
|
17
|
-
|
1
|
+
Gem::Specification.new do |spec|
|
2
|
+
spec.name = 'backup_mongo_s3'
|
3
|
+
spec.version = '0.0.5'
|
4
|
+
spec.authors = ['Yakupov Dima']
|
5
|
+
spec.email = ['yakupov.dima@mail.ru']
|
6
|
+
spec.summary = "Some summary"
|
7
|
+
spec.description = "Command-line application for MongoDB backup(mongodump/mongorestore) to Amazon S3"
|
8
|
+
spec.homepage = ''
|
9
|
+
spec.license = 'MIT'
|
10
|
+
|
11
|
+
spec.files = `git ls-files -z`.split("\x0")
|
12
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
13
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
14
|
+
spec.require_paths = ["lib"]
|
15
|
+
|
16
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
17
|
+
|
18
|
+
spec.add_runtime_dependency 'aws-sdk', '~> 1.57'
|
19
|
+
end
|
data/bin/backup_mongo_s3
CHANGED
@@ -1,284 +1,278 @@
|
|
1
|
-
module BackupMongoS3
|
2
|
-
class Application
|
3
|
-
|
4
|
-
def initialize(argv)
|
5
|
-
@parser = OptionParser.new
|
6
|
-
|
7
|
-
@params = parse_options(argv)
|
8
|
-
@config = parse_config(@params[:config])
|
9
|
-
|
10
|
-
@db = Db.new(@config[:mongo])
|
11
|
-
@storage = Storage.new(@config[:s3])
|
12
|
-
end
|
13
|
-
|
14
|
-
public
|
15
|
-
|
16
|
-
def run
|
17
|
-
|
18
|
-
case
|
19
|
-
when @params[:backups_list] # BACKUPS_LIST
|
20
|
-
show_backups_list(@params[:backups_list])
|
21
|
-
|
22
|
-
when @params[:backup_all] # BACKUP_ALL
|
23
|
-
dbs_str = @config[:backup][:dbs]
|
24
|
-
|
25
|
-
if dbs_str.nil? || dbs_str.empty?
|
26
|
-
raise 'config.yml::backup.dbs is empty'
|
27
|
-
end
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
backup(
|
32
|
-
|
33
|
-
when @params[:backup] # BACKUP
|
34
|
-
backup([@params[:backup]])
|
35
|
-
|
36
|
-
when @params[:restore] # RESTORE
|
37
|
-
if @params[:backup_date].nil?
|
38
|
-
raise 'param --date BACKUP_DATE is not specified'
|
39
|
-
end
|
40
|
-
|
41
|
-
restore(@params[:restore], @params[:backup_date])
|
42
|
-
|
43
|
-
when @params[:cron_update] || @params[:cron_clear] # CRON
|
44
|
-
cron_options =
|
45
|
-
{
|
46
|
-
update: @params[:cron_update],
|
47
|
-
clear: @params[:cron_clear],
|
48
|
-
config: @params[:config],
|
49
|
-
time: @config[:backup][:cron_time],
|
50
|
-
}
|
51
|
-
|
52
|
-
Scheduler.new(cron_options).execute
|
53
|
-
|
54
|
-
else
|
55
|
-
puts "\n#{@parser}\n"
|
56
|
-
exit
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
private
|
62
|
-
|
63
|
-
def show_backups_list(db_name)
|
64
|
-
|
65
|
-
backups = @storage.get_backups_list(
|
66
|
-
|
67
|
-
if backups.empty?
|
68
|
-
puts 'Backups not found'
|
69
|
-
else
|
70
|
-
|
71
|
-
puts sprintf('%-30s %-15s %-20s', 'name', 'size, MB', 'last_modified')
|
72
|
-
|
73
|
-
backups.each do |backup|
|
74
|
-
|
75
|
-
backup_name = File.join(File.dirname(backup.key), File.basename(backup.key, '.backup'))
|
76
|
-
backup_size = backup.content_length / 1024 / 1024
|
77
|
-
backup_last_modified = backup.last_modified
|
78
|
-
|
79
|
-
puts sprintf('%-30s %-15s %-20s', backup_name, backup_size, backup_last_modified)
|
80
|
-
end
|
81
|
-
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def backup(
|
86
|
-
|
87
|
-
history_days_str = @config[:backup][:history_days]
|
88
|
-
|
89
|
-
begin
|
90
|
-
history_days = history_days_str
|
91
|
-
rescue
|
92
|
-
raise 'config.yml::backup.history_days is not integer'
|
93
|
-
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
puts "backup db #{db_name.upcase}:"
|
98
|
-
|
99
|
-
tmp_dir = get_temp_dir
|
100
|
-
|
101
|
-
begin
|
102
|
-
|
103
|
-
dump_path = File.join(tmp_dir, db_name)
|
104
|
-
|
105
|
-
puts "\t dump db..."
|
106
|
-
@db.dump(db_name, tmp_dir)
|
107
|
-
|
108
|
-
if Dir["#{dump_path}/*"].empty?
|
109
|
-
puts "\t [skip] db is empty"
|
110
|
-
|
111
|
-
else
|
112
|
-
|
113
|
-
puts "\t compress..."
|
114
|
-
system("zip -6 -r '#{dump_path}.zip' '#{dump_path}/' -j > /dev/null")
|
115
|
-
raise 'Error zip' unless $?.exitstatus.zero?
|
116
|
-
|
117
|
-
FileUtils.rm_rf(dump_path)
|
118
|
-
|
119
|
-
zip_file_path = "#{dump_path}.zip"
|
120
|
-
|
121
|
-
if File.exists?(zip_file_path)
|
122
|
-
puts "\t upload backup to s3..."
|
123
|
-
@storage.upload(db_name, zip_file_path)
|
124
|
-
|
125
|
-
puts "\t delete old backups from s3..."
|
126
|
-
@storage.delete_old_backups(db_name, history_days)
|
127
|
-
|
128
|
-
File.delete(zip_file_path)
|
129
|
-
end
|
130
|
-
|
131
|
-
end
|
132
|
-
|
133
|
-
ensure
|
134
|
-
FileUtils.remove_entry_secure(tmp_dir)
|
135
|
-
end
|
136
|
-
|
137
|
-
end
|
138
|
-
|
139
|
-
puts '[done] backup'
|
140
|
-
end
|
141
|
-
|
142
|
-
|
143
|
-
def restore(db_name, date)
|
144
|
-
|
145
|
-
puts "restore db #{db_name.upcase}:"
|
146
|
-
|
147
|
-
tmp_dir = get_temp_dir
|
148
|
-
|
149
|
-
begin
|
150
|
-
|
151
|
-
dump_path = File.join(tmp_dir, db_name)
|
152
|
-
|
153
|
-
zip_file_path = "#{dump_path}.zip"
|
154
|
-
|
155
|
-
puts "\t download file from s3..."
|
156
|
-
@storage.download(db_name, date, zip_file_path)
|
157
|
-
|
158
|
-
if File.exists?(zip_file_path)
|
159
|
-
puts "\t uncompress..."
|
160
|
-
system("unzip '#{zip_file_path}' -d '#{dump_path}' > /dev/null")
|
161
|
-
raise 'Error unzip' unless $?.exitstatus.zero?
|
162
|
-
|
163
|
-
File.delete(zip_file_path)
|
164
|
-
|
165
|
-
puts "\t restore db..."
|
166
|
-
@db.restore(db_name, dump_path)
|
167
|
-
end
|
168
|
-
|
169
|
-
ensure
|
170
|
-
FileUtils.remove_entry_secure(tmp_dir)
|
171
|
-
end
|
172
|
-
|
173
|
-
puts '[done] restore'
|
174
|
-
end
|
175
|
-
|
176
|
-
def create_config(path)
|
177
|
-
|
178
|
-
path = '.' if path.nil? || path == ''
|
179
|
-
|
180
|
-
file = File.join(path, 'config.yml')
|
181
|
-
|
182
|
-
if File.exists?(file)
|
183
|
-
raise "create_config: '#{file}' already exists"
|
184
|
-
elsif File.exists?(file.downcase)
|
185
|
-
raise "create_config: '#{file.downcase}' exists, which could conflict with '#{file}'"
|
186
|
-
elsif !File.exists?(File.dirname(file))
|
187
|
-
raise "create_config: directory '#{File.dirname(file)}' does not exist"
|
188
|
-
else
|
189
|
-
file_template = File.join(BackupMongoS3.root_path, 'lib/helpers/config.yml.template')
|
190
|
-
|
191
|
-
FileUtils.cp file_template, file
|
192
|
-
end
|
193
|
-
|
194
|
-
puts "[done] file #{file} was created"
|
195
|
-
end
|
196
|
-
|
197
|
-
def parse_options(argv)
|
198
|
-
params = {}
|
199
|
-
|
200
|
-
@parser.on('--backup_all', 'Backup databases specified in config.yml and upload to S3 bucket') do
|
201
|
-
params[:backup_all] = true
|
202
|
-
end
|
203
|
-
@parser.on('--backup DB_NAME', String, 'Backup database and upload to S3 bucket') do |db_name|
|
204
|
-
params[:backup] = db_name
|
205
|
-
end
|
206
|
-
@parser.on('-r', '--restore DB_NAME', String, 'Restore database from BACKUP_DATE backup') do |db_name|
|
207
|
-
params[:restore] = db_name
|
208
|
-
end
|
209
|
-
@parser.on('-d', '--date BACKUP_DATE', String, 'Restore date YYYYMMDD') do |backup_date|
|
210
|
-
params[:backup_date] = backup_date
|
211
|
-
end
|
212
|
-
@parser.on('-l', '--list_backups [DB_NAME]', String, 'Show list of available backups') do |db_name|
|
213
|
-
params[:backups_list] = db_name || ''
|
214
|
-
end
|
215
|
-
@parser.on('--write_cron', 'Add/update backup_all job') do
|
216
|
-
params[:cron_update] = true
|
217
|
-
end
|
218
|
-
@parser.on('--clear_cron', 'Clear backup_all job') do
|
219
|
-
params[:cron_clear] = true
|
220
|
-
end
|
221
|
-
@parser.on('-c', '--config PATH', String, 'Path to config *.yml. Default ./config.yml') do |path|
|
222
|
-
params[:config] = path || ''
|
223
|
-
end
|
224
|
-
@parser.on('--create_config [PATH]', String, 'Create template config.yml in current/PATH directory') do |path|
|
225
|
-
create_config(path)
|
226
|
-
exit
|
227
|
-
end
|
228
|
-
@parser.on('-h', '--help', 'Show help') do
|
229
|
-
puts "\n#{@parser}\n"
|
230
|
-
exit
|
231
|
-
end
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
config
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
temp_dir
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
end
|
279
|
-
|
280
|
-
Dir.mktmpdir(nil, temp_dir)
|
281
|
-
end
|
282
|
-
|
283
|
-
end
|
1
|
+
module BackupMongoS3
|
2
|
+
class Application
|
3
|
+
|
4
|
+
def initialize(argv)
|
5
|
+
@parser = OptionParser.new
|
6
|
+
|
7
|
+
@params = parse_options(argv)
|
8
|
+
@config = parse_config(@params[:config])
|
9
|
+
|
10
|
+
@db = Db.new(@config[:mongo])
|
11
|
+
@storage = Storage.new(@config[:s3])
|
12
|
+
end
|
13
|
+
|
14
|
+
public
|
15
|
+
|
16
|
+
def run
|
17
|
+
|
18
|
+
case
|
19
|
+
when @params[:backups_list] # BACKUPS_LIST
|
20
|
+
show_backups_list(@params[:backups_list])
|
21
|
+
|
22
|
+
when @params[:backup_all] # BACKUP_ALL
|
23
|
+
dbs_str = @config[:backup][:dbs]
|
24
|
+
|
25
|
+
if dbs_str.nil? || dbs_str.empty?
|
26
|
+
raise 'config.yml::backup.dbs is empty'
|
27
|
+
end
|
28
|
+
|
29
|
+
db_names = dbs_str.split(',').each { |db_name| db_name.strip! }
|
30
|
+
|
31
|
+
backup(db_names)
|
32
|
+
|
33
|
+
when @params[:backup] # BACKUP
|
34
|
+
backup([@params[:backup]])
|
35
|
+
|
36
|
+
when @params[:restore] # RESTORE
|
37
|
+
if @params[:backup_date].nil?
|
38
|
+
raise 'param --date BACKUP_DATE is not specified'
|
39
|
+
end
|
40
|
+
|
41
|
+
restore(@params[:restore], @params[:backup_date])
|
42
|
+
|
43
|
+
when @params[:cron_update] || @params[:cron_clear] # CRON
|
44
|
+
cron_options =
|
45
|
+
{
|
46
|
+
update: @params[:cron_update],
|
47
|
+
clear: @params[:cron_clear],
|
48
|
+
config: @params[:config],
|
49
|
+
time: @config[:backup][:cron_time],
|
50
|
+
}
|
51
|
+
|
52
|
+
Scheduler.new(cron_options).execute
|
53
|
+
|
54
|
+
else
|
55
|
+
puts "\n#{@parser}\n"
|
56
|
+
exit(0)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def show_backups_list(db_name)
|
64
|
+
|
65
|
+
backups = @storage.get_backups_list(db_name)
|
66
|
+
|
67
|
+
if backups.empty?
|
68
|
+
puts 'Backups not found'
|
69
|
+
else
|
70
|
+
|
71
|
+
puts sprintf('%-30s %-15s %-20s', 'name', 'size, MB', 'last_modified')
|
72
|
+
|
73
|
+
backups.each do |backup|
|
74
|
+
|
75
|
+
backup_name = File.join(File.dirname(backup.key), File.basename(backup.key, '.backup'))
|
76
|
+
backup_size = backup.content_length / 1024 / 1024
|
77
|
+
backup_last_modified = backup.last_modified
|
78
|
+
|
79
|
+
puts sprintf('%-30s %-15s %-20s', backup_name, backup_size, backup_last_modified)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def backup(db_names)
|
86
|
+
|
87
|
+
history_days_str = @config[:backup][:history_days]
|
88
|
+
|
89
|
+
begin
|
90
|
+
history_days = Integer(history_days_str)
|
91
|
+
rescue
|
92
|
+
raise 'config.yml::backup.history_days is not integer'
|
93
|
+
end
|
94
|
+
|
95
|
+
db_names.each do |db_name|
|
96
|
+
|
97
|
+
puts "backup db #{db_name.upcase}:"
|
98
|
+
|
99
|
+
tmp_dir = get_temp_dir
|
100
|
+
|
101
|
+
begin
|
102
|
+
|
103
|
+
dump_path = File.join(tmp_dir, db_name)
|
104
|
+
|
105
|
+
puts "\t dump db..."
|
106
|
+
@db.dump(db_name, tmp_dir)
|
107
|
+
|
108
|
+
if Dir["#{dump_path}/*"].empty?
|
109
|
+
puts "\t [skip] db is empty"
|
110
|
+
|
111
|
+
else
|
112
|
+
|
113
|
+
puts "\t compress..."
|
114
|
+
system("zip -6 -r '#{dump_path}.zip' '#{dump_path}/' -j > /dev/null")
|
115
|
+
raise 'Error zip' unless $?.exitstatus.zero?
|
116
|
+
|
117
|
+
FileUtils.rm_rf(dump_path)
|
118
|
+
|
119
|
+
zip_file_path = "#{dump_path}.zip"
|
120
|
+
|
121
|
+
if File.exists?(zip_file_path)
|
122
|
+
puts "\t upload backup to s3..."
|
123
|
+
@storage.upload(db_name, zip_file_path)
|
124
|
+
|
125
|
+
puts "\t delete old backups from s3..."
|
126
|
+
@storage.delete_old_backups(db_name, history_days)
|
127
|
+
|
128
|
+
File.delete(zip_file_path)
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
ensure
|
134
|
+
FileUtils.remove_entry_secure(tmp_dir)
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
puts '[done] backup'
|
140
|
+
end
|
141
|
+
|
142
|
+
|
143
|
+
def restore(db_name, date)
|
144
|
+
|
145
|
+
puts "restore db #{db_name.upcase}:"
|
146
|
+
|
147
|
+
tmp_dir = get_temp_dir
|
148
|
+
|
149
|
+
begin
|
150
|
+
|
151
|
+
dump_path = File.join(tmp_dir, db_name)
|
152
|
+
|
153
|
+
zip_file_path = "#{dump_path}.zip"
|
154
|
+
|
155
|
+
puts "\t download file from s3..."
|
156
|
+
@storage.download(db_name, date, zip_file_path)
|
157
|
+
|
158
|
+
if File.exists?(zip_file_path)
|
159
|
+
puts "\t uncompress..."
|
160
|
+
system("unzip '#{zip_file_path}' -d '#{dump_path}' > /dev/null")
|
161
|
+
raise 'Error unzip' unless $?.exitstatus.zero?
|
162
|
+
|
163
|
+
File.delete(zip_file_path)
|
164
|
+
|
165
|
+
puts "\t restore db..."
|
166
|
+
@db.restore(db_name, dump_path)
|
167
|
+
end
|
168
|
+
|
169
|
+
ensure
|
170
|
+
FileUtils.remove_entry_secure(tmp_dir)
|
171
|
+
end
|
172
|
+
|
173
|
+
puts '[done] restore'
|
174
|
+
end
|
175
|
+
|
176
|
+
def create_config(path)
|
177
|
+
|
178
|
+
path = '.' if path.nil? || path == ''
|
179
|
+
|
180
|
+
file = File.join(path, 'config.yml')
|
181
|
+
|
182
|
+
if File.exists?(file)
|
183
|
+
raise "create_config: '#{file}' already exists"
|
184
|
+
elsif File.exists?(file.downcase)
|
185
|
+
raise "create_config: '#{file.downcase}' exists, which could conflict with '#{file}'"
|
186
|
+
elsif !File.exists?(File.dirname(file))
|
187
|
+
raise "create_config: directory '#{File.dirname(file)}' does not exist"
|
188
|
+
else
|
189
|
+
file_template = File.join(BackupMongoS3.root_path, 'lib/helpers/config.yml.template')
|
190
|
+
|
191
|
+
FileUtils.cp file_template, file
|
192
|
+
end
|
193
|
+
|
194
|
+
puts "[done] file #{file} was created"
|
195
|
+
end
|
196
|
+
|
197
|
+
def parse_options(argv)
|
198
|
+
params = {}
|
199
|
+
|
200
|
+
@parser.on('--backup_all', 'Backup databases specified in config.yml and upload to S3 bucket') do
|
201
|
+
params[:backup_all] = true
|
202
|
+
end
|
203
|
+
@parser.on('--backup DB_NAME', String, 'Backup database and upload to S3 bucket') do |db_name|
|
204
|
+
params[:backup] = db_name
|
205
|
+
end
|
206
|
+
@parser.on('-r', '--restore DB_NAME', String, 'Restore database from BACKUP_DATE backup') do |db_name|
|
207
|
+
params[:restore] = db_name
|
208
|
+
end
|
209
|
+
@parser.on('-d', '--date BACKUP_DATE', String, 'Restore date YYYYMMDD') do |backup_date|
|
210
|
+
params[:backup_date] = backup_date
|
211
|
+
end
|
212
|
+
@parser.on('-l', '--list_backups [DB_NAME]', String, 'Show list of available backups') do |db_name|
|
213
|
+
params[:backups_list] = db_name || ''
|
214
|
+
end
|
215
|
+
@parser.on('--write_cron', 'Add/update backup_all job') do
|
216
|
+
params[:cron_update] = true
|
217
|
+
end
|
218
|
+
@parser.on('--clear_cron', 'Clear backup_all job') do
|
219
|
+
params[:cron_clear] = true
|
220
|
+
end
|
221
|
+
@parser.on('-c', '--config PATH', String, 'Path to config *.yml. Default: ./config.yml') do |path|
|
222
|
+
params[:config] = path || ''
|
223
|
+
end
|
224
|
+
@parser.on('--create_config [PATH]', String, 'Create template config.yml in current/PATH directory') do |path|
|
225
|
+
create_config(path)
|
226
|
+
exit(0)
|
227
|
+
end
|
228
|
+
@parser.on('-h', '--help', 'Show help') do
|
229
|
+
puts "\n#{@parser}\n"
|
230
|
+
exit(0)
|
231
|
+
end
|
232
|
+
|
233
|
+
@parser.parse!(argv)
|
234
|
+
|
235
|
+
if [params[:backup_all], params[:backup], params[:restore], params[:backups_list], params[:cron_update], params[:cron_clear]].compact.length > 1
|
236
|
+
raise 'Can only backup_all, backup, restore, list_backups, write_cron or clear_cron. Choose one.'
|
237
|
+
end
|
238
|
+
|
239
|
+
if params[:config].nil? || params[:config] == ''
|
240
|
+
params[:config] = './config.yml'
|
241
|
+
end
|
242
|
+
|
243
|
+
params[:config] = File.absolute_path(params[:config])
|
244
|
+
|
245
|
+
params
|
246
|
+
end
|
247
|
+
|
248
|
+
def parse_config(config_path)
|
249
|
+
|
250
|
+
begin
|
251
|
+
config = YAML.load(File.read(config_path))
|
252
|
+
rescue Errno::ENOENT
|
253
|
+
raise "Could not find config file '#{config_path}'"
|
254
|
+
rescue ArgumentError => error
|
255
|
+
raise "Could not parse config file '#{config_path}' - #{error}"
|
256
|
+
end
|
257
|
+
|
258
|
+
config.deep_symbolize_keys!
|
259
|
+
|
260
|
+
raise 'config.yml. Section <backup> not found' if config[:backup].nil?
|
261
|
+
raise 'config.yml. Section <mongo> not found' if config[:mongo].nil?
|
262
|
+
raise 'config.yml. Section <s3> not found' if config[:s3].nil?
|
263
|
+
|
264
|
+
config
|
265
|
+
end
|
266
|
+
|
267
|
+
def get_temp_dir
|
268
|
+
temp_dir = @config[:backup][:temp_directory]
|
269
|
+
|
270
|
+
if temp_dir.nil? || temp_dir == ''
|
271
|
+
temp_dir = Dir.tmpdir
|
272
|
+
end
|
273
|
+
|
274
|
+
Dir.mktmpdir(nil, temp_dir)
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
284
278
|
end
|
data/lib/backup_mongo_s3/db.rb
CHANGED
@@ -2,21 +2,23 @@ module BackupMongoS3
|
|
2
2
|
class Db
|
3
3
|
|
4
4
|
def initialize(options)
|
5
|
+
@options = options
|
5
6
|
@connection_options = connection(options)
|
6
7
|
end
|
7
8
|
|
8
9
|
private
|
9
10
|
def connection(options)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
host = (options[:host].nil? || options[:host] == '') ? 'localhost' : options[:host]
|
12
|
+
port = options[:port].nil? ? 27017 : options[:port]
|
13
|
+
username = options[:username]
|
14
|
+
password = options[:password]
|
15
|
+
authentication_database = options[:authentication_database]
|
15
16
|
|
16
17
|
auth_options = ''
|
17
18
|
|
18
|
-
unless username.nil? || username
|
19
|
-
auth_options = "-u '#{username}' -p '#{password}'
|
19
|
+
unless username.nil? || username == '' || password.nil? || password == ''
|
20
|
+
auth_options = "-u '#{username}' -p '#{password}'"
|
21
|
+
auth_options << " --authenticationDatabase '#{authentication_database}'" unless authentication_database.nil? || authentication_database == ''
|
20
22
|
end
|
21
23
|
|
22
24
|
"--host '#{host}' --port '#{port}' #{auth_options}"
|
@@ -24,7 +26,10 @@ module BackupMongoS3
|
|
24
26
|
|
25
27
|
public
|
26
28
|
def dump(db_name, backup_path)
|
27
|
-
command =
|
29
|
+
command = 'mongodump'
|
30
|
+
command << " #{@connection_options}"
|
31
|
+
command << ' --dumpDbUsersAndRoles' if @options[:dump_db_users_and_roles] == true || @options[:dump_db_users_and_roles] == 1
|
32
|
+
command << " --db '#{db_name}' --out '#{backup_path}'"
|
28
33
|
command << ' > /dev/null'
|
29
34
|
|
30
35
|
system(command)
|
@@ -33,11 +38,15 @@ module BackupMongoS3
|
|
33
38
|
|
34
39
|
public
|
35
40
|
def restore(db_name, backup_path)
|
36
|
-
command =
|
41
|
+
command = 'mongorestore'
|
42
|
+
command << " #{@connection_options}"
|
43
|
+
command << ' --restoreDbUsersAndRoles' if @options[:restore_db_users_and_roles] == true || @options[:restore_db_users_and_roles] == 1
|
44
|
+
command << ' --drop' if @options[:drop_collection] == true || @options[:drop_collection] == 1
|
45
|
+
command << " --db '#{db_name}' '#{backup_path}'"
|
37
46
|
command << ' > /dev/null'
|
38
47
|
|
39
48
|
system(command)
|
40
|
-
raise "Error
|
49
|
+
raise "Error mongorestore '#{db_name}'" unless $?.exitstatus.zero?
|
41
50
|
end
|
42
51
|
|
43
52
|
|
@@ -1,102 +1,97 @@
|
|
1
|
-
module BackupMongoS3
|
2
|
-
class Scheduler
|
3
|
-
|
4
|
-
def initialize(options = {})
|
5
|
-
@options = options
|
6
|
-
|
7
|
-
if [@options[:write], @options[:clear]].compact.length > 1
|
8
|
-
raise 'cron: Can only write or clear. Choose one.'
|
9
|
-
end
|
10
|
-
|
11
|
-
unless @options[:time] =~ /\A[*\-,0-9]+ [*\-,0-9]+ [*\-,0-9]+ [*\-,0-9]+ [*\-,0-6]+\z/
|
12
|
-
raise 'config.yml: cron_time is not valid'
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def execute
|
17
|
-
write_crontab(updated_crontab)
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def read_crontab
|
23
|
-
return @read_crontab if @read_crontab
|
24
|
-
|
25
|
-
command = 'crontab -l'
|
26
|
-
|
27
|
-
command_results = %x[#{command} 2> /dev/null]
|
28
|
-
|
29
|
-
@read_crontab = $?.exitstatus.zero? ? prepare(command_results) : ''
|
30
|
-
end
|
31
|
-
|
32
|
-
def prepare(contents)
|
33
|
-
# Some cron implementations require all non-comment lines to be newline-
|
34
|
-
# terminated. (issue #95) Strip all newlines and replace with the default
|
35
|
-
# platform record seperator ($/)
|
36
|
-
contents.gsub!(/\s+$/, $/)
|
37
|
-
end
|
38
|
-
|
39
|
-
def write_crontab(contents)
|
40
|
-
command = 'crontab -'
|
41
|
-
|
42
|
-
IO.popen(command, 'r+') do |crontab|
|
43
|
-
crontab.write(contents)
|
44
|
-
crontab.close_write
|
45
|
-
end
|
46
|
-
|
47
|
-
success = $?.exitstatus.zero?
|
48
|
-
|
49
|
-
if success
|
50
|
-
action = @options[:update] ? 'updated' : 'cleared'
|
51
|
-
puts "[done] crontab file #{action}"
|
52
|
-
exit(0)
|
53
|
-
else
|
54
|
-
raise "Couldn't write crontab"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
def updated_crontab
|
59
|
-
# Check for unopened or unclosed identifier blocks
|
60
|
-
if read_crontab =~ Regexp.new("^#{comment_open}\s*$") && (read_crontab =~ Regexp.new("^#{comment_close}\s*$")).nil?
|
61
|
-
raise "Unclosed indentifier; Your crontab file contains '#{comment_open}', but no '#{comment_close}'"
|
62
|
-
elsif (read_crontab =~ Regexp.new("^#{comment_open}\s*$")).nil? && read_crontab =~ Regexp.new("^#{comment_close}\s*$")
|
63
|
-
raise "Unopened indentifier; Your crontab file contains '#{comment_close}', but no '#{comment_open}'"
|
64
|
-
end
|
65
|
-
|
66
|
-
# If an existing identier block is found, replace it with the new cron entries
|
67
|
-
if read_crontab =~ Regexp.new("^#{comment_open}\s*$") && read_crontab =~ Regexp.new("^#{comment_close}\s*$")
|
68
|
-
# If the existing crontab file contains backslashes they get lost going through gsub.
|
69
|
-
# .gsub('\\', '\\\\\\') preserves them. Go figure.
|
70
|
-
read_crontab.gsub(Regexp.new("^#{comment_open}\s*$.+^#{comment_close}\s*$", Regexp::MULTILINE), crontab_task.chomp.gsub('\\', '\\\\\\'))
|
71
|
-
else # Otherwise, append the new cron entries after any existing ones
|
72
|
-
[read_crontab, crontab_task].join("\n\n")
|
73
|
-
end.gsub(/\n{3,}/, "\n\n") # More than two newlines becomes just two.
|
74
|
-
end
|
75
|
-
|
76
|
-
def crontab_task
|
77
|
-
return '' if @options[:clear]
|
78
|
-
[comment_open, crontab_job, comment_close].compact.join("\n") + "\n"
|
79
|
-
end
|
80
|
-
|
81
|
-
def crontab_job
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
def comment_close
|
99
|
-
"# End #{comment_base}"
|
100
|
-
end
|
101
|
-
end
|
1
|
+
module BackupMongoS3
|
2
|
+
class Scheduler
|
3
|
+
|
4
|
+
def initialize(options = {})
|
5
|
+
@options = options
|
6
|
+
|
7
|
+
if [@options[:write], @options[:clear]].compact.length > 1
|
8
|
+
raise 'cron: Can only write or clear. Choose one.'
|
9
|
+
end
|
10
|
+
|
11
|
+
unless @options[:time] =~ /\A[*\-,0-9]+ [*\-,0-9]+ [*\-,0-9]+ [*\-,0-9]+ [*\-,0-6]+\z/
|
12
|
+
raise 'config.yml: cron_time is not valid'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute
|
17
|
+
write_crontab(updated_crontab)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def read_crontab
|
23
|
+
return @read_crontab if @read_crontab
|
24
|
+
|
25
|
+
command = 'crontab -l'
|
26
|
+
|
27
|
+
command_results = %x[#{command} 2> /dev/null]
|
28
|
+
|
29
|
+
@read_crontab = $?.exitstatus.zero? ? prepare(command_results) : ''
|
30
|
+
end
|
31
|
+
|
32
|
+
def prepare(contents)
|
33
|
+
# Some cron implementations require all non-comment lines to be newline-
|
34
|
+
# terminated. (issue #95) Strip all newlines and replace with the default
|
35
|
+
# platform record seperator ($/)
|
36
|
+
contents.gsub!(/\s+$/, $/)
|
37
|
+
end
|
38
|
+
|
39
|
+
def write_crontab(contents)
|
40
|
+
command = 'crontab -'
|
41
|
+
|
42
|
+
IO.popen(command, 'r+') do |crontab|
|
43
|
+
crontab.write(contents)
|
44
|
+
crontab.close_write
|
45
|
+
end
|
46
|
+
|
47
|
+
success = $?.exitstatus.zero?
|
48
|
+
|
49
|
+
if success
|
50
|
+
action = @options[:update] ? 'updated' : 'cleared'
|
51
|
+
puts "[done] crontab file #{action}"
|
52
|
+
exit(0)
|
53
|
+
else
|
54
|
+
raise "Couldn't write crontab"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def updated_crontab
|
59
|
+
# Check for unopened or unclosed identifier blocks
|
60
|
+
if read_crontab =~ Regexp.new("^#{comment_open}\s*$") && (read_crontab =~ Regexp.new("^#{comment_close}\s*$")).nil?
|
61
|
+
raise "Unclosed indentifier; Your crontab file contains '#{comment_open}', but no '#{comment_close}'"
|
62
|
+
elsif (read_crontab =~ Regexp.new("^#{comment_open}\s*$")).nil? && read_crontab =~ Regexp.new("^#{comment_close}\s*$")
|
63
|
+
raise "Unopened indentifier; Your crontab file contains '#{comment_close}', but no '#{comment_open}'"
|
64
|
+
end
|
65
|
+
|
66
|
+
# If an existing identier block is found, replace it with the new cron entries
|
67
|
+
if read_crontab =~ Regexp.new("^#{comment_open}\s*$") && read_crontab =~ Regexp.new("^#{comment_close}\s*$")
|
68
|
+
# If the existing crontab file contains backslashes they get lost going through gsub.
|
69
|
+
# .gsub('\\', '\\\\\\') preserves them. Go figure.
|
70
|
+
read_crontab.gsub(Regexp.new("^#{comment_open}\s*$.+^#{comment_close}\s*$", Regexp::MULTILINE), crontab_task.chomp.gsub('\\', '\\\\\\'))
|
71
|
+
else # Otherwise, append the new cron entries after any existing ones
|
72
|
+
[read_crontab, crontab_task].join("\n\n")
|
73
|
+
end.gsub(/\n{3,}/, "\n\n") # More than two newlines becomes just two.
|
74
|
+
end
|
75
|
+
|
76
|
+
def crontab_task
|
77
|
+
return '' if @options[:clear]
|
78
|
+
[comment_open, crontab_job, comment_close].compact.join("\n") + "\n"
|
79
|
+
end
|
80
|
+
|
81
|
+
def crontab_job
|
82
|
+
"#{@options[:time]} /bin/bash -l -c '#{BackupMongoS3.name} --backup_all --config #{@options[:config]}'"
|
83
|
+
end
|
84
|
+
|
85
|
+
def comment_base
|
86
|
+
"#{BackupMongoS3.name} task"
|
87
|
+
end
|
88
|
+
|
89
|
+
def comment_open
|
90
|
+
"# Begin #{comment_base}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def comment_close
|
94
|
+
"# End #{comment_base}"
|
95
|
+
end
|
96
|
+
end
|
102
97
|
end
|
@@ -35,8 +35,8 @@ module BackupMongoS3
|
|
35
35
|
|
36
36
|
file.close
|
37
37
|
|
38
|
-
rescue Exception =>
|
39
|
-
raise "Error upload file <#{file_name}> to s3 <#{key}>: #{
|
38
|
+
rescue Exception => error
|
39
|
+
raise "Error upload file <#{file_name}> to s3 <#{key}>: #{error.message}"
|
40
40
|
end
|
41
41
|
|
42
42
|
end
|
@@ -63,8 +63,8 @@ module BackupMongoS3
|
|
63
63
|
raise 'Backup signature is not valid'
|
64
64
|
end
|
65
65
|
|
66
|
-
rescue Exception =>
|
67
|
-
raise "Error download file <#{key}> from s3 to <#{file_name}>: #{
|
66
|
+
rescue Exception => error
|
67
|
+
raise "Error download file <#{key}> from s3 to <#{file_name}>: #{error.message}"
|
68
68
|
end
|
69
69
|
|
70
70
|
end
|
data/lib/backup_mongo_s3.rb
CHANGED
@@ -1,28 +1,28 @@
|
|
1
|
-
require 'optparse'
|
2
|
-
require 'aws-sdk'
|
3
|
-
require 'fileutils'
|
4
|
-
require 'digest/md5'
|
5
|
-
require 'tmpdir'
|
6
|
-
|
7
|
-
require_relative 'backup_mongo_s3/application'
|
8
|
-
require_relative 'backup_mongo_s3/db'
|
9
|
-
require_relative 'backup_mongo_s3/storage'
|
10
|
-
require_relative 'backup_mongo_s3/scheduler'
|
11
|
-
|
12
|
-
require_relative 'helpers/fixnum'
|
13
|
-
require_relative 'helpers/hash'
|
14
|
-
require_relative 'helpers/time'
|
15
|
-
|
16
|
-
module BackupMongoS3
|
17
|
-
|
18
|
-
def self.name
|
19
|
-
File.basename( __FILE__, '.rb')
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.root_path
|
23
|
-
@root_path if @root_path
|
24
|
-
spec = Gem::Specification.find_by_name(self.name)
|
25
|
-
@root_path = spec.gem_dir
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
1
|
+
require 'optparse'
|
2
|
+
require 'aws-sdk'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'digest/md5'
|
5
|
+
require 'tmpdir'
|
6
|
+
|
7
|
+
require_relative 'backup_mongo_s3/application'
|
8
|
+
require_relative 'backup_mongo_s3/db'
|
9
|
+
require_relative 'backup_mongo_s3/storage'
|
10
|
+
require_relative 'backup_mongo_s3/scheduler'
|
11
|
+
|
12
|
+
require_relative 'helpers/fixnum'
|
13
|
+
require_relative 'helpers/hash'
|
14
|
+
require_relative 'helpers/time'
|
15
|
+
|
16
|
+
module BackupMongoS3
|
17
|
+
|
18
|
+
def self.name
|
19
|
+
File.basename( __FILE__, '.rb')
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.root_path
|
23
|
+
@root_path if @root_path
|
24
|
+
spec = Gem::Specification.find_by_name(self.name)
|
25
|
+
@root_path = spec.gem_dir
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -5,7 +5,7 @@ backup:
|
|
5
5
|
# All backups older than [today - history_days] will be deleted when run backup/backup_all
|
6
6
|
history_days: 5
|
7
7
|
|
8
|
-
# Temporary directory for dump files. Default system temp directory
|
8
|
+
# Temporary directory for dump files. Default: system temp directory
|
9
9
|
temp_directory:
|
10
10
|
|
11
11
|
# [minute] [hour] [day] [month] [weekday]
|
@@ -17,6 +17,22 @@ mongo:
|
|
17
17
|
username:
|
18
18
|
password:
|
19
19
|
|
20
|
+
# If you do not specify an authentication database than database specified to backup(restore) holds the user’s credentials
|
21
|
+
authentication_database: admin
|
22
|
+
|
23
|
+
# mongodump
|
24
|
+
|
25
|
+
# Includes user and role definitions when performing mongodump on a specific database
|
26
|
+
dump_db_users_and_roles: true
|
27
|
+
|
28
|
+
# mongorestore
|
29
|
+
|
30
|
+
# Restore user and role definitions for the given database
|
31
|
+
restore_db_users_and_roles: true
|
32
|
+
|
33
|
+
# Modifies the restoration procedure to drop every collection from the target database before restoring the collection from the dumped backup
|
34
|
+
drop_collection: true
|
35
|
+
|
20
36
|
s3:
|
21
37
|
access_key_id: 'access_key_id'
|
22
38
|
secret_access_key: 'secret_access_key'
|
data/lib/helpers/time.rb
CHANGED
@@ -1,25 +1,25 @@
|
|
1
|
-
class Time
|
2
|
-
|
3
|
-
def midnight
|
4
|
-
change(:hour => 0)
|
5
|
-
end
|
6
|
-
|
7
|
-
def change(options)
|
8
|
-
new_year = options.fetch(:year, year)
|
9
|
-
new_month = options.fetch(:month, month)
|
10
|
-
new_day = options.fetch(:day, day)
|
11
|
-
new_hour = options.fetch(:hour, hour)
|
12
|
-
new_min = options.fetch(:min, options[:hour] ? 0 : min)
|
13
|
-
new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
|
14
|
-
new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
|
15
|
-
|
16
|
-
if utc?
|
17
|
-
::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
|
18
|
-
elsif zone
|
19
|
-
::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
|
20
|
-
else
|
21
|
-
::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec + (new_usec.to_r / 1000000), utc_offset)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
1
|
+
class Time
|
2
|
+
|
3
|
+
def midnight
|
4
|
+
change(:hour => 0)
|
5
|
+
end
|
6
|
+
|
7
|
+
def change(options)
|
8
|
+
new_year = options.fetch(:year, year)
|
9
|
+
new_month = options.fetch(:month, month)
|
10
|
+
new_day = options.fetch(:day, day)
|
11
|
+
new_hour = options.fetch(:hour, hour)
|
12
|
+
new_min = options.fetch(:min, options[:hour] ? 0 : min)
|
13
|
+
new_sec = options.fetch(:sec, (options[:hour] || options[:min]) ? 0 : sec)
|
14
|
+
new_usec = options.fetch(:usec, (options[:hour] || options[:min] || options[:sec]) ? 0 : Rational(nsec, 1000))
|
15
|
+
|
16
|
+
if utc?
|
17
|
+
::Time.utc(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
|
18
|
+
elsif zone
|
19
|
+
::Time.local(new_year, new_month, new_day, new_hour, new_min, new_sec, new_usec)
|
20
|
+
else
|
21
|
+
::Time.new(new_year, new_month, new_day, new_hour, new_min, new_sec + (new_usec.to_r / 1000000), utc_offset)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
25
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: backup_mongo_s3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yakupov Dima
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-11-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ~>
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.6'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aws-sdk
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.57'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.57'
|
27
41
|
description: Command-line application for MongoDB backup(mongodump/mongorestore) to
|
28
42
|
Amazon S3
|
29
43
|
email:
|