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,304 @@
1
+ module MotherBrain
2
+ # Allows for motherbrain clients to lock a chef resource. A mutex is created
3
+ # with a type and name. Sending #lock to the mutex will then store a data bag
4
+ # item with mutex, the requestor's client_name, and the current time. An
5
+ # attempt to lock an already-locked mutex will fail if the lock is owned by
6
+ # someone else, or succeed if the lock is owned by the current user.
7
+ #
8
+ # @example Creating a mutex and obtaining a lock
9
+ #
10
+ # mutex = ChefMutex.new(chef_environment: "my_environment")
11
+ #
12
+ # mutex.lock # => true
13
+ # # do stuff
14
+ # mutex.unlock # => true
15
+ #
16
+ # @example Running a block within an obtained lock
17
+ #
18
+ # mutex = ChefMutex.new(chef_environment: "my_environment")
19
+ #
20
+ # mutex.synchronize do
21
+ # # do stuff
22
+ # end
23
+ #
24
+ class ChefMutex
25
+ class << self
26
+ # Create a new ChefMutex and run the given block of code within it. Terminate the
27
+ # ChefMutex after the block of code finishes executing.
28
+ #
29
+ # @see {ChefMutex#initialize}, {ChefMutex#synchronize}
30
+ def synchronize(options, &block)
31
+ mutex = new(options)
32
+ mutex.synchronize(&block)
33
+ ensure
34
+ mutex.terminate
35
+ end
36
+ end
37
+
38
+ include Celluloid
39
+ include Celluloid::Notifications
40
+ include MB::Logging
41
+ include MB::Mixin::Services
42
+
43
+ extend Forwardable
44
+
45
+ DATA_BAG = "_motherbrain_locks_".freeze
46
+
47
+ LOCK_TYPES = [
48
+ :chef_environment
49
+ ]
50
+
51
+ attr_reader :type
52
+ attr_reader :name
53
+
54
+ attr_reader :force
55
+ attr_reader :job
56
+ attr_reader :report_job_status
57
+ attr_reader :unlock_on_failure
58
+
59
+ execute_block_on_receiver :synchronize
60
+
61
+ finalizer :finalize_callback
62
+
63
+ # @option options [#to_s] :chef_environment
64
+ # The name of the environment to lock
65
+ # @option options [Boolean] :force (false)
66
+ # Force the lock to be written, even if it already exists.
67
+ # @option options [MotherBrain::Job] :job
68
+ # A job that will receive status updates during lock/unlock
69
+ # @option options [Boolean] :report_job_status (false)
70
+ # @option options [Boolean] :unlock_on_failure (true)
71
+ # If false and the block raises an error, the lock will persist.
72
+ def initialize(options = {})
73
+ options = options.reverse_merge(
74
+ force: false,
75
+ unlock_on_failure: true
76
+ )
77
+
78
+ type, name = options.find { |key, value| LOCK_TYPES.include? key }
79
+
80
+ @type = type
81
+ @name = name
82
+ @force = options[:force]
83
+ @job = options[:job]
84
+ @report_job_status = options[:report_job_status]
85
+ @unlock_on_failure = options[:unlock_on_failure]
86
+
87
+ lock_manager.register(Actor.current)
88
+ end
89
+
90
+ # @return [String]
91
+ def data_bag_id
92
+ result = to_s.dup
93
+
94
+ result.downcase!
95
+ result.gsub! /[^\w]+/, "-" # dasherize
96
+ result.gsub! /^-+|-+$/, "" # remove dashes from beginning/end
97
+
98
+ result
99
+ end
100
+
101
+ # @return [String]
102
+ def to_s
103
+ "#{type}:#{name}"
104
+ end
105
+
106
+ # Attempts to create a lock. Fails if the lock already exists.
107
+ #
108
+ # @return [Boolean]
109
+ def lock
110
+ unless type
111
+ raise InvalidLockType, "Must pass a valid lock type (#{LOCK_TYPES})"
112
+ end
113
+
114
+ log.info { "Locking #{to_s}" }
115
+
116
+ if job
117
+ job.set_status "Locking #{to_s}"
118
+ job.report_running if report_job_status
119
+ end
120
+
121
+ report(attempt_lock)
122
+ end
123
+
124
+ # Returns whether or not the object is locked.
125
+ #
126
+ # @return [Boolean]
127
+ def locked?
128
+ !!read
129
+ end
130
+
131
+ # Obtains a lock, runs the block, and releases the lock when the block
132
+ # completes. Raises a ResourceLocked error if the lock was unobtainable.
133
+ # If the block raises an error, the resource is unlocked, unless
134
+ # unlock_on_failure: true is passed in to the option hash.
135
+ #
136
+ # @raise [MotherBrain::ResourceLocked] if the lock is unobtainable
137
+ #
138
+ # @return [Boolean]
139
+ def synchronize
140
+ unless lock
141
+ current_lock = read
142
+
143
+ err = "Resource #{current_lock['id']} locked by #{current_lock['client_name']}"
144
+ err << " since #{current_lock['time']} (PID #{current_lock['process_id']})"
145
+
146
+ raise ResourceLocked.new(err)
147
+ end
148
+
149
+ yield
150
+
151
+ unlock
152
+ rescue => ex
153
+ ex = ex.respond_to?(:cause) ? ex.cause : ex
154
+
155
+ unless ex.is_a?(ResourceLocked)
156
+ unlock if unlock_on_failure
157
+ end
158
+
159
+ abort(ex)
160
+ end
161
+
162
+ # Attempts to unlock the lock. Fails if the lock doesn't exist, or if it is
163
+ # held by someone else
164
+ #
165
+ # @return [Boolean]
166
+ def unlock
167
+ if job
168
+ job.report_running if report_job_status
169
+ job.set_status("Unlocking #{to_s}")
170
+ end
171
+
172
+ attempt_unlock
173
+
174
+ report(true)
175
+ end
176
+
177
+ private
178
+
179
+ def finalize_callback
180
+ lock_manager.async.unregister(Actor.current) if lock_manager.alive?
181
+ end
182
+
183
+ # Reports a job status
184
+ # @param [Object] result
185
+ # @return [Object] result
186
+ def report(result)
187
+ if job && report_job_status
188
+ job.report_boolean(result)
189
+ end
190
+
191
+ result
192
+ end
193
+
194
+ # Check to see if the passed in lock was created by us
195
+ #
196
+ # @param [Hash] current_lock the lock data obtained from #read
197
+ # @return [Boolean]
198
+ def our_lock?(current_lock)
199
+ return nil unless current_lock
200
+ return false unless current_lock["client_name"] == client_name
201
+ return false unless current_lock["process_id"] == Process.pid
202
+ true
203
+ end
204
+
205
+ # @return [Boolean]
206
+ def attempt_lock
207
+ unless self.force
208
+ current_lock = read
209
+ return our_lock?(current_lock) if current_lock
210
+ end
211
+
212
+ write
213
+ end
214
+
215
+ # @return [Boolean]
216
+ def attempt_unlock
217
+ unless self.force
218
+ current_lock = read
219
+
220
+ return unless current_lock && our_lock?(current_lock)
221
+ end
222
+
223
+ delete
224
+ end
225
+
226
+ # @return [String]
227
+ def client_name
228
+ Application.ridley.client_name
229
+ end
230
+
231
+ # @return [Ridley::ChainLink]
232
+ def data_bag
233
+ Application.ridley.data_bag
234
+ end
235
+
236
+ # Delete the lock from the data bag.
237
+ #
238
+ # @return [Boolean]
239
+ def delete
240
+ return true unless locks
241
+
242
+ result = locks.delete(data_bag_id)
243
+
244
+ lock_manager.unregister(Actor.current)
245
+
246
+ result
247
+ rescue
248
+ lock_manager.register(Actor.current)
249
+ end
250
+
251
+ # Create our data bag if it doesn't already exist
252
+ def ensure_data_bag_exists
253
+ data_bag.create(name: DATA_BAG) unless locks
254
+ end
255
+
256
+ # @return [Ridley::DBIChainLink] if the data bag exists
257
+ # @return [nil] if it does not
258
+ def locks
259
+ result = data_bag.find(DATA_BAG)
260
+
261
+ return unless result
262
+
263
+ result.item
264
+ end
265
+
266
+ # Read the lock from the data bag.
267
+ #
268
+ # @return [Hash] if the lock exists
269
+ # @return [nil] if it does not
270
+ def read
271
+ return unless locks
272
+
273
+ result = locks.find(data_bag_id)
274
+
275
+ result.to_hash if result
276
+ end
277
+
278
+ def unregister_lock
279
+ lock_manager.unregister(Actor.current)
280
+ end
281
+
282
+ # Write the lock to the data bag.
283
+ #
284
+ # @return [Boolean]
285
+ def write
286
+ ensure_data_bag_exists
287
+
288
+ result = locks.new(
289
+ id: data_bag_id,
290
+ type: type,
291
+ name: name,
292
+ client_name: client_name,
293
+ process_id: Process.pid,
294
+ time: Time.now
295
+ ).save
296
+
297
+ lock_manager.register(Actor.current)
298
+
299
+ result
300
+ rescue
301
+ lock_manager.unregister(Actor.current)
302
+ end
303
+ end
304
+ end
@@ -0,0 +1,39 @@
1
+ module MotherBrain
2
+ class CleanRoomBase
3
+ class << self
4
+ # Create a DSL writer function that will assign the a given value
5
+ # to the real object of this clean room.
6
+ #
7
+ # @param [Symbol] attribute
8
+ def dsl_attr_writer(attribute)
9
+ class_eval do
10
+ define_method(attribute) do |value|
11
+ set_attribute(attribute, value)
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ include MB::Logging
18
+
19
+ # @param [Object] real_model
20
+ def initialize(real_model, &block)
21
+ @real_model = real_model
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :real_model
27
+
28
+ def set_attribute(name, value)
29
+ real_model.send("#{name}=", value)
30
+ end
31
+
32
+ def method_missing(method_name, *args, &block)
33
+ ErrorHandler.wrap PluginSyntaxError,
34
+ backtrace: caller,
35
+ method_name: method_name,
36
+ text: "'#{method_name}' is not a valid keyword"
37
+ end
38
+ end
39
+ end
data/lib/mb/cli.rb ADDED
@@ -0,0 +1,50 @@
1
+ module MotherBrain
2
+ module Cli
3
+ autoload :Base, 'mb/cli/base'
4
+ autoload :Shell, 'mb/cli/shell'
5
+ autoload :SubCommand, 'mb/cli/sub_command'
6
+
7
+ # This is the main entry point for the CLI. It exposes the method {#execute!} to
8
+ # start the CliGateway.
9
+ #
10
+ # @note the arity of {#initialize} and {#execute!} are extremely important for testing purposes. It
11
+ # is a requirement to perform in-process testing with Aruba. In process testing is much faster
12
+ # than spawning a new Ruby process for each test.
13
+ class Runner
14
+ # @param [Array] argv
15
+ # @param [IO] stdin
16
+ # @param [IO] stdout
17
+ # @param [IO] stderr
18
+ # @param [Kernel] kernel
19
+ def initialize(argv, stdin = STDIN, stdout = STDOUT, stderr = STDERR, kernel = Kernel)
20
+ @argv, @stdin, @stdout, @stderr, @kernel = argv, stdin, stdout, stderr, kernel
21
+ end
22
+
23
+ # Start the CLI Gateway
24
+ def execute!
25
+ MB::CliGateway.start(@argv)
26
+ rescue MBError => ex
27
+ ui.error ex
28
+ @kernel.exit(ex.exit_code)
29
+ rescue Ridley::Errors::ConnectionFailed => ex
30
+ ui.error "[ERROR] Unable to connect to the configured Chef server: #{ex.message}."
31
+ ui.error "[ERROR] Check your configuration and network settings and try again."
32
+ @kernel.exit(MB::ChefConnectionError.exit_code)
33
+ rescue Thor::Error => ex
34
+ ui.error ex.message
35
+ @kernel.exit(1)
36
+ rescue Errno::EPIPE
37
+ # This happens if a thor command is piped to something like `head`,
38
+ # which closes the pipe when it's done reading. This will also
39
+ # mean that if the pipe is closed, further unnecessary
40
+ # computation will not occur.
41
+ @kernel.exit(0)
42
+ end
43
+
44
+ # @return [MB::Cli::Shell::Color, MB::Cli::Shell::Basic]
45
+ def ui
46
+ @ui ||= MB::Cli::Shell.shell.new
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,51 @@
1
+ module MotherBrain
2
+ module Cli
3
+ class Base < Thor
4
+ include Thor::Actions
5
+ include MB::Mixin::CodedExit
6
+ include MB::Mixin::Services
7
+
8
+ class << self
9
+ # Registers a SubCommand with this Cli::Base class
10
+ #
11
+ # @param [MB::Cli::SubCommand] klass
12
+ def register_subcommand(klass)
13
+ self.register(klass, klass.name.gsub('-', '_'), klass.usage.gsub('-', '_'), klass.description)
14
+ end
15
+
16
+ # @return [MB::Cli::Shell::Color, MB::Cli::Shell::Basic]
17
+ def ui
18
+ @ui ||= MB::Cli::Shell.shell.new
19
+ end
20
+ end
21
+
22
+ no_tasks do
23
+ # @param [MB::Job] job
24
+ def display_job(job)
25
+ CliClient.new(job).display
26
+ end
27
+ # @note from Jamie: Increased verbosity for Michael Ivey. This is pretty much the most important line of code
28
+ # in this entire codebase so DO NOT REMOVE.
29
+ alias_method :display_job_status_and_wait_until_it_is_done_while_providing_user_feedback, :display_job
30
+
31
+ # @return [MB::Cli::Shell::Color, MB::Cli::Shell::Basic]
32
+ def ui
33
+ self.class.ui
34
+ end
35
+
36
+ def requires_one_of(*valid_options)
37
+ valid_options = valid_options.flatten
38
+
39
+ return if options.slice(*valid_options).any?
40
+
41
+ valid_cli_arguments = valid_options.map { |key|
42
+ key.to_s.dasherize.prepend('--')
43
+ }
44
+
45
+ ui.say "Requires one of #{valid_cli_arguments.join(', ')}"
46
+ exit 1
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,29 @@
1
+ module MotherBrain
2
+ module Cli
3
+ module Shell
4
+ autoload :Basic, 'mb/cli/shell/basic'
5
+ autoload :Color, 'mb/cli/shell/color'
6
+ autoload :Ext, 'mb/cli/shell/ext'
7
+
8
+ class << self
9
+ attr_writer :shell
10
+
11
+ # Returns the shell used in the motherbrain CLI. If you are in a Unix platform
12
+ # it will use a colored shell, otherwise it will use a color-less one.
13
+ #
14
+ # @return [Shell::Basic, Shell::Color]
15
+ def shell
16
+ @shell ||= if ENV['MB_SHELL'] && ENV['MB_SHELL'].size > 0
17
+ Shell.const_get(ENV['MB_SHELL'].capitalize)
18
+ elsif Buff::Platform.windows? && !ENV['ANSICON']
19
+ Shell::Basic
20
+ else
21
+ Shell::Color
22
+ end
23
+ end
24
+ end
25
+
26
+ ::Thor::Base.shell = shell
27
+ end
28
+ end
29
+ end