motherbrain 0.0.0.placeholder → 0.13.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (259) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +9 -0
  5. data/CHANGELOG.md +196 -0
  6. data/COMMANDS.md +9 -0
  7. data/CONTRIBUTING.md +24 -0
  8. data/Dockerfile +26 -0
  9. data/Gemfile +60 -2
  10. data/Guardfile +30 -0
  11. data/LICENSE +10 -0
  12. data/MANIFESTS.md +90 -0
  13. data/OPERATORS_GUIDE.md +195 -0
  14. data/PLUGINS.md +268 -0
  15. data/README.md +304 -16
  16. data/Thorfile +123 -0
  17. data/VAGRANT.md +116 -0
  18. data/bin/boot +9 -0
  19. data/bin/mb +5 -0
  20. data/bin/mbsrv +5 -0
  21. data/config.json +32 -0
  22. data/features/cli/bootstrap_command/configurable_scripts.feature +32 -0
  23. data/features/cli/configure_command.feature +57 -0
  24. data/features/cli/environment_command/create_command.feature +22 -0
  25. data/features/cli/environment_command/destroy_command.feature +33 -0
  26. data/features/cli/environment_command/from_command.feature +29 -0
  27. data/features/cli/environment_command/list_command.feature +0 -0
  28. data/features/cli/node_limiting.feature +47 -0
  29. data/features/cli/plugin_command/list_command.feature +46 -0
  30. data/features/cli/service_command/service_command.feature +21 -0
  31. data/features/cli/template_command.feature +10 -0
  32. data/features/cli/validate_config.feature +46 -0
  33. data/features/step_definitions/bootstrap_steps.rb +57 -0
  34. data/features/step_definitions/chef_server_steps.rb +3 -0
  35. data/features/step_definitions/configuration_steps.rb +18 -0
  36. data/features/step_definitions/core_cli_steps.rb +33 -0
  37. data/features/step_definitions/environment_steps.rb +43 -0
  38. data/features/step_definitions/node_steps.rb +3 -0
  39. data/features/step_definitions/plugin_steps.rb +15 -0
  40. data/features/step_definitions/template_steps.rb +7 -0
  41. data/features/support/env.rb +68 -0
  42. data/lib/mb/api.rb +8 -0
  43. data/lib/mb/api/application.rb +7 -0
  44. data/lib/mb/api/endpoint.rb +5 -0
  45. data/lib/mb/api/helpers.rb +38 -0
  46. data/lib/mb/api/v1.rb +56 -0
  47. data/lib/mb/api/v1/config_endpoint.rb +12 -0
  48. data/lib/mb/api/v1/environments_endpoint.rb +174 -0
  49. data/lib/mb/api/v1/jobs_endpoint.rb +31 -0
  50. data/lib/mb/api/v1/plugins_endpoint.rb +90 -0
  51. data/lib/mb/api/validators.rb +5 -0
  52. data/lib/mb/api/validators/sem_ver.rb +18 -0
  53. data/lib/mb/application.rb +148 -0
  54. data/lib/mb/berkshelf.rb +50 -0
  55. data/lib/mb/bootstrap.rb +9 -0
  56. data/lib/mb/bootstrap/manager.rb +250 -0
  57. data/lib/mb/bootstrap/manifest.rb +131 -0
  58. data/lib/mb/bootstrap/routine.rb +199 -0
  59. data/lib/mb/bootstrap/template.rb +73 -0
  60. data/lib/mb/bootstrap/worker.rb +227 -0
  61. data/lib/mb/chef.rb +6 -0
  62. data/lib/mb/chef/config.rb +69 -0
  63. data/lib/mb/chef/run_list_item.rb +115 -0
  64. data/lib/mb/chef_mutex.rb +304 -0
  65. data/lib/mb/clean_room_base.rb +39 -0
  66. data/lib/mb/cli.rb +50 -0
  67. data/lib/mb/cli/base.rb +51 -0
  68. data/lib/mb/cli/shell.rb +29 -0
  69. data/lib/mb/cli/shell/basic.rb +11 -0
  70. data/lib/mb/cli/shell/color.rb +11 -0
  71. data/lib/mb/cli/shell/ext.rb +41 -0
  72. data/lib/mb/cli/sub_command.rb +95 -0
  73. data/lib/mb/cli/sub_command/component.rb +56 -0
  74. data/lib/mb/cli/sub_command/plugin.rb +232 -0
  75. data/lib/mb/cli_client.rb +178 -0
  76. data/lib/mb/cli_gateway.rb +426 -0
  77. data/lib/mb/cli_gateway/sub_commands.rb +3 -0
  78. data/lib/mb/cli_gateway/sub_commands/environment.rb +124 -0
  79. data/lib/mb/cli_gateway/sub_commands/plugin.rb +148 -0
  80. data/lib/mb/command.rb +88 -0
  81. data/lib/mb/command_invoker.rb +235 -0
  82. data/lib/mb/command_invoker/worker.rb +40 -0
  83. data/lib/mb/command_runner.rb +233 -0
  84. data/lib/mb/component.rb +245 -0
  85. data/lib/mb/config.rb +275 -0
  86. data/lib/mb/config_manager.rb +75 -0
  87. data/lib/mb/console.rb +35 -0
  88. data/lib/mb/cookbook_metadata.rb +73 -0
  89. data/lib/mb/core_ext.rb +3 -0
  90. data/lib/mb/core_ext/dir.rb +37 -0
  91. data/lib/mb/core_ext/enumerable.rb +48 -0
  92. data/lib/mb/core_ext/file.rb +24 -0
  93. data/lib/mb/core_ext/signal.rb +11 -0
  94. data/lib/mb/environment_manager.rb +195 -0
  95. data/lib/mb/error_handler.rb +212 -0
  96. data/lib/mb/errors.rb +693 -0
  97. data/lib/mb/file_system.rb +60 -0
  98. data/lib/mb/file_system/tempfile.rb +25 -0
  99. data/lib/mb/gear.rb +154 -0
  100. data/lib/mb/gears.rb +8 -0
  101. data/lib/mb/gears/dynamic_service.rb +218 -0
  102. data/lib/mb/gears/jmx.rb +24 -0
  103. data/lib/mb/gears/jmx/action.rb +46 -0
  104. data/lib/mb/gears/mysql.rb +20 -0
  105. data/lib/mb/gears/mysql/action.rb +190 -0
  106. data/lib/mb/gears/service.rb +163 -0
  107. data/lib/mb/gears/service/action.rb +58 -0
  108. data/lib/mb/gears/service/action_runner.rb +161 -0
  109. data/lib/mb/grape_ext.rb +3 -0
  110. data/lib/mb/grape_ext/endpoint.rb +13 -0
  111. data/lib/mb/group.rb +143 -0
  112. data/lib/mb/job.rb +183 -0
  113. data/lib/mb/job/state_machine.rb +34 -0
  114. data/lib/mb/job/states.rb +46 -0
  115. data/lib/mb/job_manager.rb +96 -0
  116. data/lib/mb/job_record.rb +67 -0
  117. data/lib/mb/job_ticket.rb +25 -0
  118. data/lib/mb/lock_manager.rb +116 -0
  119. data/lib/mb/logging.rb +134 -0
  120. data/lib/mb/logging/basic_format.rb +31 -0
  121. data/lib/mb/manifest.rb +128 -0
  122. data/lib/mb/mixin.rb +3 -0
  123. data/lib/mb/mixin/attribute_setting.rb +265 -0
  124. data/lib/mb/mixin/coded_exit.rb +49 -0
  125. data/lib/mb/mixin/locks.rb +54 -0
  126. data/lib/mb/mixin/services.rb +100 -0
  127. data/lib/mb/node_filter.rb +97 -0
  128. data/lib/mb/node_querier.rb +527 -0
  129. data/lib/mb/plugin.rb +300 -0
  130. data/lib/mb/plugin_manager.rb +589 -0
  131. data/lib/mb/provisioner.rb +186 -0
  132. data/lib/mb/provisioner/manager.rb +213 -0
  133. data/lib/mb/provisioner/manifest.rb +125 -0
  134. data/lib/mb/provisioner/provision_data.rb +96 -0
  135. data/lib/mb/provisioners.rb +5 -0
  136. data/lib/mb/provisioners/aws.rb +395 -0
  137. data/lib/mb/rest_gateway.rb +72 -0
  138. data/lib/mb/ridley_ext.rb +5 -0
  139. data/lib/mb/ridley_ext/cookbook_object.rb +15 -0
  140. data/lib/mb/srv_ctl.rb +183 -0
  141. data/lib/mb/test.rb +104 -0
  142. data/lib/mb/thor_ext.rb +49 -0
  143. data/lib/mb/upgrade.rb +6 -0
  144. data/lib/mb/upgrade/manager.rb +85 -0
  145. data/lib/mb/upgrade/worker.rb +149 -0
  146. data/lib/mb/version.rb +1 -1
  147. data/lib/motherbrain.rb +166 -2
  148. data/man/man_helper.rb +81 -0
  149. data/man/mb.1 +494 -0
  150. data/man/mb.1.html +300 -0
  151. data/man/mb.1.ronn.erb +62 -0
  152. data/motherbrain.gemspec +56 -20
  153. data/scripts/node_name.rb +14 -0
  154. data/spec/fixtures/cb_metadata.json +7 -0
  155. data/spec/fixtures/cb_metadata.rb +14 -0
  156. data/spec/fixtures/fake_id_rsa +27 -0
  157. data/spec/fixtures/fake_key.pem +27 -0
  158. data/spec/fixtures/myface-0.1.0/metadata.rb +14 -0
  159. data/spec/fixtures/myface-0.1.0/motherbrain.rb +0 -0
  160. data/spec/fixtures/test_env.json +15 -0
  161. data/spec/spec_helper.rb +67 -0
  162. data/spec/support/actor_mocking.rb +7 -0
  163. data/spec/support/berkshelf.rb +24 -0
  164. data/spec/support/chef_server.rb +102 -0
  165. data/spec/support/doubles.rb +11 -0
  166. data/spec/support/klass.rb +10 -0
  167. data/spec/support/matchers/each.rb +12 -0
  168. data/spec/support/matchers/error_codes.rb +5 -0
  169. data/spec/support/matchers/exit_codes.rb +57 -0
  170. data/spec/support/matchers/jobs.rb +11 -0
  171. data/spec/support/spec_helpers.rb +145 -0
  172. data/spec/unit/mb/api/application_spec.rb +11 -0
  173. data/spec/unit/mb/api/helpers_spec.rb +5 -0
  174. data/spec/unit/mb/api/v1/config_endpoint_spec.rb +19 -0
  175. data/spec/unit/mb/api/v1/environments_endpoint_spec.rb +71 -0
  176. data/spec/unit/mb/api/v1/jobs_endpoint_spec.rb +24 -0
  177. data/spec/unit/mb/api/v1/plugins_endpoint_spec.rb +298 -0
  178. data/spec/unit/mb/api/v1_spec.rb +37 -0
  179. data/spec/unit/mb/api/validators/sem_ver_spec.rb +5 -0
  180. data/spec/unit/mb/application_spec.rb +19 -0
  181. data/spec/unit/mb/berkshelf_spec.rb +38 -0
  182. data/spec/unit/mb/bootstrap/manager_spec.rb +347 -0
  183. data/spec/unit/mb/bootstrap/manifest_spec.rb +333 -0
  184. data/spec/unit/mb/bootstrap/routine_spec.rb +393 -0
  185. data/spec/unit/mb/bootstrap/template_spec.rb +100 -0
  186. data/spec/unit/mb/bootstrap/worker_spec.rb +194 -0
  187. data/spec/unit/mb/chef/config_spec.rb +33 -0
  188. data/spec/unit/mb/chef/run_list_item_spec.rb +34 -0
  189. data/spec/unit/mb/chef_mutex_spec.rb +314 -0
  190. data/spec/unit/mb/clean_room_base_spec.rb +31 -0
  191. data/spec/unit/mb/cli/base_spec.rb +43 -0
  192. data/spec/unit/mb/cli/shell/basic_spec.rb +5 -0
  193. data/spec/unit/mb/cli/shell/color_spec.rb +5 -0
  194. data/spec/unit/mb/cli/shell/ext_spec.rb +11 -0
  195. data/spec/unit/mb/cli/shell_spec.rb +38 -0
  196. data/spec/unit/mb/cli/sub_command/base_spec.rb +102 -0
  197. data/spec/unit/mb/cli/sub_command/component_spec.rb +5 -0
  198. data/spec/unit/mb/cli/sub_command/plugin_spec.rb +91 -0
  199. data/spec/unit/mb/cli/sub_command_spec.rb +43 -0
  200. data/spec/unit/mb/cli/ui.rb +0 -0
  201. data/spec/unit/mb/cli_client_spec.rb +51 -0
  202. data/spec/unit/mb/cli_gateway_spec.rb +386 -0
  203. data/spec/unit/mb/command_invoker/worker_spec.rb +43 -0
  204. data/spec/unit/mb/command_invoker_spec.rb +230 -0
  205. data/spec/unit/mb/command_runner_spec.rb +299 -0
  206. data/spec/unit/mb/command_spec.rb +76 -0
  207. data/spec/unit/mb/component_spec.rb +185 -0
  208. data/spec/unit/mb/config_manager_spec.rb +31 -0
  209. data/spec/unit/mb/config_spec.rb +408 -0
  210. data/spec/unit/mb/cookbook_metadata_spec.rb +89 -0
  211. data/spec/unit/mb/core_ext/dir_spec.rb +92 -0
  212. data/spec/unit/mb/core_ext/enumerable_spec.rb +104 -0
  213. data/spec/unit/mb/core_ext/file_spec.rb +58 -0
  214. data/spec/unit/mb/core_ext/signal_spec.rb +24 -0
  215. data/spec/unit/mb/environment_manager_spec.rb +166 -0
  216. data/spec/unit/mb/error_handler_spec.rb +173 -0
  217. data/spec/unit/mb/errors_spec.rb +132 -0
  218. data/spec/unit/mb/file_system/tempfile_spec.rb +14 -0
  219. data/spec/unit/mb/file_system_spec.rb +69 -0
  220. data/spec/unit/mb/gear_spec.rb +125 -0
  221. data/spec/unit/mb/gears/dynamic_service_spec.rb +187 -0
  222. data/spec/unit/mb/gears/jmx/action_spec.rb +34 -0
  223. data/spec/unit/mb/gears/jmx_spec.rb +32 -0
  224. data/spec/unit/mb/gears/mysql/action_spec.rb +118 -0
  225. data/spec/unit/mb/gears/mysql_spec.rb +21 -0
  226. data/spec/unit/mb/gears/service/action_runner_spec.rb +182 -0
  227. data/spec/unit/mb/gears/service/action_spec.rb +44 -0
  228. data/spec/unit/mb/gears/service_spec.rb +124 -0
  229. data/spec/unit/mb/group_spec.rb +280 -0
  230. data/spec/unit/mb/job_manager_spec.rb +56 -0
  231. data/spec/unit/mb/job_record_spec.rb +60 -0
  232. data/spec/unit/mb/job_spec.rb +201 -0
  233. data/spec/unit/mb/locks_manager_spec.rb +88 -0
  234. data/spec/unit/mb/logging_spec.rb +133 -0
  235. data/spec/unit/mb/manifest_spec.rb +105 -0
  236. data/spec/unit/mb/mixin/attribute_setting_spec.rb +180 -0
  237. data/spec/unit/mb/mixin/coded_exit_spec.rb +25 -0
  238. data/spec/unit/mb/mixin/locks_spec.rb +32 -0
  239. data/spec/unit/mb/mixin/services_spec.rb +75 -0
  240. data/spec/unit/mb/node_filter_spec.rb +86 -0
  241. data/spec/unit/mb/node_querier_spec.rb +532 -0
  242. data/spec/unit/mb/plugin_manager_spec.rb +724 -0
  243. data/spec/unit/mb/plugin_spec.rb +247 -0
  244. data/spec/unit/mb/provisioner/manager_spec.rb +141 -0
  245. data/spec/unit/mb/provisioner/manifest_spec.rb +182 -0
  246. data/spec/unit/mb/provisioner/provision_data_spec.rb +113 -0
  247. data/spec/unit/mb/provisioner_spec.rb +251 -0
  248. data/spec/unit/mb/provisioners/aws_spec.rb +392 -0
  249. data/spec/unit/mb/provisioners/environment_factory_spec.rb +108 -0
  250. data/spec/unit/mb/rest_gateway_spec.rb +13 -0
  251. data/spec/unit/mb/ridley_ext/cookbook_object_spec.rb +105 -0
  252. data/spec/unit/mb/srv_ctl_spec.rb +142 -0
  253. data/spec/unit/mb/upgrade/manager_spec.rb +37 -0
  254. data/spec/unit/mb/upgrade/worker_spec.rb +219 -0
  255. data/spec/unit/motherbrain_spec.rb +58 -0
  256. data/templates/bootstrap.json +8 -0
  257. data/templates/motherbrain.rb +44 -0
  258. metadata +694 -15
  259. data/Rakefile +0 -1
