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 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: