backup_mongo_s3 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9061e28c3c46d385a8614560860fb30e46324e90
4
- data.tar.gz: f713fb942fab101d186fb2c15512a1dcf51c9494
3
+ metadata.gz: 2b383cf9b1ceae0117bca48c8ed36c22a1caaec2
4
+ data.tar.gz: 6ae335bfcbc2dfbcebd74f34f9f9381c373e5e53
5
5
  SHA512:
6
- metadata.gz: 9d7e997321ac840f384f679490cc3b54e696195caa3b3408888d3eb6d89c77528125ff62d88493248fdb37146037cfb36f191147822224661aaec881be9944ea
7
- data.tar.gz: a181784c456c452a3a1dfabc1192cd746c89667d2317af0b43e6f55762d5baee31772f11d3d1e101c5e3778046faef50d0096c23e2ec3da59c9d6aad08331d4d
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
- TODO: Write a gem description
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
@@ -1,17 +1,19 @@
1
- Gem::Specification.new do |spec|
2
- spec.name = 'backup_mongo_s3'
3
- spec.version = '0.0.4'
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
- end
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
@@ -6,6 +6,6 @@ begin
6
6
  BackupMongoS3::Application.new(ARGV).run
7
7
  rescue SystemExit
8
8
  # do nothing
9
- rescue Exception => err
10
- abort "[abort] #{err.message}"
9
+ rescue Exception => error
10
+ abort "[abort] #{error.message}"
11
11
  end
@@ -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
- dbs_name = dbs_str.split(',').each { |db_name| db_name.strip! }
30
-
31
- backup(dbs_name)
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("#{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(dbs_name)
86
-
87
- history_days_str = @config[:backup][:history_days]
88
-
89
- begin
90
- history_days = history_days_str.to_i
91
- rescue
92
- raise 'config.yml::backup.history_days is not integer'
93
- end
94
-
95
- dbs_name.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
227
- end
228
- @parser.on('-h', '--help', 'Show help') do
229
- puts "\n#{@parser}\n"
230
- exit
231
- end
232
-
233
- begin
234
- @parser.parse!(argv)
235
-
236
- rescue OptionParser::ParseError => err
237
- puts "#{err.message}\n\n#{@parser}"
238
- exit
239
- end
240
-
241
- if [params[:backup_all], params[:backup], params[:restore], params[:backups_list], params[:cron_update], params[:cron_clear]].compact.length > 1
242
- raise 'Can only backup_all, backup, restore, backups_list, cron_update or cron_clear. Choose one.'
243
- end
244
-
245
- if params[:config].nil? || params[:config] == ''
246
- params[:config] = './config.yml'
247
- end
248
-
249
- params[:config] = File.absolute_path(params[:config])
250
-
251
- params
252
- end
253
-
254
- def parse_config(config_path)
255
-
256
- begin
257
- config = YAML.load(File.read(config_path))
258
- rescue Errno::ENOENT
259
- raise "Could not find config file '#{config_path}'"
260
- rescue ArgumentError => err
261
- raise "Could not parse config file '#{config_path}' - #{err}"
262
- end
263
-
264
- config.deep_symbolize_keys!
265
-
266
- raise 'config.yml. Section <backup> not found' if config[:backup].nil?
267
- raise 'config.yml. Section <mongo> not found' if config[:mongo].nil?
268
- raise 'config.yml. Section <s3> not found' if config[:s3].nil?
269
-
270
- config
271
- end
272
-
273
- def get_temp_dir
274
- temp_dir = @config[:backup][:temp_directory]
275
-
276
- if temp_dir.nil? || temp_dir == ''
277
- temp_dir = Dir.tmpdir
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
@@ -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
- host = (options[:host].nil? || options[:host].empty?) ? 'localhost' : options[:host]
12
- port = options[:port].nil? ? 27017 : options[:port]
13
- username = options[:username]
14
- password = options[:password]
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.empty? || password.nil? || password.empty?
19
- auth_options = "-u '#{username}' -p '#{password}' --authenticationDatabase 'admin'"
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 = "mongodump --dumpDbUsersAndRoles #{@connection_options} --db '#{db_name}' --out '#{backup_path}'"
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 = "mongorestore --restoreDbUsersAndRoles #{@connection_options} --db '#{db_name}' '#{backup_path}'"
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 mongodump '#{db_name}'" unless $?.exitstatus.zero?
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
- job = [@options[:time]]
83
- job << BackupMongoS3.name
84
- job << '--backup_all'
85
- job << "--config #{@options[:config]}"
86
-
87
- job.join(' ')
88
- end
89
-
90
- def comment_base
91
- "#{BackupMongoS3.name} task"
92
- end
93
-
94
- def comment_open
95
- "# Begin #{comment_base}"
96
- end
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 => err
39
- raise "Error upload file <#{file_name}> to s3 <#{key}>: #{err.message}"
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 => err
67
- raise "Error download file <#{key}> from s3 to <#{file_name}>: #{err.message}"
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
@@ -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
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-10-31 00:00:00.000000000 Z
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: