PageRenderTime 47ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/spec/database/mongodb_spec.rb

http://github.com/meskyanichi/backup
Ruby | 391 lines | 328 code | 63 blank | 0 comment | 5 complexity | 223d3079a2cad76110906d1f4677c801 MD5 | raw file
  1. require "spec_helper"
  2. module Backup
  3. describe Database::MongoDB do
  4. let(:model) { Model.new(:test_trigger, "test label") }
  5. let(:db) { Database::MongoDB.new(model) }
  6. before do
  7. allow_any_instance_of(Database::MongoDB).to receive(:utility)
  8. .with(:mongodump).and_return("mongodump")
  9. allow_any_instance_of(Database::MongoDB).to receive(:utility)
  10. .with(:mongo).and_return("mongo")
  11. allow_any_instance_of(Database::MongoDB).to receive(:utility)
  12. .with(:cat).and_return("cat")
  13. allow_any_instance_of(Database::MongoDB).to receive(:utility)
  14. .with(:tar).and_return("tar")
  15. end
  16. it_behaves_like "a class that includes Config::Helpers"
  17. it_behaves_like "a subclass of Database::Base"
  18. describe "#initialize" do
  19. it "provides default values" do
  20. expect(db.database_id).to be_nil
  21. expect(db.name).to be_nil
  22. expect(db.username).to be_nil
  23. expect(db.password).to be_nil
  24. expect(db.authdb).to be_nil
  25. expect(db.host).to be_nil
  26. expect(db.port).to be_nil
  27. expect(db.ipv6).to be_nil
  28. expect(db.only_collections).to be_nil
  29. expect(db.additional_options).to be_nil
  30. expect(db.lock).to be_nil
  31. expect(db.oplog).to be_nil
  32. end
  33. it "configures the database" do
  34. db = Database::MongoDB.new(model, :my_id) do |mongodb|
  35. mongodb.name = "my_name"
  36. mongodb.username = "my_username"
  37. mongodb.password = "my_password"
  38. mongodb.authdb = "my_authdb"
  39. mongodb.host = "my_host"
  40. mongodb.port = "my_port"
  41. mongodb.ipv6 = "my_ipv6"
  42. mongodb.only_collections = "my_only_collections"
  43. mongodb.additional_options = "my_additional_options"
  44. mongodb.lock = "my_lock"
  45. mongodb.oplog = "my_oplog"
  46. end
  47. expect(db.database_id).to eq "my_id"
  48. expect(db.name).to eq "my_name"
  49. expect(db.username).to eq "my_username"
  50. expect(db.password).to eq "my_password"
  51. expect(db.authdb).to eq "my_authdb"
  52. expect(db.host).to eq "my_host"
  53. expect(db.port).to eq "my_port"
  54. expect(db.ipv6).to eq "my_ipv6"
  55. expect(db.only_collections).to eq "my_only_collections"
  56. expect(db.additional_options).to eq "my_additional_options"
  57. expect(db.lock).to eq "my_lock"
  58. expect(db.oplog).to eq "my_oplog"
  59. end
  60. end # describe '#initialize'
  61. describe "#perform!" do
  62. before do
  63. expect(db).to receive(:log!).ordered.with(:started)
  64. expect(db).to receive(:prepare!).ordered
  65. end
  66. context "with #lock set to false" do
  67. it "does not lock the database" do
  68. expect(db).to receive(:lock_database).never
  69. expect(db).to receive(:unlock_database).never
  70. expect(db).to receive(:dump!).ordered
  71. expect(db).to receive(:package!).ordered
  72. db.perform!
  73. end
  74. end
  75. context "with #lock set to true" do
  76. before { db.lock = true }
  77. it "locks the database" do
  78. expect(db).to receive(:lock_database).ordered
  79. expect(db).to receive(:dump!).ordered
  80. expect(db).to receive(:package!).ordered
  81. expect(db).to receive(:unlock_database).ordered
  82. db.perform!
  83. end
  84. it "ensures the database is unlocked" do
  85. expect(db).to receive(:lock_database).ordered
  86. expect(db).to receive(:dump!).ordered
  87. expect(db).to receive(:package!).ordered.and_raise("an error")
  88. expect(db).to receive(:unlock_database).ordered
  89. expect do
  90. db.perform!
  91. end.to raise_error "an error"
  92. end
  93. end
  94. end # describe '#perform!'
  95. describe "#dump!" do
  96. before do
  97. allow(db).to receive(:mongodump).and_return("mongodump_command")
  98. allow(db).to receive(:dump_path).and_return("/tmp/trigger/databases")
  99. expect(FileUtils).to receive(:mkdir_p).ordered
  100. .with("/tmp/trigger/databases/MongoDB")
  101. end
  102. context "when #only_collections are not specified" do
  103. it "runs mongodump once" do
  104. expect(db).to receive(:run).ordered.with("mongodump_command")
  105. db.send(:dump!)
  106. end
  107. end
  108. context "when #only_collections are specified" do
  109. it "runs mongodump for each collection" do
  110. db.only_collections = ["collection_a", "collection_b"]
  111. expect(db).to receive(:run).ordered.with(
  112. "mongodump_command --collection='collection_a'"
  113. )
  114. expect(db).to receive(:run).ordered.with(
  115. "mongodump_command --collection='collection_b'"
  116. )
  117. db.send(:dump!)
  118. end
  119. it "allows only_collections to be a single string" do
  120. db.only_collections = "collection_a"
  121. expect(db).to receive(:run).ordered.with(
  122. "mongodump_command --collection='collection_a'"
  123. )
  124. db.send(:dump!)
  125. end
  126. end
  127. end # describe '#dump!'
  128. describe "#package!" do
  129. let(:pipeline) { double }
  130. let(:compressor) { double }
  131. before do
  132. allow(db).to receive(:dump_path).and_return("/tmp/trigger/databases")
  133. end
  134. context "without a compressor" do
  135. it "packages the dump without compression" do
  136. expect(Pipeline).to receive(:new).ordered.and_return(pipeline)
  137. expect(pipeline).to receive(:<<).ordered.with(
  138. "tar -cf - -C '/tmp/trigger/databases' 'MongoDB'"
  139. )
  140. expect(pipeline).to receive(:<<).ordered.with(
  141. "cat > '/tmp/trigger/databases/MongoDB.tar'"
  142. )
  143. expect(pipeline).to receive(:run).ordered
  144. expect(pipeline).to receive(:success?).ordered.and_return(true)
  145. expect(FileUtils).to receive(:rm_rf).ordered.with(
  146. "/tmp/trigger/databases/MongoDB"
  147. )
  148. expect(db).to receive(:log!).ordered.with(:finished)
  149. db.send(:package!)
  150. end
  151. end # context 'without a compressor'
  152. context "with a compressor" do
  153. before do
  154. allow(model).to receive(:compressor).and_return(compressor)
  155. allow(compressor).to receive(:compress_with).and_yield("cmp_cmd", ".cmp_ext")
  156. end
  157. it "packages the dump with compression" do
  158. expect(Pipeline).to receive(:new).ordered.and_return(pipeline)
  159. expect(pipeline).to receive(:<<).ordered.with(
  160. "tar -cf - -C '/tmp/trigger/databases' 'MongoDB'"
  161. )
  162. expect(pipeline).to receive(:<<).ordered.with("cmp_cmd")
  163. expect(pipeline).to receive(:<<).ordered.with(
  164. "cat > '/tmp/trigger/databases/MongoDB.tar.cmp_ext'"
  165. )
  166. expect(pipeline).to receive(:run).ordered
  167. expect(pipeline).to receive(:success?).ordered.and_return(true)
  168. expect(FileUtils).to receive(:rm_rf).ordered.with(
  169. "/tmp/trigger/databases/MongoDB"
  170. )
  171. expect(db).to receive(:log!).ordered.with(:finished)
  172. db.send(:package!)
  173. end
  174. end # context 'with a compressor'
  175. context "when the pipeline fails" do
  176. before do
  177. allow_any_instance_of(Pipeline).to receive(:success?).and_return(false)
  178. allow_any_instance_of(Pipeline).to receive(:error_messages).and_return("error messages")
  179. end
  180. it "raises an error and does not remove the packaging path" do
  181. expect(FileUtils).to receive(:rm_rf).never
  182. expect(db).to receive(:log!).never
  183. expect do
  184. db.send(:package!)
  185. end.to raise_error(Database::MongoDB::Error) { |err|
  186. expect(err.message).to eq(
  187. "Database::MongoDB::Error: Dump Failed!\n error messages"
  188. )
  189. }
  190. end
  191. end # context 'when the pipeline fails'
  192. end # describe '#package!'
  193. describe "#mongodump" do
  194. let(:option_methods) do
  195. %w[
  196. name_option credential_options connectivity_options
  197. ipv6_option oplog_option user_options dump_packaging_path
  198. ]
  199. end
  200. it "returns full mongodump command built from all options" do
  201. option_methods.each { |name| allow(db).to receive(name).and_return(name) }
  202. expect(db.send(:mongodump)).to eq(
  203. "mongodump name_option credential_options connectivity_options " \
  204. "ipv6_option oplog_option user_options --out='dump_packaging_path'"
  205. )
  206. end
  207. it "handles nil values from option methods" do
  208. option_methods.each { |name| allow(db).to receive(name).and_return(nil) }
  209. expect(db.send(:mongodump)).to eq "mongodump --out=''"
  210. end
  211. end # describe '#mongodump'
  212. describe "mongo and monogodump option methods" do
  213. describe "#name_option" do
  214. it "returns database argument if #name is specified" do
  215. expect(db.send(:name_option)).to be_nil
  216. db.name = "my_database"
  217. expect(db.send(:name_option)).to eq "--db='my_database'"
  218. end
  219. end # describe '#name_option'
  220. describe "#credential_options" do
  221. it "returns credentials arguments based on #username and #password and #authdb" do
  222. expect(db.send(:credential_options)).to eq ""
  223. db.username = "my_user"
  224. expect(db.send(:credential_options)).to eq(
  225. "--username='my_user'"
  226. )
  227. db.password = "my_password"
  228. expect(db.send(:credential_options)).to eq(
  229. "--username='my_user' --password='my_password'"
  230. )
  231. db.authdb = "my_authdb"
  232. expect(db.send(:credential_options)).to eq(
  233. "--username='my_user' --password='my_password' --authenticationDatabase='my_authdb'"
  234. )
  235. db.username = nil
  236. expect(db.send(:credential_options)).to eq(
  237. "--password='my_password' --authenticationDatabase='my_authdb'"
  238. )
  239. db.authdb = nil
  240. expect(db.send(:credential_options)).to eq(
  241. "--password='my_password'"
  242. )
  243. end
  244. end # describe '#credential_options'
  245. describe "#connectivity_options" do
  246. it "returns connectivity arguments based on #host and #port" do
  247. expect(db.send(:connectivity_options)).to eq ""
  248. db.host = "my_host"
  249. expect(db.send(:connectivity_options)).to eq(
  250. "--host='my_host'"
  251. )
  252. db.port = "my_port"
  253. expect(db.send(:connectivity_options)).to eq(
  254. "--host='my_host' --port='my_port'"
  255. )
  256. db.host = nil
  257. expect(db.send(:connectivity_options)).to eq(
  258. "--port='my_port'"
  259. )
  260. end
  261. end # describe '#connectivity_options'
  262. describe "#ipv6_option" do
  263. it "returns the ipv6 argument if #ipv6 is true" do
  264. expect(db.send(:ipv6_option)).to be_nil
  265. db.ipv6 = true
  266. expect(db.send(:ipv6_option)).to eq "--ipv6"
  267. end
  268. end # describe '#ipv6_option'
  269. describe "#oplog_option" do
  270. it "returns the oplog argument if #oplog is true" do
  271. expect(db.send(:oplog_option)).to be_nil
  272. db.oplog = true
  273. expect(db.send(:oplog_option)).to eq "--oplog"
  274. end
  275. end # describe '#oplog_option'
  276. describe "#user_options" do
  277. it "returns arguments for any #additional_options specified" do
  278. expect(db.send(:user_options)).to eq ""
  279. db.additional_options = ["--opt1", "--opt2"]
  280. expect(db.send(:user_options)).to eq "--opt1 --opt2"
  281. db.additional_options = "--opta --optb"
  282. expect(db.send(:user_options)).to eq "--opta --optb"
  283. end
  284. end # describe '#user_options'
  285. end # describe 'mongo and monogodump option methods'
  286. describe "#lock_database" do
  287. it "runs command to disable profiling and lock the database" do
  288. db = Database::MongoDB.new(model)
  289. allow(db).to receive(:mongo_shell).and_return("mongo_shell")
  290. expect(db).to receive(:run).with(
  291. "echo 'use admin\n" \
  292. "db.setProfilingLevel(0)\n" \
  293. "db.fsyncLock()' | mongo_shell\n"
  294. )
  295. db.send(:lock_database)
  296. end
  297. end # describe '#lock_database'
  298. describe "#unlock_database" do
  299. it "runs command to unlock the database" do
  300. db = Database::MongoDB.new(model)
  301. allow(db).to receive(:mongo_shell).and_return("mongo_shell")
  302. expect(db).to receive(:run).with(
  303. "echo 'use admin\n" \
  304. "db.fsyncUnlock()' | mongo_shell\n"
  305. )
  306. db.send(:unlock_database)
  307. end
  308. end # describe '#unlock_database'
  309. describe "#mongo_shell" do
  310. specify "with all options" do
  311. db.host = "my_host"
  312. db.port = "my_port"
  313. db.username = "my_user"
  314. db.password = "my_pwd"
  315. db.authdb = "my_authdb"
  316. db.ipv6 = true
  317. db.name = "my_db"
  318. expect(db.send(:mongo_shell)).to eq(
  319. "mongo --host='my_host' --port='my_port' --username='my_user' " \
  320. "--password='my_pwd' --authenticationDatabase='my_authdb' --ipv6 'my_db'"
  321. )
  322. end
  323. specify "with no options" do
  324. expect(db.send(:mongo_shell)).to eq "mongo"
  325. end
  326. end # describe '#mongo_shell'
  327. end
  328. end