@@ -0,0 +1,724 @@
1
+ require 'spec_helper'
2
+
3
+ describe MotherBrain::PluginManager do
4
+ describe "ClassMethods" do
5
+ subject { described_class }
6
+
7
+ describe "::new" do
8
+ context "when 'remote_loading' is disabled" do
9
+ before(:each) do
10
+ described_class.any_instance.stub(eager_loading?: false)
11
+ end
12
+
13
+ it "has a nil value for eager_load_timer" do
14
+ subject.new.eager_load_timer.should be_nil
15
+ end
16
+ end
17
+
18
+ context "when 'eager_loading' is enabled" do
19
+ before(:each) do
20
+ described_class.any_instance.stub(eager_loading?: true )
21
+ end
22
+
23
+ it "sets a Timer for remote_load_timer" do
24
+ subject.any_instance.should_receive(:load_all_remote)
25
+
26
+ subject.new.eager_load_timer.should be_a(Timers::Timer)
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ subject { described_class.new }
33
+
34
+ describe "#install" do
35
+ let(:plugin) { double(name: "rspec", version: "1.2.3") }
36
+ let(:plugin_install_path) { subject.install_path_for(plugin) }
37
+
38
+ before { subject.stub_chain(:chef_connection, :cookbook, :download) }
39
+
40
+ context "when the remote contains the plugin and it is not installed" do
41
+ before do
42
+ subject.stub(:find).with(plugin.name, plugin.version, remote: true).and_return(plugin)
43
+ subject.stub(:find).with(plugin.name, plugin.version, remote: false).and_return(nil)
44
+ end
45
+
46
+ it "searches for the plugin of the given name/version on the remote" do
47
+ subject.should_receive(:find).with(plugin.name, plugin.version, remote: true).and_return(plugin)
48
+
49
+ subject.install(plugin.name, plugin.version)
50
+ end
51
+
52
+ it "returns the found plugin" do
53
+ expect(subject.install(plugin.name, plugin.version)).to eq(plugin)
54
+ end
55
+
56
+ it "downloads the cookbook containing the plugin to the Berkshelf" do
57
+ cookbook_resource = double
58
+ subject.stub_chain(:chef_connection, :cookbook).and_return(cookbook_resource)
59
+ cookbook_resource.should_receive(:download).with(plugin.name, plugin.version, plugin_install_path)
60
+
61
+ subject.install(plugin.name, plugin.version)
62
+ end
63
+
64
+ it "adds the plugin to the list of plugins" do
65
+ subject.install(plugin.name, plugin.version)
66
+
67
+ expect(subject.list).to include(plugin)
68
+ end
69
+ end
70
+
71
+ context "when the remote does not have a plugin of the given name/version" do
72
+ before { subject.should_receive(:find).with(plugin.name, plugin.version, remote: true).and_return(nil) }
73
+
74
+ it "raises a PluginNotFound error" do
75
+ expect { subject.install(plugin.name, plugin.version) }.to raise_error(MB::PluginNotFound)
76
+ end
77
+ end
78
+ end
79
+
80
+ describe "#install_path_for" do
81
+ let(:plugin) { double(name: "rspec", version: "1.2.3") }
82
+
83
+ it "returns a Pathname" do
84
+ expect(subject.install_path_for(plugin)).to be_a(Pathname)
85
+ end
86
+ end
87
+
88
+ describe "#uninstall" do
89
+ let(:plugin) { double(name: "rpsec", version: "1.2.3") }
90
+ let(:plugin_install_path) { subject.install_path_for(plugin) }
91
+
92
+ before do
93
+ subject.add(plugin)
94
+ FileUtils.mkdir_p(plugin_install_path)
95
+ end
96
+
97
+ it "returns the uninstalled plugin" do
98
+ expect(subject.uninstall(plugin.name, plugin.version)).to eql(plugin)
99
+ end
100
+
101
+ it "removes the plugin from the plugins list" do
102
+ subject.uninstall(plugin.name, plugin.version)
103
+
104
+ expect(subject.list).to_not include(plugin)
105
+ end
106
+
107
+ it "removes the plugin and it's cookbook from disk" do
108
+ subject.uninstall(plugin.name, plugin.version)
109
+
110
+ expect(plugin_install_path).to_not exist
111
+ end
112
+
113
+ context "when the plugin of the given name/version is not installed" do
114
+ before { subject.stub(:find).with(plugin.name, plugin.version, remote: false).and_return(nil) }
115
+
116
+ it "returns nil" do
117
+ expect(subject.uninstall(plugin.name, plugin.version)).to be_nil
118
+ end
119
+ end
120
+ end
121
+
122
+ describe "#latest" do
123
+ let(:name) { "apple" }
124
+ let(:version) { "2.0.0" }
125
+
126
+ let(:plugin) do
127
+ double('plugin', name: name, version: version)
128
+ end
129
+
130
+ before(:each) do
131
+ subject.stub(list: [plugin])
132
+ end
133
+
134
+ it "searches the latest version of the plugin matching the given name" do
135
+ subject.should_receive(:find).with(name, version, remote: false).and_return(plugin)
136
+ subject.latest(name).should eql(plugin)
137
+ end
138
+
139
+ context "when no suitable plugin can be found" do
140
+ before(:each) do
141
+ subject.stub(list: [])
142
+ end
143
+
144
+ it "returns nil" do
145
+ subject.latest(name).should be_nil
146
+ end
147
+ end
148
+ end
149
+
150
+ describe "#load_all" do
151
+ let(:count) { 3 }
152
+
153
+ before(:each) do
154
+ subject.clear_plugins
155
+ paths = Array.new
156
+
157
+ count.times do
158
+ paths << generate_cookbook(SecureRandom.hex(16), with_plugin: true)
159
+ end
160
+
161
+ MB::Berkshelf.stub(:cookbooks).and_return(paths)
162
+ end
163
+
164
+ it "sends a load message to self with each plugin found in the berkshelf" do
165
+ subject.should_receive(:load_installed).with(anything, force: false).exactly(count).times
166
+
167
+ subject.load_all
168
+ end
169
+
170
+ it "has a plugin for each plugin in the paths" do
171
+ subject.load_all
172
+
173
+ subject.list.should have(count).items
174
+ subject.list.should each be_a(MB::Plugin)
175
+ end
176
+
177
+ context "when 'remote_loading' is enabled" do
178
+ before(:each) do
179
+ subject.stub(eager_loading?: true)
180
+ end
181
+
182
+ it "calls #load_all_remote" do
183
+ subject.should_receive(:load_all_remote)
184
+ subject.load_all
185
+ end
186
+ end
187
+ end
188
+
189
+ describe "#load_all_installed" do
190
+ before do
191
+ install_cookbook("ruby", "1.2.3", with_plugin: true)
192
+ install_cookbook("ruby", "2.0.0", with_plugin: true)
193
+ install_cookbook("elixir", "1.3.4", with_plugin: false)
194
+ end
195
+
196
+ it "loads each plugin found in the Berkshelf" do
197
+ subject.load_all_installed
198
+ expect(subject.list).to have(2).items
199
+ expect(subject).to have_plugin("ruby", "1.2.3")
200
+ expect(subject).to have_plugin("ruby", "2.0.0")
201
+ end
202
+ end
203
+
204
+ describe "#load_all_remote" do
205
+ before do
206
+ described_class.any_instance.stub(async_loading?: false)
207
+ chef_cookbook("ruby", "1.2.3", with_plugin: true)
208
+ chef_cookbook("ruby", "2.0.0", with_plugin: false)
209
+ chef_cookbook("elixir", "1.3.4", with_plugin: true)
210
+ end
211
+
212
+ it "loads each plugin found on the Chef Server" do
213
+ subject.load_all_remote
214
+ expect(subject.list).to have(2).items
215
+ expect(subject).to have_plugin("ruby", "1.2.3")
216
+ expect(subject).to have_plugin("elixir", "1.3.4")
217
+ end
218
+
219
+ context "given a value for :name" do
220
+ it "only loads each plugin found on the Chef Server matching the given name" do
221
+ subject.load_all_remote(name: "ruby")
222
+ expect(subject.list).to have(1).item
223
+ expect(subject).to have_plugin("ruby", "1.2.3")
224
+ end
225
+ end
226
+ end
227
+
228
+ describe "#load_installed" do
229
+ let(:plugin) do
230
+ metadata = MB::CookbookMetadata.new do
231
+ name 'apple'
232
+ version '1.0.0'
233
+ end
234
+ MB::Plugin.new(metadata)
235
+ end
236
+
237
+ let(:path) { '/tmp/one/apple-1.0.0' }
238
+
239
+ before(:each) do
240
+ MB::Plugin.stub(:from_path).with(path).and_return(plugin)
241
+ end
242
+
243
+ it "adds an instantiated plugin to the hash of plugins" do
244
+ subject.load_installed(path)
245
+
246
+ subject.list.should include(plugin)
247
+ end
248
+ end
249
+
250
+ describe "#load_remote" do
251
+ let(:name) { "nginx" }
252
+ let(:version) { "1.2.0" }
253
+ let(:resource) do
254
+ Ridley::CookbookObject.new(double('client'))
255
+ end
256
+
257
+ before(:each) do
258
+ subject.ridley.stub_chain(:cookbook, :find).and_return(resource)
259
+ end
260
+
261
+ context "when the cookbook doesn't contain a motherbrain plugin" do
262
+ before(:each) { resource.stub(has_motherbrain_plugin?: false) }
263
+
264
+ it "returns nil if resource doesn't contain a motherbrain plugin" do
265
+ subject.load_remote(name, version).should be_nil
266
+ end
267
+ end
268
+
269
+ context "when the cookbook contains a motherbrain plugin" do
270
+ before(:each) { resource.stub(has_motherbrain_plugin?: true) }
271
+ let(:temp_dir) { MB::FileSystem.tmpdir }
272
+
273
+ context "and the files are transferred successfully" do
274
+ before(:each) do
275
+ File.write(File.join(temp_dir, MB::Plugin::PLUGIN_FILENAME), "# blank plugin")
276
+
277
+ MB::FileSystem.stub(tmpdir: temp_dir)
278
+
279
+ resource.stub(:download_file).and_return(true)
280
+
281
+ json_metadata = File.read(fixtures_path.join('cb_metadata.json'))
282
+ resource.stub_chain(:metadata, :to_json).and_return(json_metadata)
283
+ end
284
+
285
+ it "adds the plugin to the set of plugins" do
286
+ subject.load_remote(name, version)
287
+
288
+ subject.list.should have(1).item
289
+ end
290
+
291
+ it "cleans up the generated temporary files" do
292
+ subject.load_remote(name, version)
293
+
294
+ File.exist?(temp_dir).should be_false
295
+ end
296
+ end
297
+
298
+ context "when the plugin is not downloaded successfully" do
299
+ before(:each) { resource.stub(:download_file).and_return(nil) }
300
+
301
+ it "returns nil" do
302
+ subject.load_remote(name, version).should be_nil
303
+ end
304
+ end
305
+ end
306
+
307
+ context "when the remote does not have a cookbook of the given name/version" do
308
+ before(:each) { subject.ridley.stub_chain(:cookbook, :find).and_return(nil) }
309
+
310
+ it "returns nil" do
311
+ subject.load_remote(name, version).should be_nil
312
+ end
313
+ end
314
+ end
315
+
316
+ describe "#add" do
317
+ let(:plugin) do
318
+ metadata = MB::CookbookMetadata.new do
319
+ name 'apple'
320
+ version '1.0.0'
321
+ end
322
+ MB::Plugin.new(metadata)
323
+ end
324
+
325
+ it "returns the added plugin" do
326
+ subject.add(plugin).should eql(plugin)
327
+ end
328
+
329
+ it "adds the plugin to the Set of plugins" do
330
+ subject.add(plugin)
331
+
332
+ subject.list.should include(plugin)
333
+ end
334
+
335
+ context "when the plugin is already added" do
336
+ it "returns nil" do
337
+ subject.add(plugin)
338
+
339
+ subject.add(plugin).should be_nil
340
+ end
341
+
342
+ context "when given 'true' for the ':force' option" do
343
+ it "adds the plugin anyway" do
344
+ subject.add(plugin)
345
+
346
+ subject.add(plugin, force: true).should eql(plugin)
347
+ end
348
+
349
+ it "doesn't add a duplicate plugin" do
350
+ subject.add(plugin)
351
+ subject.add(plugin)
352
+
353
+ subject.list.should have(1).item
354
+ end
355
+ end
356
+ end
357
+ end
358
+
359
+ describe "#async_loading?" do
360
+ context "if the plugin manager is configured for async loading" do
361
+ before(:each) do
362
+ MB::Application.config.plugin_manager.stub(async_loading: true)
363
+ end
364
+
365
+ it "returns true" do
366
+ subject.async_loading?.should be_true
367
+ end
368
+ end
369
+
370
+ context "if the plugin manager is not configured for async loading" do
371
+ before(:each) do
372
+ MB::Application.config.plugin_manager.stub(async_loading: false)
373
+ end
374
+
375
+ it "returns false" do
376
+ subject.async_loading?.should be_false
377
+ end
378
+ end
379
+ end
380
+
381
+ describe "#find" do
382
+ let(:one) do
383
+ metadata = MB::CookbookMetadata.new do
384
+ name 'apple'
385
+ version '1.0.0'
386
+ end
387
+ MB::Plugin.new(metadata)
388
+ end
389
+ let(:two) do
390
+ metadata = MB::CookbookMetadata.new do
391
+ name 'apple'
392
+ version '2.0.0'
393
+ end
394
+ MB::Plugin.new(metadata)
395
+ end
396
+ let(:three) do
397
+ metadata = MB::CookbookMetadata.new do
398
+ name 'orange'
399
+ version '2.0.0'
400
+ end
401
+ MB::Plugin.new(metadata)
402
+ end
403
+
404
+ before(:each) do
405
+ subject.add(one)
406
+ subject.add(two)
407
+ subject.add(three)
408
+ end
409
+
410
+ it "returns the plugin of the given name and version" do
411
+ subject.find(one.name, one.version).should eql(one)
412
+ end
413
+
414
+ it "returns nil if the plugin of a given name and version is not found" do
415
+ subject.find("glade", "3.2.4").should be_nil
416
+ end
417
+
418
+ it "returns the latest if a version is not passed" do
419
+ subject.find("apple").should eq(two)
420
+ end
421
+ end
422
+
423
+ describe "#for_environment" do
424
+ let(:plugin_id) { "rspec-test" }
425
+ let(:environment_id) { "rspec-testenv" }
426
+ let(:environment) do
427
+ double('environment',
428
+ name: environment_id,
429
+ cookbook_versions: {
430
+ plugin_id => ">= 1.2.3"
431
+ }
432
+ )
433
+ end
434
+ let(:options) do
435
+ {
436
+ remote: true
437
+ }
438
+ end
439
+
440
+ context "when the environment exists" do
441
+ before(:each) do
442
+ environment_manager.should_receive(:find).with(environment_id).and_return(environment)
443
+ end
444
+
445
+ it "attempts to satisfy the environment's plugin (cookbook) constraint" do
446
+ subject.should_receive(:satisfy).with(plugin_id, ">= 1.2.3", options)
447
+ subject.for_environment(plugin_id, environment_id, options)
448
+ end
449
+ end
450
+
451
+ context "when the environment exists but does not have a lock" do
452
+ before do
453
+ environment.stub(cookbook_versions: Hash.new)
454
+ environment_manager.should_receive(:find).with(environment_id).and_return(environment)
455
+ end
456
+
457
+ it "satisfies the environment using a wildcard constraint (>= 0.0.0)" do
458
+ subject.should_receive(:satisfy).with(plugin_id, ">= 0.0.0", options)
459
+ subject.for_environment(plugin_id, environment_id, options)
460
+ end
461
+ end
462
+
463
+ context "when the environment does not exist" do
464
+ before(:each) do
465
+ environment_manager.should_receive(:find).with(environment_id).
466
+ and_raise(MB::EnvironmentNotFound.new(environment_id))
467
+ end
468
+
469
+ it "raises an EnvironmentNotFound error" do
470
+ expect {
471
+ subject.for_environment(plugin_id, environment_id)
472
+ }.to raise_error(MB::EnvironmentNotFound)
473
+ end
474
+ end
475
+ end
476
+
477
+ describe "#for_run_list_entry" do
478
+ let(:cookbook_name) { "foobook" }
479
+ let(:recipe_name) { "server" }
480
+ let(:version) { "1.1.2" }
481
+ let(:environment) { "foo" }
482
+
483
+ let(:run_list_entry) { "recipe[#{cookbook_name}::#{recipe_name}]" }
484
+ let(:run_list_entry_with_version) { run_list_entry.sub(/\]/, "@#{version}]") }
485
+
486
+ describe "with run list version" do
487
+ it "should use the run list version definition" do
488
+ subject.should_receive(:find).with(cookbook_name, version, {})
489
+ subject.for_run_list_entry(run_list_entry_with_version)
490
+ end
491
+
492
+ it "should use the run list version even if an environment is provided" do
493
+ subject.should_receive(:find).with(cookbook_name, version, {})
494
+ subject.for_run_list_entry(run_list_entry_with_version, environment)
495
+ end
496
+ end
497
+
498
+ describe "with environment" do
499
+ it "should use the environment version lock" do
500
+ subject.should_receive(:for_environment).with(cookbook_name, environment, {})
501
+ subject.for_run_list_entry(run_list_entry, environment)
502
+ end
503
+ end
504
+
505
+ it "should find the plugin" do
506
+ subject.should_receive(:find).with(cookbook_name, nil, {})
507
+ subject.for_run_list_entry(run_list_entry)
508
+ end
509
+ end
510
+
511
+ describe "#clear_plugins" do
512
+ let(:plugin) do
513
+ metadata = MB::CookbookMetadata.new do
514
+ name 'apple'
515
+ version '1.0.0'
516
+ end
517
+ MB::Plugin.new(metadata)
518
+ end
519
+
520
+ it "clears any loaded plugins" do
521
+ subject.add(plugin)
522
+ subject.clear_plugins
523
+
524
+ subject.list.should be_empty
525
+ end
526
+ end
527
+
528
+ describe "#list" do
529
+ it "returns an Array of plugins" do
530
+ result = subject.list
531
+
532
+ result.should be_a(Array)
533
+ result.should each be_a(MB::Plugin)
534
+ end
535
+
536
+ context "given 'true' for the :remote option" do
537
+ it "loads the remote plugins before returning" do
538
+ subject.should_receive(:load_all_remote)
539
+
540
+ subject.list(remote: true)
541
+ end
542
+ end
543
+ end
544
+
545
+ describe "#satisfy" do
546
+ let(:plugin_id) { "rspec-test" }
547
+ let(:versions) do
548
+ [
549
+ "1.0.0",
550
+ "1.2.3",
551
+ "1.3.0"
552
+ ]
553
+ end
554
+ let(:options) do
555
+ {
556
+ remote: false
557
+ }
558
+ end
559
+
560
+ let(:constraint) { ">= 1.2.3" }
561
+ let(:plugin) { double('plugin', name: plugin_id, version: '1.3.0') }
562
+
563
+ context "when given a non-wildcard, non-equality constraint" do
564
+ before(:each) do
565
+ subject.should_receive(:versions).with(plugin_id, options[:remote]).and_return(versions)
566
+ end
567
+
568
+ it "returns the best plugin for the given constraint" do
569
+ subject.should_receive(:find).with(plugin_id, "1.3.0", remote: false).and_return(plugin)
570
+ subject.satisfy(plugin_id, constraint, options).should eql(plugin)
571
+ end
572
+ end
573
+
574
+ context "when given a constraint containing an eqluality operator" do
575
+ let(:constraint) { "= 1.0.0" }
576
+
577
+ it "does not attempt to get a list of all versions" do
578
+ subject.should_not_receive(:versions)
579
+
580
+ subject.satisfy(plugin_id, constraint, options)
581
+ end
582
+
583
+ context "when the :remote option is set to true" do
584
+ before { options[:remote] = true }
585
+
586
+ it "attempts to eagerly load a plugin of the same name/version from the remote" do
587
+ subject.should_receive(:load_remote).with(plugin_id, "1.0.0")
588
+
589
+ subject.satisfy(plugin_id, constraint, options)
590
+ end
591
+ end
592
+ end
593
+
594
+ context "when given a wild card constraint (>= 0.0.0)" do
595
+ let(:constraint) { ">= 0.0.0" }
596
+
597
+ it "returns the latest plugin" do
598
+ subject.should_receive(:latest).with(plugin_id, options).and_return(plugin)
599
+
600
+ subject.satisfy(plugin_id, constraint, options).should eql(plugin)
601
+ end
602
+ end
603
+ end
604
+
605
+ describe "#installed_versions" do
606
+ before { MB::Berkshelf.stub(cookbooks_path: fixtures_path) }
607
+
608
+ context "when the installed cookbooks have at least one version containing a plugin" do
609
+ it "returns an array containing a string for each" do
610
+ versions = subject.installed_versions("myface")
611
+ versions.should have(1).item
612
+ versions.should each be_a(String)
613
+ end
614
+ end
615
+
616
+ context "when the installed cookbooks does not have a version containing a plugin" do
617
+ it "returns an empty array" do
618
+ subject.installed_versions("nginx").should be_empty
619
+ end
620
+ end
621
+ end
622
+
623
+ describe "#remote_versions" do
624
+ let(:name) { "nginx" }
625
+ let(:chef_connection) { double('chef-connection') }
626
+
627
+ let(:versions) do
628
+ [
629
+ "1.0.0",
630
+ "2.0.0"
631
+ ]
632
+ end
633
+
634
+ before do
635
+ subject.stub(chef_connection: chef_connection)
636
+ chef_connection.stub_chain(:cookbook, :versions).with(name).and_return(versions)
637
+ end
638
+
639
+ it "attempts to load a plugin for every version of the cookbook present on the chef server" do
640
+ versions.each do |version|
641
+ subject.should_receive(:load_remote).with(name, version).and_return(nil)
642
+ end
643
+
644
+ subject.remote_versions(name)
645
+ end
646
+
647
+ context "when the cookbook versions on the remote contain a plugin" do
648
+ before do
649
+ versions.each do |version|
650
+ subject.should_receive(:load_remote).with(name, version).and_return(double(version: version))
651
+ end
652
+ end
653
+
654
+ it "returns an array of strings including the versions" do
655
+ subject.remote_versions(name).should include(*versions)
656
+ end
657
+ end
658
+
659
+ context "when the cookbook versions on the remote do not contain a plugin" do
660
+ before do
661
+ versions.each do |version|
662
+ subject.should_receive(:load_remote).with(name, version).and_return(nil)
663
+ end
664
+ end
665
+
666
+ it "returns an empty array of strings" do
667
+ subject.remote_versions(name).should be_empty
668
+ end
669
+ end
670
+
671
+ context "when the remote chef server does not contain any cookbooks of the given name" do
672
+ let(:versions) { Array.new }
673
+
674
+ before do
675
+ chef_connection.stub_chain(:cookbook, :versions).with(name).and_return(versions)
676
+ end
677
+
678
+ it "returns an empty array" do
679
+ subject.remote_versions(name).should be_empty
680
+ end
681
+ end
682
+ end
683
+
684
+ describe "#versions" do
685
+ let(:name) { "nginx" }
686
+ let(:installed_versions) { [ "1.2.3", "2.0.0" ] }
687
+ let(:remote_versions) { [ "3.0.0" ] }
688
+
689
+ context "when given 'false' for the remote argument" do
690
+ before do
691
+ subject.should_not_receive(:remote_versions)
692
+ subject.should_receive(:installed_versions).with(name).and_return(installed_versions)
693
+ end
694
+
695
+ it "returns only the installed plugins" do
696
+ expect(subject.versions(name, false)).to eql(installed_versions)
697
+ end
698
+ end
699
+
700
+ context "when given 'true' for the remote argument" do
701
+ before do
702
+ subject.should_receive(:remote_versions).with(name).and_return(remote_versions)
703
+ subject.should_receive(:installed_versions).with(name).and_return(installed_versions)
704
+ end
705
+
706
+ it "includes the remote versions" do
707
+ expect(subject.versions(name, true)).to include(*remote_versions)
708
+ end
709
+
710
+ it "includes the installed versions" do
711
+ expect(subject.versions(name, true)).to include(*installed_versions)
712
+ end
713
+
714
+ context "when no plugins are found" do
715
+ let(:remote_versions) { Array.new }
716
+ let(:installed_versions) { Array.new }
717
+
718
+ it "raises a PluginNotFound error" do
719
+ expect { subject.versions(name, true) }.to raise_error(MB::PluginNotFound)
720
+ end
721
+ end
722
+ end
723
+ end
724
+ end