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,247 @@
1
+ require 'spec_helper'
2
+
3
+ describe MB::Plugin do
4
+ let(:metadata) do
5
+ MB::CookbookMetadata.new do
6
+ name "motherbrain"
7
+ maintainer "Jamie Winsor"
8
+ maintainer_email "jamie@vialstudios.com"
9
+ license "Apache 2.0"
10
+ description "Installs/Configures motherbrain"
11
+ long_description "Installs/Configures motherbrain"
12
+ version "0.1.0"
13
+ end
14
+ end
15
+
16
+ describe "ClassMethods" do
17
+ subject { MB::Plugin }
18
+
19
+ describe "::load" do
20
+ subject(:plugin) do
21
+ described_class.load(metadata) do
22
+ # blank plugin
23
+ end
24
+ end
25
+
26
+ its(:name) { should eql('motherbrain') }
27
+ its(:maintainer) { should eql('Jamie Winsor') }
28
+ its(:maintainer_email) { should eql('jamie@vialstudios.com') }
29
+ its(:license) { should eql('Apache 2.0') }
30
+ its(:description) { should eql('Installs/Configures motherbrain') }
31
+ its(:long_description) { should eql('Installs/Configures motherbrain') }
32
+ its(:version) { subject.to_s.should eql('motherbrain (0.1.0)') }
33
+ its(:components) { should be_empty }
34
+ its(:commands) { should be_empty }
35
+ its(:bootstrap_routine) { should be_nil }
36
+
37
+ context "when a stack_order block is present" do
38
+ subject do
39
+ described_class.load(metadata) do
40
+ stack_order do
41
+ # empty routine
42
+ end
43
+ end
44
+ end
45
+
46
+ it "has a Bootstrap::Routine for the value of bootstrap_routine" do
47
+ subject.bootstrap_routine.should be_a(MB::Bootstrap::Routine)
48
+ end
49
+ end
50
+
51
+ context "when a command keyword is present" do
52
+ subject do
53
+ described_class.load(metadata) do
54
+ command "start" do
55
+ description "Start all services"
56
+
57
+ execute do
58
+ component("activemq").invoke("start")
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ it { subject.commands.should have(1).item }
65
+ it { subject.command("start").should_not be_nil }
66
+ end
67
+
68
+ context "when a component keyword is present" do
69
+ subject do
70
+ described_class.load(metadata) do
71
+ component "activemq" do
72
+ description "do stuff to AMQ"
73
+ end
74
+ end
75
+ end
76
+
77
+ it { subject.components.should have(1).item }
78
+ it { subject.component("activemq").should_not be_nil }
79
+ end
80
+
81
+ context "when the metadata contains invalid values for attributes" do
82
+ let(:metadata) do
83
+ MB::CookbookMetadata.new do
84
+ name 1
85
+ end
86
+ end
87
+
88
+ it { -> { plugin }.should raise_error MB::InvalidCookbookMetadata }
89
+ end
90
+
91
+ context "when the plugin contains an unknown keyword" do
92
+ subject(:plugin) do
93
+ described_class.load(metadata) do
94
+ unknown_keyword
95
+ end
96
+ end
97
+
98
+ it { -> { plugin }.should raise_error MB::PluginSyntaxError }
99
+ end
100
+ end
101
+
102
+ describe "#from_path" do
103
+ let(:cb_path) { fixtures_path.join('myface-0.1.0') }
104
+
105
+ subject { described_class.from_path(cb_path) }
106
+
107
+ it "returns a MB::Plugin from the given directory" do
108
+ subject.should be_a(MB::Plugin)
109
+ end
110
+ end
111
+ end
112
+
113
+ subject { described_class.new(metadata) }
114
+
115
+ describe "#to_s" do
116
+ it "returns the name and version of the plugin" do
117
+ subject.to_s.should eql("motherbrain (0.1.0)")
118
+ end
119
+ end
120
+
121
+ describe "comparing plugins" do
122
+ let(:one) do
123
+ metadata = MB::CookbookMetadata.new do
124
+ name 'apple'
125
+ version '1.0.0'
126
+ end
127
+ described_class.new(metadata)
128
+ end
129
+ let(:two) do
130
+ metadata = MB::CookbookMetadata.new do
131
+ name 'apple'
132
+ version '2.0.0'
133
+ end
134
+ described_class.new(metadata)
135
+ end
136
+ let(:three) do
137
+ metadata = MB::CookbookMetadata.new do
138
+ name 'cherry'
139
+ version '1.0.0'
140
+ end
141
+ described_class.new(metadata)
142
+ end
143
+ let(:four) do
144
+ metadata = MB::CookbookMetadata.new do
145
+ name 'cherry'
146
+ version '2.0.0'
147
+ end
148
+ described_class.new(metadata)
149
+ end
150
+ let(:five) do
151
+ metadata = MB::CookbookMetadata.new do
152
+ name 'orange'
153
+ version '1.0.0'
154
+ end
155
+ described_class.new(metadata)
156
+ end
157
+ let(:six) do
158
+ metadata = MB::CookbookMetadata.new do
159
+ name 'orange'
160
+ version '2.0.0'
161
+ end
162
+ described_class.new(metadata)
163
+ end
164
+
165
+ let(:list) do
166
+ [
167
+ one,
168
+ two,
169
+ three,
170
+ four,
171
+ five,
172
+ six
173
+ ]
174
+ end
175
+
176
+ it "returns the list in the proper order" do
177
+ list.shuffle.sort.should eql(list)
178
+ end
179
+ end
180
+
181
+ describe "#to_hash" do
182
+ subject do
183
+ described_class.new(metadata).to_hash
184
+ end
185
+
186
+ it "includes a 'name' field and value" do
187
+ subject[:name].should_not be_nil
188
+ end
189
+
190
+ it "includes a 'version' field and value" do
191
+ subject[:version].should_not be_nil
192
+ end
193
+
194
+ it "includes a 'description' field and value" do
195
+ subject[:description].should_not be_nil
196
+ end
197
+
198
+ it "includes a 'long_description' field and value" do
199
+ subject[:long_description].should_not be_nil
200
+ end
201
+
202
+ it "includes a 'maintainer' field and value" do
203
+ subject[:maintainer].should_not be_nil
204
+ end
205
+
206
+ it "includes a 'maintainer_email' field and value" do
207
+ subject[:maintainer_email].should_not be_nil
208
+ end
209
+ end
210
+
211
+ describe "#command" do
212
+ let(:plugin) do
213
+ described_class.new(metadata) do
214
+ command "existing" do
215
+ # block
216
+ end
217
+ end
218
+ end
219
+
220
+ subject { plugin.command(name) }
221
+
222
+ context "when the component has a command matching the given name" do
223
+ let(:name) { "existing" }
224
+
225
+ it { should be_a(MB::Command) }
226
+ it { name.should eql("existing") }
227
+ end
228
+
229
+ context "when the component does not have a command matching the given name" do
230
+ let(:name) { "not-there" }
231
+
232
+ it { should be_nil }
233
+ end
234
+ end
235
+
236
+ describe "#command!" do
237
+ before do
238
+ subject.stub(command: nil)
239
+ end
240
+
241
+ it "raises a CommandNotFound error when no matching command is present" do
242
+ expect {
243
+ subject.command!("stop")
244
+ }.to raise_error(MB::CommandNotFound)
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,141 @@
1
+ require 'spec_helper'
2
+
3
+ describe MB::Provisioner::Manager do
4
+ subject { provisioner_manager }
5
+
6
+ let(:provisioner_manager) { described_class.new }
7
+
8
+ let(:job) { double(MB::Job, alive?: true) }
9
+ let(:environment) { "production" }
10
+ let(:manifest) { double(MB::Manifest, to_hash: { 'a' => 1 }, provisioner: nil, options: nil) }
11
+ let(:plugin) { double(MB::Plugin, name: "MyPlugin") }
12
+
13
+ subject { described_class.new }
14
+
15
+ describe "#async_provision" do
16
+ let(:environment) { double('environment') }
17
+ let(:manifest) { double('manifest') }
18
+ let(:plugin) { double('plugin') }
19
+
20
+ it "delegates asynchronously to {#provision}" do
21
+ subject.should_receive(:async).with(
22
+ :provision,
23
+ kind_of(MB::Job),
24
+ environment,
25
+ manifest,
26
+ plugin,
27
+ anything()
28
+ )
29
+
30
+ subject.async_provision(environment, manifest, plugin)
31
+ end
32
+
33
+ it "returns a JobRecord" do
34
+ subject.async_provision(environment, manifest, plugin).should be_a(MB::JobRecord)
35
+ end
36
+ end
37
+
38
+ describe "#provision" do
39
+ let(:bootstrapper) { double('bootstrapper') }
40
+ let(:default_provisioner) { provisioner_manager.provisioner_registry[MB::Provisioner.default_id] }
41
+ let(:options) { Hash.new }
42
+
43
+ let!(:job) { MB::Job.new(:provision) }
44
+ let!(:ticket) { job.ticket }
45
+
46
+ subject(:provision) do
47
+ provisioner_manager.stub(bootstrapper: bootstrapper)
48
+ provisioner_manager.provision(job, environment, manifest, plugin, options)
49
+ end
50
+
51
+ context "when the manifest defines a provisioner that is started and registered" do
52
+ before { manifest.stub(provisioner: MB::Provisioner.default_id) }
53
+
54
+ it "successfully provisions and bootstraps" do
55
+ response = double('provision-response')
56
+ bootstrap_manifest = double('bootstrap-manifest')
57
+
58
+ manifest.should_receive(:validate!).with(plugin)
59
+ default_provisioner.should_receive(:up).with(job, environment, manifest, plugin, anything).and_return(response)
60
+ MB::Bootstrap::Manifest.should_receive(:from_provisioner).with(response, manifest).
61
+ and_return(bootstrap_manifest)
62
+
63
+ provisioner_manager.should_receive(:write_bootstrap_manifest)
64
+ bootstrapper.should_receive(:bootstrap)
65
+
66
+ provision
67
+ expect(ticket.state).to eql(:success)
68
+ end
69
+ end
70
+
71
+ context "with skip_bootstrap: true" do
72
+ before { options[:skip_bootstrap] = true }
73
+
74
+ it "successfully provisions but skips bootstrap" do
75
+ response = double('provision-response')
76
+
77
+ manifest.should_receive(:validate!).with(plugin)
78
+ default_provisioner.should_receive(:up).with(job, environment, manifest, plugin, anything).and_return(response)
79
+
80
+ bootstrapper.should_not_receive(:bootstrap)
81
+
82
+ provision
83
+ expect(ticket.state).to eql(:success)
84
+ end
85
+ end
86
+ end
87
+
88
+ describe "#write_bootstrap_manifest" do
89
+ subject(:write_bootstrap_manifest) do
90
+ provisioner_manager.send(:write_bootstrap_manifest, job, environment, manifest, plugin)
91
+ end
92
+
93
+ it "writes the manifest" do
94
+ job.should_receive :set_status
95
+
96
+ expect {
97
+ write_bootstrap_manifest
98
+ }.to change { MB::FileSystem.manifests.opendir.count }.by 1
99
+
100
+ filename = MB::FileSystem.manifests.opendir.to_a.select { |filename|
101
+ filename.end_with?(".json")
102
+ }.last
103
+
104
+ expect(filename).to include(plugin.name)
105
+ expect(filename).to include(environment)
106
+
107
+ contents = File.read(MB::FileSystem.manifests.join(filename))
108
+
109
+ expect(JSON.parse(contents)).to eq(manifest.to_hash)
110
+ end
111
+ end
112
+
113
+ describe "#choose_provisioner" do
114
+ let(:id) { nil }
115
+ let(:provisioner) { subject.choose_provisioner(id) }
116
+
117
+ context "when the given id is nil" do
118
+ let(:id) { nil }
119
+
120
+ it "returns the running default provisioner" do
121
+ expect(provisioner).to be_a(MB::Provisioner.default)
122
+ end
123
+ end
124
+
125
+ context "when the given id matches a registered provisioner" do
126
+ let(:id) { :aws }
127
+
128
+ it "returns the matching running provisioner" do
129
+ expect(provisioner).to be_a(MB::Provisioner::AWS)
130
+ end
131
+ end
132
+
133
+ context "when there is no provisioner registered with the given id" do
134
+ let(:id) { :not_existent }
135
+
136
+ it "raises ProvisionerNotRegistered if a provisioner with the given ID has not been registered" do
137
+ expect { provisioner }.to raise_error(MB::ProvisionerNotStarted)
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,182 @@
1
+ require 'spec_helper'
2
+
3
+ describe MB::Provisioner::Manifest do
4
+ subject { described_class.new(valid_manifest) }
5
+
6
+ let(:plugin) {
7
+ metadata = MB::CookbookMetadata.new {
8
+ name "motherbrain"
9
+ version "0.1.0"
10
+ }
11
+
12
+ MB::Plugin.new(metadata)
13
+ }
14
+
15
+ let(:activemq) { MB::Component.new('activemq', plugin) }
16
+ let(:nginx) { MB::Component.new('nginx', plugin) }
17
+
18
+ let(:amq_master) { MB::Group.new('master') }
19
+ let(:amq_slave) { MB::Group.new('slave') }
20
+ let(:nginx_master) { MB::Group.new('master') }
21
+
22
+ let(:valid_manifest) {
23
+ {
24
+ provisioner: "magic",
25
+ options: {
26
+ image_id: "emi-1234ABCD",
27
+ key_name: "mb",
28
+ security_groups: ["foo", "bar"]
29
+ },
30
+ nodes: [
31
+ {
32
+ type: "m1.large",
33
+ groups: ["activemq::master"]
34
+ },
35
+ {
36
+ type: "m1.large",
37
+ count: 2,
38
+ groups: ["activemq::slave"]
39
+ },
40
+ {
41
+ type: "m1.small",
42
+ groups: ["nginx::master"]
43
+ }
44
+ ]
45
+ }
46
+ }
47
+
48
+ before(:each) do
49
+ plugin.stub(:components).and_return([activemq, nginx])
50
+ activemq.stub(:groups).and_return([amq_master, amq_slave])
51
+ nginx.stub(:groups).and_return([nginx_master])
52
+ end
53
+
54
+ its(:node_count) { should == 4 }
55
+ its(:provisioner) { should == "magic" }
56
+
57
+ describe "ClassMethods" do
58
+ subject { described_class }
59
+
60
+ describe "::validate!" do
61
+ it "returns true if the manifest is valid" do
62
+ subject.validate!(valid_manifest, plugin).should be_true
63
+ end
64
+
65
+ it "accepts a Provisioner::Manifest" do
66
+ subject.validate!(described_class.new({ nodes: [] }), plugin).should be_true
67
+ end
68
+
69
+ it "raises InvalidProvisionManifest if given a non-hash value" do
70
+ expect {
71
+ subject.validate!(1, plugin)
72
+ }.to raise_error(MB::InvalidProvisionManifest)
73
+ end
74
+
75
+ context "when manifest contains a group that is not part of the plugin" do
76
+ before(:each) do
77
+ plugin.stub(:has_component?).with("activemq").and_return(false)
78
+ plugin.stub(:has_component?).with("nginx").and_return(false)
79
+ end
80
+
81
+ it "raises an InvalidProvisionManifest error" do
82
+ lambda {
83
+ subject.validate!(valid_manifest, plugin)
84
+ }.should raise_error(MB::InvalidProvisionManifest)
85
+ end
86
+ end
87
+
88
+ context "when manifest contains a group that is not part of a group" do
89
+ before(:each) do
90
+ activemq.stub(:has_group?).with("master").and_return(false)
91
+ end
92
+
93
+ it "raises an InvalidProvisionManifest error" do
94
+ lambda {
95
+ subject.validate!(valid_manifest, plugin)
96
+ }.should raise_error(MB::InvalidProvisionManifest)
97
+ end
98
+ end
99
+
100
+ context "when groups is not in {group}::{group} format" do
101
+ let(:invalid_manifest) {
102
+ {
103
+ nodes: [
104
+ {
105
+ type: "m1.large",
106
+ count: 1,
107
+ groups: [
108
+ "activemq",
109
+ "activemq::slave"
110
+ ]
111
+ }
112
+ ]
113
+ }
114
+ }
115
+
116
+ it "raises an InvalidProvisionManifest error" do
117
+ lambda {
118
+ subject.validate!(invalid_manifest, plugin)
119
+ }.should raise_error(MB::InvalidProvisionManifest)
120
+ end
121
+ end
122
+
123
+ context "when groups is not an array" do
124
+ let(:invalid_manifest) {
125
+ {
126
+ nodes: [
127
+ {
128
+ type: "m1.large",
129
+ count: 1,
130
+ groups: "activemq::slave"
131
+ }
132
+ ]
133
+ }
134
+ }
135
+
136
+ it "raises an InvalidProvisionManifest error" do
137
+ lambda {
138
+ subject.validate!(invalid_manifest, plugin)
139
+ }.should raise_error(MB::InvalidProvisionManifest)
140
+ end
141
+ end
142
+
143
+ context "when type is not provided or nil" do
144
+ let(:invalid_manifest) {
145
+ {
146
+ nodes: [
147
+ {
148
+ count: 1,
149
+ groups: "activemq::slave"
150
+ }
151
+ ]
152
+ }
153
+ }
154
+
155
+ it "raises an InvalidProvisionManifest error" do
156
+ expect {
157
+ subject.validate!(invalid_manifest, plugin)
158
+ }.to raise_error(MB::InvalidProvisionManifest)
159
+ end
160
+ end
161
+
162
+ context "when group is nil" do
163
+ let(:invalid_manifest) {
164
+ {
165
+ nodes: [
166
+ {
167
+ type: "m1.large",
168
+ count: 1
169
+ }
170
+ ]
171
+ }
172
+ }
173
+
174
+ it "raises an InvalidProvisionManifest error" do
175
+ expect {
176
+ subject.validate!(invalid_manifest, plugin)
177
+ }.to raise_error(MB::InvalidProvisionManifest)
178
+ end
179
+ end
180
+ end
181
+ end
182
+ end