mongo-db-utils 0.0.9 → 0.0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +0 -5
- data/README.md +18 -3
- data/integration-test-env/.gitignore +4 -0
- data/integration-test-env/README.md +29 -0
- data/integration-test-env/create_two_replica_sets +76 -0
- data/integration-test-env/create_two_standalone_dbs +27 -0
- data/integration-test-env/helper.rb +50 -0
- data/integration-test-env/kill_processes +19 -0
- data/integration-test-env/replica_sets/config.yml +21 -0
- data/integration-test-env/seed_replica_sets +15 -0
- data/integration-test-env/standalone/config.yml +19 -0
- data/lib/mongo-db-utils/cli.rb +27 -10
- data/lib/mongo-db-utils/cmd.rb +8 -24
- data/lib/mongo-db-utils/config-loader.rb +33 -24
- data/lib/mongo-db-utils/console.rb +68 -5
- data/lib/mongo-db-utils/models/bucket.rb +17 -0
- data/lib/mongo-db-utils/models/config.rb +82 -0
- data/lib/mongo-db-utils/models/db.rb +81 -0
- data/lib/mongo-db-utils/tools/commands.rb +80 -0
- data/lib/mongo-db-utils/version.rb +1 -1
- data/mongo-db-utils.gemspec +4 -4
- data/spec/config_loader_spec.rb +30 -0
- data/spec/config_proxy_spec.rb +33 -0
- data/spec/models/config_spec.rb +52 -0
- data/spec/models/db_spec.rb +36 -0
- data/spec/mongo_tools_cmd_spec.rb +38 -0
- metadata +42 -45
- data/lib/mongo-db-utils/cmd/mongotools.rb +0 -68
- data/lib/mongo-db-utils/models.rb +0 -161
- data/spec/config-loader-spec.rb +0 -21
- data/spec/mongo_db_utils_spec.rb +0 -65
@@ -1,59 +1,68 @@
|
|
1
|
-
|
1
|
+
project_root = File.dirname(File.absolute_path(__FILE__))
|
2
|
+
|
3
|
+
Dir.glob(project_root + '/lib/mongo-db-utils/models/*', &method(:require))
|
4
|
+
|
2
5
|
require 'yaml'
|
3
6
|
|
4
7
|
module MongoDbUtils
|
5
8
|
|
6
9
|
class ConfigLoader
|
7
|
-
|
10
|
+
|
8
11
|
ROOT_FOLDER = "~/.mongo-db-utils"
|
9
12
|
CONFIG_LOCATION = "#{ROOT_FOLDER}/config.yml"
|
10
13
|
|
11
|
-
|
12
|
-
full_path = File.expand_path(load_path)
|
13
|
-
puts "loading config from #{full_path}"
|
14
|
+
attr_reader :config
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
config
|
19
|
-
else
|
20
|
-
self.create_fresh_install_config(full_path)
|
21
|
-
end
|
16
|
+
def initialize(config_path)
|
17
|
+
@config_path = config_path
|
18
|
+
load
|
22
19
|
end
|
23
20
|
|
24
|
-
def
|
25
|
-
path = File.expand_path(
|
21
|
+
def flush
|
22
|
+
path = File.expand_path(@config_path)
|
26
23
|
puts "removing: #{path}"
|
27
24
|
FileUtils.rm(path) if File.exist?(path)
|
28
|
-
|
25
|
+
initialize_files(path)
|
29
26
|
end
|
30
27
|
|
31
|
-
def
|
28
|
+
def save(config)
|
32
29
|
raise "config is nil" if config.nil?
|
33
|
-
|
34
|
-
File.open( File.expand_path(path), 'w' ) do |out|
|
30
|
+
File.open( File.expand_path(@config_path), 'w' ) do |out|
|
35
31
|
YAML.dump( config, out )
|
36
32
|
end
|
37
33
|
end
|
38
34
|
|
39
|
-
private
|
40
|
-
def
|
35
|
+
private
|
36
|
+
def load
|
37
|
+
full_path = File.expand_path(@config_path)
|
38
|
+
puts "loading config from #{full_path}"
|
39
|
+
|
40
|
+
if File.exist?(full_path) && YAML.load(File.open(full_path))
|
41
|
+
config = YAML.load(File.open(full_path))
|
42
|
+
config.writer = self
|
43
|
+
@config = config
|
44
|
+
else
|
45
|
+
@config = create_fresh_install_config(full_path)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def create_fresh_install_config(full_path)
|
41
50
|
config = Model::Config.new
|
42
51
|
config.writer = self
|
43
52
|
config.backup_folder = "#{ROOT_FOLDER}/backups"
|
44
|
-
|
53
|
+
initialize_files(full_path)
|
45
54
|
File.open( full_path, 'w' ) do |out|
|
46
55
|
YAML.dump( config, out )
|
47
56
|
end
|
48
57
|
config
|
49
58
|
end
|
50
59
|
|
51
|
-
def
|
60
|
+
def get_folder_name(path)
|
52
61
|
/(.*)\/.*.yml/.match(path)[1]
|
53
62
|
end
|
54
63
|
|
55
|
-
def
|
56
|
-
folder =
|
64
|
+
def initialize_files(path)
|
65
|
+
folder = get_folder_name(path)
|
57
66
|
FileUtils.mkdir_p(folder) unless File.exist?(folder)
|
58
67
|
FileUtils.touch(path)
|
59
68
|
end
|
@@ -1,8 +1,45 @@
|
|
1
1
|
require 'highline/import'
|
2
2
|
require 'mongo-db-utils/version'
|
3
|
-
|
3
|
+
Dir['lib/mongo-db-utils/models/*.rb'].each {|file| require file.gsub("lib/", "") }
|
4
|
+
|
4
5
|
|
5
6
|
module MongoDbUtils
|
7
|
+
|
8
|
+
# This is a workaround for this issue:
|
9
|
+
# https://github.com/JEG2/highline/issues/69
|
10
|
+
# In ruby 2 + highline the yaml strings don't get serialized correctly.
|
11
|
+
# The workaround is for any argument that is of type HighLine::String to call to_s on it
|
12
|
+
class ConfigProxy
|
13
|
+
|
14
|
+
def initialize(config)
|
15
|
+
@config = config
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
def method_missing(name, *args, &block)
|
20
|
+
cleaned_args = args.map{ |a| trim(clean(a)) }
|
21
|
+
cleaned_args.each{ |a| puts "#{a} -> #{a.class}"}
|
22
|
+
@config.send(name, *cleaned_args, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def clean(a)
|
26
|
+
if a.class.to_s == "HighLine::String"
|
27
|
+
a.to_s
|
28
|
+
else
|
29
|
+
a
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def trim(s)
|
34
|
+
if(s.class.to_s == "String")
|
35
|
+
s.strip
|
36
|
+
else
|
37
|
+
s
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
|
6
43
|
class Console
|
7
44
|
|
8
45
|
HEADER = <<-eos
|
@@ -13,7 +50,7 @@ eos
|
|
13
50
|
|
14
51
|
|
15
52
|
def initialize(config, cmd)
|
16
|
-
@config = config
|
53
|
+
@config = ConfigProxy.new(config)
|
17
54
|
@cmd = cmd
|
18
55
|
end
|
19
56
|
|
@@ -128,9 +165,15 @@ eos
|
|
128
165
|
end
|
129
166
|
|
130
167
|
def add_config
|
131
|
-
|
132
|
-
|
133
|
-
|
168
|
+
my_menu("Single db or replica set?") do |menu|
|
169
|
+
menu.choice "single db" do add_single_db end
|
170
|
+
menu.choice "replica set db" do add_replica_set end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def add_single_db
|
175
|
+
mongo_uri = ask("Mongo uri (eg: 'mongodb://user:pass@locahost:27017/db')")
|
176
|
+
new_uri = mongo_uri.to_s.strip
|
134
177
|
successful = @config.add_db_from_uri(new_uri)
|
135
178
|
|
136
179
|
say("bad uri!") unless successful
|
@@ -142,6 +185,21 @@ eos
|
|
142
185
|
end
|
143
186
|
end
|
144
187
|
|
188
|
+
def add_replica_set
|
189
|
+
mongo_uri = ask("Replica Set uri: (eg: mongodb://user:pass@host1:port,host2:port,.../db)1
|
190
|
+
")
|
191
|
+
replica_set_name = ask("Replica Set name: ")
|
192
|
+
|
193
|
+
successful = @config.add_replica_set(mongo_uri, replica_set_name)
|
194
|
+
|
195
|
+
say("bad replica set uri") unless successful
|
196
|
+
|
197
|
+
my_menu("") do |menu|
|
198
|
+
menu.choice (successful ? "add another" : "try again?") do add_config end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
|
145
203
|
def remove_server_from_config
|
146
204
|
db_list_menu("Remove server from config:") do |db|
|
147
205
|
@config.remove_db(db)
|
@@ -209,5 +267,10 @@ eos
|
|
209
267
|
end
|
210
268
|
end
|
211
269
|
|
270
|
+
private
|
271
|
+
def clean(s)
|
272
|
+
s.to_s.strip
|
273
|
+
end
|
274
|
+
|
212
275
|
end
|
213
276
|
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'mongo-db-utils/models/db'
|
2
|
+
module MongoDbUtils
|
3
|
+
|
4
|
+
module Model
|
5
|
+
class Config
|
6
|
+
attr_reader :dbs, :buckets
|
7
|
+
attr_writer :writer
|
8
|
+
attr_accessor :backup_folder
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@dbs = []
|
12
|
+
@buckets = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def empty?
|
16
|
+
@dbs.nil? || @dbs.empty?
|
17
|
+
end
|
18
|
+
|
19
|
+
def has_buckets?
|
20
|
+
!@buckets.nil? && !@buckets.empty?
|
21
|
+
end
|
22
|
+
|
23
|
+
def flush
|
24
|
+
@dbs = []
|
25
|
+
@writer.flush
|
26
|
+
end
|
27
|
+
|
28
|
+
def remove_db(db)
|
29
|
+
@dbs = @dbs - [db]
|
30
|
+
save
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_replica_set(uri, name)
|
34
|
+
add_db ReplicaSetDb.new(uri,name)
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def add_db_from_uri(uri)
|
39
|
+
add_db Db.new(uri)
|
40
|
+
end
|
41
|
+
|
42
|
+
def already_contains(db)
|
43
|
+
!@dbs.find{|current| current.uri == db.uri }.nil?
|
44
|
+
end
|
45
|
+
|
46
|
+
# because we are serializing the config - the bucket may be nil
|
47
|
+
# at this point
|
48
|
+
def add_bucket(bucket)
|
49
|
+
@buckets = [] if @buckets.nil?
|
50
|
+
unless already_contains_bucket?(bucket)
|
51
|
+
@buckets << bucket
|
52
|
+
save
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def already_contains_bucket?(bucket)
|
57
|
+
!@buckets.find{ |b| b.to_s == bucket.to_s}.nil?
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_s
|
61
|
+
"Config"
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
def save
|
66
|
+
@writer.save(self) unless @writer.nil?
|
67
|
+
end
|
68
|
+
|
69
|
+
def add_db(db)
|
70
|
+
@dbs = [] if @dbs.nil?
|
71
|
+
unless db.nil? || already_contains(db)
|
72
|
+
@dbs << db
|
73
|
+
@dbs.sort!
|
74
|
+
save
|
75
|
+
end
|
76
|
+
@dbs.include?(db) && !db.nil?
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module MongoDbUtils
|
2
|
+
module Model
|
3
|
+
|
4
|
+
# A Db stored in the config
|
5
|
+
class Db
|
6
|
+
|
7
|
+
URI_NO_USER = /mongodb:\/\/(.*)\/(.*$)/
|
8
|
+
URI_USER = /mongodb:\/\/(.*):(.*)@(.*)\/(.*$)/
|
9
|
+
|
10
|
+
attr_accessor :username, :password, :name, :uri
|
11
|
+
|
12
|
+
def initialize(uri)
|
13
|
+
user,pwd,host_port,db = nil
|
14
|
+
|
15
|
+
if( uri.match(URI_USER))
|
16
|
+
match, user, pwd, host_port, name = *uri.match(URI_USER)
|
17
|
+
elsif(uri.match(URI_NO_USER))
|
18
|
+
match, host_port, name = *uri.match(URI_NO_USER)
|
19
|
+
user = ""
|
20
|
+
pwd = ""
|
21
|
+
end
|
22
|
+
|
23
|
+
raise "can't parse uri" if( host_port.nil? || name.nil? )
|
24
|
+
|
25
|
+
@host_port = host_port
|
26
|
+
@name = name
|
27
|
+
@username = user
|
28
|
+
@password = pwd
|
29
|
+
@uri = uri
|
30
|
+
end
|
31
|
+
|
32
|
+
def authentication_required?
|
33
|
+
has?(self.username) && has?(self.password)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Return the host string in a format that is compatable with mongo binary tools
|
37
|
+
# See: http://docs.mongodb.org/manual/reference/program/mongodump/#cmdoption-mongodump--host
|
38
|
+
def to_host_s
|
39
|
+
"#{@host_port}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_s_simple
|
43
|
+
"#{@name} on #{@host_port} - (#{@username}:#{@password})"
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_s
|
47
|
+
"[SingleDb-(#{to_host_s}/#{name})]"
|
48
|
+
end
|
49
|
+
|
50
|
+
def <=>(other)
|
51
|
+
self.to_s <=> other.to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
def has?(s)
|
56
|
+
!s.nil? && !s.empty?
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
class ReplicaSetDb < Db
|
62
|
+
|
63
|
+
attr_reader :set_name
|
64
|
+
def initialize(uri, name)
|
65
|
+
super(uri)
|
66
|
+
@set_name = name
|
67
|
+
end
|
68
|
+
|
69
|
+
# Note: we override this to provide a replica set format
|
70
|
+
def to_host_s
|
71
|
+
"#{@set_name}/#{@host_port}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_s
|
75
|
+
"[ReplicaSetDb-(#{to_host_s}/#{@name})]"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'aws/s3'
|
2
|
+
|
3
|
+
module MongoDbUtils
|
4
|
+
|
5
|
+
module Tools
|
6
|
+
|
7
|
+
class BaseCmd
|
8
|
+
private
|
9
|
+
def self.o(key,value)
|
10
|
+
Option.new(key,value)
|
11
|
+
end
|
12
|
+
|
13
|
+
# options common to all commands
|
14
|
+
def self.build_base_options(host_and_port,db,username="",password="")
|
15
|
+
options = []
|
16
|
+
options << o("-h", host_and_port)
|
17
|
+
options << o("-db", db)
|
18
|
+
options << o("-u", username)
|
19
|
+
options << o("-p", password)
|
20
|
+
options
|
21
|
+
end
|
22
|
+
|
23
|
+
# given an array of options build a string of those options unless the option is empty
|
24
|
+
def self.options_string(opts)
|
25
|
+
out = ""
|
26
|
+
opts.each do |o|
|
27
|
+
out << "#{o.key} #{o.value} " unless o.empty?
|
28
|
+
end
|
29
|
+
out.strip
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Dump < BaseCmd
|
34
|
+
|
35
|
+
# create the cmd string that will be executed by the system
|
36
|
+
def self.cmd(host_and_port,db,output,username = "", password = "")
|
37
|
+
options = build_base_options(host_and_port,db,username,password)
|
38
|
+
options << o("-o", output)
|
39
|
+
"mongodump #{options_string(options)}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# run the command
|
43
|
+
def self.run(host_and_port,db,output,username="", password ="")
|
44
|
+
cmd_string = self.cmd(host_and_port,db,output,username,password)
|
45
|
+
puts "[Dump] run: #{cmd_string}"
|
46
|
+
`#{cmd_string}`
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
class Restore < BaseCmd
|
52
|
+
def self.cmd(host_and_port,db,source_folder,username = "", password = "")
|
53
|
+
options = build_base_options(host_and_port,db,username,password)
|
54
|
+
params = options_string(options) << " --drop #{source_folder}"
|
55
|
+
"mongorestore #{params}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.run(host_and_port,db,source_folder,username="", password ="")
|
59
|
+
cmd_string = self.cmd(host_and_port,db,source_folder,username,password)
|
60
|
+
puts "[Restore] run: #{cmd_string}"
|
61
|
+
`#{cmd_string}`
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
class Option
|
67
|
+
attr_accessor :key, :value
|
68
|
+
|
69
|
+
def initialize(key,value)
|
70
|
+
@key = key
|
71
|
+
@value = value
|
72
|
+
end
|
73
|
+
|
74
|
+
def empty?
|
75
|
+
@value.nil? || @value.empty?
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|