mongo-oplog-backup 0.0.11 → 0.0.12
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/bin/mongo-oplog-backup +62 -0
- data/lib/mongo_oplog_backup.rb +1 -0
- data/lib/mongo_oplog_backup/config.rb +17 -3
- data/lib/mongo_oplog_backup/ext/timestamp.rb +11 -1
- data/lib/mongo_oplog_backup/restore.rb +141 -0
- data/lib/mongo_oplog_backup/version.rb +1 -1
- data/spec/timestamp_spec.rb +9 -0
- data/testing.sh +57 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4c98c007c62b80bc6c87b1f141deba9d27933865
|
4
|
+
data.tar.gz: 9a731f45965d4218bbef98e711908d236d41d29c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5642b1a2790b9d2493c1d5e7f90e8baae53bc6d9c6a81084475d03100903b6cb704124f7a4336060f7c9256900db00a677b06c548c665f5ccdcbae7989f28bea
|
7
|
+
data.tar.gz: cbba467fb5f8c9ffe9c30a79298c6d3779d5e590ebab20bf874139b76b1adb288560caf7910f734093f0dfe5ce5e563a041c8b7ce5bc4af0f75ae8f8d3a3aef6
|
data/bin/mongo-oplog-backup
CHANGED
@@ -18,6 +18,9 @@ opts = Slop.parse(help: true, strict: true) do
|
|
18
18
|
on :ssl, "Connect to a mongod instance over an SSL connection"
|
19
19
|
on :sslAllowInvalidCertificates, "Allow connections to a mongod instance with an invalid certificate"
|
20
20
|
on :sslCAFile, "Specifies a Certificate Authority file for validating the SSL certificate provided by the mongod instance.", argument: :required
|
21
|
+
on :sslPEMKeyFile, "Specifies a SSL client certificate and private key for authenticating with the mongod instance.", argument: :required
|
22
|
+
on :sslPEMKeyPassword, "Specifies the password used to open the sslPEMKeyFile, if required.", argument: :required
|
23
|
+
on :authenticationDatabase, "Specifies the database to authenticate against. This can be used to override the default selection.", argument: :required
|
21
24
|
on :host, "Specifies a resolvable hostname for the mongod that you wish to backup.", default: 'localhost', argument: :required
|
22
25
|
on :port, "Specifies the port that mongod is running on", default: '27017', argument: :required
|
23
26
|
on :u, :username, "Specifies a username to authenticate to the MongoDB instance, if your database requires authentication. Use in conjunction with the --password option to supply a password.", argument: :required
|
@@ -37,6 +40,9 @@ opts = Slop.parse(help: true, strict: true) do
|
|
37
40
|
config_opts[:username] = opts[:username]
|
38
41
|
config_opts[:password] = opts[:password]
|
39
42
|
config_opts[:sslCAFile] = opts[:sslCAFile]
|
43
|
+
config_opts[:sslPEMKeyFile] = opts[:sslPEMKeyFile]
|
44
|
+
config_opts[:sslPEMKeyPassword] = opts[:sslPEMKeyPassword]
|
45
|
+
config_opts[:authenticationDatabase] = opts[:authenticationDatabase]
|
40
46
|
|
41
47
|
mode = :auto
|
42
48
|
if opts.full?
|
@@ -66,4 +72,60 @@ opts = Slop.parse(help: true, strict: true) do
|
|
66
72
|
puts "mongorestore [--drop] [--gzip] --oplogReplay #{File.join(dir, 'dump')}"
|
67
73
|
end
|
68
74
|
end
|
75
|
+
|
76
|
+
command 'restore' do
|
77
|
+
banner 'Usage: mongo-oplog-backup restore [options]'
|
78
|
+
on :v, :verbose, 'Enable verbose mode'
|
79
|
+
on :d, :dir, "Directory containing the backup to restore. Must contain a 'dump' folder.", argument: :required
|
80
|
+
on :full, 'Do a full restore, followed by an oplog restore.'
|
81
|
+
on :oplog, 'Restore only the oplogs. Assumes a manual restore has already been done.'
|
82
|
+
|
83
|
+
on :gzip, "Use gzip compression. This option is ignored by Oplog mode, because it autodetects."
|
84
|
+
on :ssl, "Connect to a mongod instance over an SSL connection"
|
85
|
+
on :sslAllowInvalidCertificates, "Allow connections to a mongod instance with an invalid certificate"
|
86
|
+
on :sslCAFile, "Specifies a Certificate Authority file for validating the SSL certificate provided by the mongod instance.", argument: :required
|
87
|
+
on :host, "Specifies a resolvable hostname for the mongod that you wish to backup.", default: 'localhost', argument: :required
|
88
|
+
on :port, "Specifies the port that mongod is running on", default: '27017', argument: :required
|
89
|
+
on :u, :username, "Specifies a username to authenticate to the MongoDB instance, if your database requires authentication. Use in conjunction with the --password option to supply a password.", argument: :required
|
90
|
+
on :p, :password, "Specifies a password to authenticate to the MongoDB instance. Use in conjunction with the --username option to supply a username. Note. the password will not be prompted for, so must be passed as an argument", argument: :required
|
91
|
+
|
92
|
+
on :noIndexRestore, "Don't restore indexes."
|
93
|
+
on :oplogLimit, "Only include oplog entries before the provided Timestamp: <seconds>[:ordinal]", argument: :required
|
94
|
+
on :oplogStartAt, "Ignore oplog batches before the provided Timestamp: <seconds>[:ordinal]. This is useful for resuming a restore after an error.", argument: :required
|
95
|
+
|
96
|
+
run do |opts, args|
|
97
|
+
dir = opts[:dir]
|
98
|
+
raise ArgumentError, 'dir must be specified' unless dir
|
99
|
+
raise ArgumentError, 'dir must contain a dump subfolder' unless File.directory?(File.join(dir, 'dump'))
|
100
|
+
config_opts = {
|
101
|
+
dir: dir,
|
102
|
+
ssl: opts.ssl?,
|
103
|
+
gzip: opts.gzip?,
|
104
|
+
sslAllowInvalidCertificates: opts.sslAllowInvalidCertificates?,
|
105
|
+
noIndexRestore: opts.noIndexRestore?
|
106
|
+
}
|
107
|
+
config_opts[:host] = opts[:host]
|
108
|
+
config_opts[:port] = opts[:port]
|
109
|
+
config_opts[:username] = opts[:username]
|
110
|
+
config_opts[:password] = opts[:password]
|
111
|
+
config_opts[:sslCAFile] = opts[:sslCAFile]
|
112
|
+
config_opts[:oplogLimit] = opts[:oplogLimit]
|
113
|
+
config_opts[:oplogStartAt] = opts[:oplogStartAt]
|
114
|
+
|
115
|
+
if opts.full?
|
116
|
+
mode = :full
|
117
|
+
elsif opts.oplog?
|
118
|
+
mode = :oplog
|
119
|
+
else
|
120
|
+
raise ArgumentError, "either full or oplog must be specified"
|
121
|
+
end
|
122
|
+
|
123
|
+
restore_opts = {}
|
124
|
+
restore_opts[:oplogLimit] = opts[:oplogLimit]
|
125
|
+
|
126
|
+
config = MongoOplogBackup::Config.new(config_opts)
|
127
|
+
restore = MongoOplogBackup::Restore.new(config)
|
128
|
+
restore.perform(mode, restore_opts)
|
129
|
+
end
|
130
|
+
end
|
69
131
|
end
|
data/lib/mongo_oplog_backup.rb
CHANGED
@@ -18,6 +18,9 @@ module MongoOplogBackup
|
|
18
18
|
options[:ssl] = conf["ssl"] unless conf["ssl"].nil?
|
19
19
|
options[:sslAllowInvalidCertificates] = conf["sslAllowInvalidCertificates"] unless conf["sslAllowInvalidCertificates"].nil?
|
20
20
|
options[:sslCAFile] = conf["sslCAFile"] unless conf["sslCAFile"].nil?
|
21
|
+
options[:sslPEMKeyFile] = conf["sslPEMKeyFile"] unless conf["sslPEMKeyFile"].nil?
|
22
|
+
options[:sslPEMKeyPassword] = conf["sslPEMKeyPassword"] unless conf["sslPEMKeyPassword"].nil?
|
23
|
+
options[:authenticationDatabase] = conf["authenticationDatabase"] unless conf["authenticationDatabase"].nil?
|
21
24
|
options[:host] = conf["host"] unless conf["host"].nil?
|
22
25
|
options[:port] = conf["port"].to_s unless conf["port"].nil?
|
23
26
|
options[:username] = conf["username"] unless conf["username"].nil?
|
@@ -39,11 +42,18 @@ module MongoOplogBackup
|
|
39
42
|
args = []
|
40
43
|
args << '--ssl' if options[:ssl]
|
41
44
|
args << '--sslAllowInvalidCertificates' if options[:sslAllowInvalidCertificates]
|
42
|
-
[:host, :port, :username, :password, :sslCAFile].each do |option|
|
45
|
+
[:host, :port, :username, :password, :sslCAFile, :sslPEMKeyFile, :sslPEMKeyPassword].each do |option|
|
43
46
|
args += ["--#{option}", options[option].strip] if options[option]
|
44
47
|
end
|
48
|
+
|
49
|
+
if options[:authenticationDatabase]
|
50
|
+
args += ['--authenticationDatabase', options[:authenticationDatabase]]
|
51
|
+
else
|
52
|
+
args += ['--authenticationDatabase', 'admin'] if options[:username] && !options[:sslPEMKeyFile]
|
53
|
+
args += ['--authenticationDatabase', '$external'] if options[:sslPEMKeyFile]
|
54
|
+
end
|
45
55
|
|
46
|
-
args += ['--
|
56
|
+
args += ['--authenticationMechanism', 'MONGODB-X509'] if options[:sslPEMKeyFile]
|
47
57
|
|
48
58
|
args
|
49
59
|
end
|
@@ -78,7 +88,11 @@ module MongoOplogBackup
|
|
78
88
|
end
|
79
89
|
|
80
90
|
def mongo(db, script)
|
81
|
-
exec(['mongo'] + command_line_options + ['--quiet', '--norc', script])
|
91
|
+
exec(['mongo'] + command_line_options + ['--quiet', '--norc', db, script])
|
92
|
+
end
|
93
|
+
|
94
|
+
def mongorestore(*args)
|
95
|
+
exec(['mongorestore'] + command_line_options + args.flatten)
|
82
96
|
end
|
83
97
|
|
84
98
|
def command_string(cmd)
|
@@ -21,10 +21,20 @@ module MongoOplogBackup::Ext
|
|
21
21
|
end
|
22
22
|
|
23
23
|
module ClassMethods
|
24
|
-
# Accepts {'t' => seconds, 'i' => increment}
|
24
|
+
# Accepts {'t' => seconds, 'i' => increment} or {'$timestamp' => {'t' => seconds, 'i' => increment}}
|
25
25
|
def from_json(data)
|
26
|
+
data = data['$timestamp'] if data['$timestamp']
|
26
27
|
self.new(data['t'], data['i'])
|
27
28
|
end
|
29
|
+
|
30
|
+
# Accepts: <seconds>[:ordinal]
|
31
|
+
def from_string(string)
|
32
|
+
match = /(\d+)(?::(\d+))?/.match(string)
|
33
|
+
return nil unless match
|
34
|
+
s1 = match[1].to_i
|
35
|
+
i1 = match[2].to_i
|
36
|
+
self.new(s1,i1)
|
37
|
+
end
|
28
38
|
end
|
29
39
|
|
30
40
|
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'mongo_oplog_backup/oplog'
|
4
|
+
|
5
|
+
module MongoOplogBackup
|
6
|
+
class Restore
|
7
|
+
attr_reader :config
|
8
|
+
|
9
|
+
def backup_folder
|
10
|
+
File.join(config.backup_dir)
|
11
|
+
end
|
12
|
+
|
13
|
+
def oplog_restore_folder
|
14
|
+
File.join(backup_folder, 'tmp-restore')
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(config)
|
18
|
+
@config = config
|
19
|
+
end
|
20
|
+
|
21
|
+
# Given a directory of oplog dumps generated by the backup feature,
|
22
|
+
# iteratively mongorestore them.
|
23
|
+
# Mongorestore <3.4 expects a file named oplog.bson the directory specified
|
24
|
+
# Mongorestore 3.4 adds support for --oplogFile parameter, which simplifies things.
|
25
|
+
def restore_oplogs(dir, options={})
|
26
|
+
default_restore_args = ['--stopOnError', '--oplogReplay']
|
27
|
+
default_restore_args << '--noIndexRestore' if !!config.options[:noIndexRestore]
|
28
|
+
if options[:oplogLimit]
|
29
|
+
default_restore_args += ['--oplogLimit', options[:oplogLimit]]
|
30
|
+
oplog_limit = BSON::Timestamp.from_string(options[:oplogLimit])
|
31
|
+
end
|
32
|
+
|
33
|
+
oplog_start_at = BSON::Timestamp.from_string(config.options[:oplogStartAt]) if config.options[:oplogStartAt]
|
34
|
+
|
35
|
+
source_files = Oplog.find_oplogs(dir)
|
36
|
+
|
37
|
+
validate_continuity!(source_files)
|
38
|
+
|
39
|
+
MongoOplogBackup.log.debug "Starting oplog restore..."
|
40
|
+
source_files.each do |filename|
|
41
|
+
# TODO: mongorestore 3.4 supports --oplogFile
|
42
|
+
|
43
|
+
unless oplog_start_at.nil?
|
44
|
+
timestamps = Oplog.timestamps_from_filename(filename)
|
45
|
+
if timestamps[:last] <= oplog_start_at
|
46
|
+
MongoOplogBackup.log.debug "Skipping batch: #{filename}. Last op in batch (ts: #{timestamps[:last]}) is before oplogStartAt: #{oplog_start_at}"
|
47
|
+
next
|
48
|
+
end
|
49
|
+
end
|
50
|
+
unless oplog_limit.nil?
|
51
|
+
timestamps = Oplog.timestamps_from_filename(filename)
|
52
|
+
if timestamps[:first] > oplog_limit
|
53
|
+
MongoOplogBackup.log.debug "Skipping batch: #{filename}. First op in batch (ts: #{timestamps[:first]}) is after oplogLimit: #{oplog_limit}"
|
54
|
+
next
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
temp_file_path = create_temp_oplog_dir(dir, filename)
|
59
|
+
oplog_dir_path = File.dirname(temp_file_path)
|
60
|
+
|
61
|
+
restore_args = default_restore_args.dup
|
62
|
+
restore_args << '--gzip' if Oplog.gzip_fingerprint(filename)
|
63
|
+
restore_args << oplog_dir_path
|
64
|
+
status = config.mongorestore(restore_args).status
|
65
|
+
if status != 0
|
66
|
+
MongoOplogBackup.log.error("Mongorestore failed during oplog restore. Aborting. Exit code: #{status}")
|
67
|
+
raise 'Oplog restore failed.'
|
68
|
+
end
|
69
|
+
|
70
|
+
begin
|
71
|
+
File.unlink(temp_file_path)
|
72
|
+
Dir.rmdir(oplog_dir_path)
|
73
|
+
rescue SystemCallError => e
|
74
|
+
MongoOplogBackup.log.error("Clean-up error for '#{temp_file_path}. #{e.message}")
|
75
|
+
raise
|
76
|
+
end
|
77
|
+
end
|
78
|
+
MongoOplogBackup.log.debug "Oplog restore complete."
|
79
|
+
end
|
80
|
+
|
81
|
+
def restore_dump(dir, options={})
|
82
|
+
restore_args = ['--stopOnError']
|
83
|
+
restore_args << '--noIndexRestore' if !!config.options[:noIndexRestore]
|
84
|
+
restore_args << '--gzip' if config.use_compression?
|
85
|
+
restore_args << File.join(dir, 'dump')
|
86
|
+
|
87
|
+
MongoOplogBackup.log.debug "Starting full restore..."
|
88
|
+
status = config.mongorestore(restore_args).status
|
89
|
+
if status != 0
|
90
|
+
MongoOplogBackup.log.error("Mongorestore failed.")
|
91
|
+
raise 'Full restore failed.'
|
92
|
+
end
|
93
|
+
MongoOplogBackup.log.debug "Full restore complete."
|
94
|
+
end
|
95
|
+
|
96
|
+
def perform(mode, options={})
|
97
|
+
if options[:oplogLimit]
|
98
|
+
raise ArgumentError, "oplogLimit is not a timestamp: eg. <seconds>[:ordinal]" unless options[:oplogLimit] =~ /\A\d+(?::\d+)?\z/
|
99
|
+
end
|
100
|
+
|
101
|
+
if mode == :oplog
|
102
|
+
restore_oplogs(backup_folder, options)
|
103
|
+
elsif mode == :full
|
104
|
+
restore_dump(backup_folder, options)
|
105
|
+
restore_oplogs(backup_folder, options)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
# Check for gaps in timestamps based only on the filename, trusting that the oplog set is complete (ie. the filename timestamps match the actual data)
|
112
|
+
def validate_continuity!(source_files)
|
113
|
+
expected_first = nil
|
114
|
+
MongoOplogBackup.log.debug "Checking timestamps..."
|
115
|
+
source_files.each do |filename|
|
116
|
+
timestamps = Oplog.timestamps_from_filename(filename)
|
117
|
+
raise("Filename without timestamps found in restore set: #{filename}") if timestamps.nil?
|
118
|
+
if expected_first && expected_first != timestamps[:first]
|
119
|
+
raise "Missing oplog dump? Expected a filename with first timestamp '#{expected_first}', but got '#{timestamps[:first]}' from filename '#{filename}'."
|
120
|
+
end
|
121
|
+
expected_first = timestamps[:last]
|
122
|
+
end
|
123
|
+
true
|
124
|
+
end
|
125
|
+
|
126
|
+
def create_temp_oplog_dir dir, filename
|
127
|
+
temp_dir = File.join(dir, 'tmp-restore', File.basename(filename))
|
128
|
+
temp_file_path = File.join(temp_dir, 'oplog.bson')
|
129
|
+
FileUtils.mkdir_p(temp_dir)
|
130
|
+
File.link(filename, temp_file_path)
|
131
|
+
return temp_file_path
|
132
|
+
rescue Errno::EEXIST => e
|
133
|
+
# Probably, 'new_name' already exists when creating link.
|
134
|
+
MongoOplogBackup.log.warn("Temporary oplog.bson link already exists: #{e.message}. Assuming valid.")
|
135
|
+
return temp_file_path
|
136
|
+
rescue SystemCallError => e
|
137
|
+
MongoOplogBackup.log.error("Setup error for oplog '#{filename}' in '#{temp_file_path}")
|
138
|
+
raise
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
data/spec/timestamp_spec.rb
CHANGED
@@ -41,6 +41,15 @@ describe MongoOplogBackup::Ext::Timestamp do
|
|
41
41
|
ts.as_json.should == json
|
42
42
|
end
|
43
43
|
|
44
|
+
it 'should handle extended-json timestamp format' do
|
45
|
+
json = { "t" => 1408004593, "i" => 20}
|
46
|
+
extendedjson = {"$timestamp" => json}
|
47
|
+
ts = BSON::Timestamp.from_json(extendedjson)
|
48
|
+
ts.seconds.should == 1408004593
|
49
|
+
ts.increment.should == 20
|
50
|
+
ts.as_json.should == json
|
51
|
+
end
|
52
|
+
|
44
53
|
it 'should define to_s' do
|
45
54
|
ts = BSON::Timestamp.new(1408004593, 2)
|
46
55
|
ts.to_s.should == '1408004593:2'
|
data/testing.sh
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/bin/bash -ex
|
2
|
+
|
3
|
+
echo "This requires a mongodb instance running on 27017."
|
4
|
+
echo "It will first SHUTDOWN any instances already running on that port, and then start new ones."
|
5
|
+
echo "If this is undesired, CTRL+C within 5 seconds..."
|
6
|
+
sleep 5
|
7
|
+
echo "Here we go..."
|
8
|
+
mongo --port 27017 admin --eval 'db.shutdownServer({force: true})' || true
|
9
|
+
sleep 5
|
10
|
+
rm -rf backup-test/*
|
11
|
+
rm -rf testdb
|
12
|
+
mkdir testdb
|
13
|
+
mongod --port 27017 --dbpath testdb --replSet rs0 --oplogSize 20 --noprealloc --fork --smallfiles --logpath mongodb.log
|
14
|
+
sleep 3
|
15
|
+
mongo --port 27017 admin --eval 'printjson(rs.initiate());'
|
16
|
+
sleep 20
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
bundle exec rspec
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
bundle exec bin/mongo-oplog-backup backup --port 27017 --dir backup-test/ --gzip --full
|
25
|
+
mongo --port 27017 backup-test --eval 'db.test.insert({"a":2})'
|
26
|
+
|
27
|
+
bundle exec bin/mongo-oplog-backup backup --port 27017 --dir backup-test/ --gzip --oplog
|
28
|
+
|
29
|
+
sleep 5
|
30
|
+
mongo --port 27017 backup-test --eval 'db.test.insert({"a":3})'
|
31
|
+
bundle exec bin/mongo-oplog-backup backup --port 27017 --dir backup-test/ --oplog
|
32
|
+
|
33
|
+
|
34
|
+
sleep 5
|
35
|
+
mongo --port 27017 backup-test --eval 'db.test.insert({"a":4})'
|
36
|
+
bundle exec bin/mongo-oplog-backup backup --port 27017 --dir backup-test/ --oplog
|
37
|
+
|
38
|
+
|
39
|
+
|
40
|
+
mongo --port 27017 admin --eval 'db.shutdownServer({force: true})'
|
41
|
+
sleep 5
|
42
|
+
rm -rf testdb/*
|
43
|
+
mongod --port 27017 --dbpath testdb --replSet rs0 --oplogSize 20 --noprealloc --fork --smallfiles --logpath mongodb.log
|
44
|
+
sleep 3
|
45
|
+
mongo --port 27017 admin --eval 'printjson(rs.initiate());'
|
46
|
+
sleep 20
|
47
|
+
|
48
|
+
export BACKUPDIR=`ls -1t backup-test/ |grep backup- |head -n 1`
|
49
|
+
|
50
|
+
bundle exec bin/mongo-oplog-backup restore --full --gzip --dir backup-test/$BACKUPDIR --port 27017
|
51
|
+
mongo --port 27017 backup-test --eval 'db.test.find()'
|
52
|
+
|
53
|
+
|
54
|
+
#mongorestore --gzip --port 27017 backup-test/$BACKUPDIR/dump
|
55
|
+
#mongo --port 27017 backup-test --eval 'db.test.find()'
|
56
|
+
#bundle exec bin/mongo-oplog-backup restore --oplog --dir backup-test/$BACKUPDIR --port 27017
|
57
|
+
#mongo --port 27017 backup-test --eval 'db.test.find()'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongo-oplog-backup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ralf Kistner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-08-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bson
|
@@ -118,6 +118,7 @@ files:
|
|
118
118
|
- lib/mongo_oplog_backup/ext/enumerable.rb
|
119
119
|
- lib/mongo_oplog_backup/ext/timestamp.rb
|
120
120
|
- lib/mongo_oplog_backup/oplog.rb
|
121
|
+
- lib/mongo_oplog_backup/restore.rb
|
121
122
|
- lib/mongo_oplog_backup/version.rb
|
122
123
|
- mongo-oplog-backup.gemspec
|
123
124
|
- oplog-last-timestamp.js
|
@@ -136,6 +137,7 @@ files:
|
|
136
137
|
- spec/oplog_spec.rb
|
137
138
|
- spec/spec_helper.rb
|
138
139
|
- spec/timestamp_spec.rb
|
140
|
+
- testing.sh
|
139
141
|
homepage: ''
|
140
142
|
licenses:
|
141
143
|
- MIT
|