capistrano_multiconfig_parallel 0.18.2 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 636e8807495e7cc5082a737fddaf6de9eb132d50
4
- data.tar.gz: 77409e0a2196ce97e32fc12f9d9feffd54972bda
3
+ metadata.gz: c16a337397b541e4bed172a5abda0ad5b625d81b
4
+ data.tar.gz: 2d452cdd7abcb3b9f7cbad1e134b6a6199cab0e9
5
5
  SHA512:
6
- metadata.gz: 524ffa981d6cfc38698300d0334ec049a384002ed9b0b31eed239ca4d02047fce155a3211e53a720d533e2b911aa320fea0e789d934dd7728fb1c239c54d2f4c
7
- data.tar.gz: d8b40b726fb03873c14d720604bbd99ee74e676d177b1778ab2cfa791d1645b86f473abd701d124f56779414ededec03c5dce0da0af7c81cb9cd61485752920f
6
+ metadata.gz: 5f6537946ef0fa1a9a70ad8aea9a255a5db0b85c4ed019f565f443b4079067d1650d353f53b13b62f5c157e7c2299462a5d2a9083f0c615f9ca4105826623300
7
+ data.tar.gz: 2c59e8ba2d189d3c757de7c4567629f16dba74933c0ddd1be57f3607f000420805e354cacaf7299926b6a2f89d785fd143abe30dc8b7b52ea65620cca1503bad
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
19
 
20
20
  s.add_runtime_dependency 'celluloid-pmap', '~> 0.2', '>= 0.2.2'
21
- s.add_runtime_dependency 'celluloid_pubsub', '~> 0.0', '>= 0.0.21'
21
+ s.add_runtime_dependency 'celluloid_pubsub', '~> 0.0', '>= 0.0.22'
22
22
  s.add_runtime_dependency 'composable_state_machine', '~> 1.0', '>= 1.0.2'
23
23
  s.add_runtime_dependency 'terminal-table', '~> 1.5', '>= 1.5.2'
24
24
  s.add_runtime_dependency 'colorize', '~> 0.7', '>= 0.7'
@@ -43,7 +43,7 @@ module CapistranoMulticonfigParallel
43
43
  options = options.stringify_keys
44
44
  return unless applications.present?
45
45
  applications.each do |app|
46
- deploy_app(options.merge('app' => app))
46
+ deploy_app(options.merge('app' => app['app']))
47
47
  end
48
48
  end
49
49
 
@@ -111,7 +111,7 @@ module CapistranoMulticonfigParallel
111
111
  return unless @jobs.present?
112
112
  FileUtils.rm Dir["#{log_directory}/worker_*.log"]
113
113
  if app_configuration.multi_secvential.to_s.downcase == 'true'
114
- @jobs.each { |job| CapistranoMulticonfigParallel::StandardDeploy.new(job) }
114
+ @jobs.each { |job| job.execute_standard_deploy }
115
115
  else
116
116
  run_async_jobs
117
117
  end
@@ -142,11 +142,10 @@ module CapistranoMulticonfigParallel
142
142
 
143
143
  def deploy_app(options = {})
144
144
  options = options.stringify_keys
145
- app = options['app'].is_a?(Hash) ? options['app'] : { 'app' => options['app'] }
146
145
  branch = @branch_backup.present? ? @branch_backup : @argv['BRANCH'].to_s
147
146
  call_task_deploy_app({
148
147
  branch: branch,
149
- app: app,
148
+ app: options['app'],
150
149
  action: options['action']
151
150
  }.reverse_merge(options))
152
151
  end
@@ -210,8 +209,7 @@ module CapistranoMulticonfigParallel
210
209
  def prepare_job(options)
211
210
  options = options.stringify_keys
212
211
  branch_name = options.fetch('branch', {})
213
- app = options.fetch('app', {})
214
- app = app.fetch('app', '')
212
+ app = options.fetch('app', '')
215
213
  box = options['env_options']['BOX']
216
214
  message = box.present? ? "BOX #{box}:" : "stage #{options['stage']}:"
217
215
  env_opts = get_app_additional_env_options(app, message)
@@ -221,15 +219,11 @@ module CapistranoMulticonfigParallel
221
219
  env_options = branch_name.present? ? { 'BRANCH' => branch_name }.merge(options['env_options']) : options['env_options']
222
220
  job_env_options = custom_command? && env_options['ACTION'].present? ? env_options.except('ACTION') : env_options
223
221
 
224
- job = {
225
- id: SecureRandom.random_number(500),
226
- app: app,
227
- env: options['stage'],
222
+ job = CapistranoMulticonfigParallel::Job.new(options.merge(
228
223
  action: custom_command? && env_options['ACTION'].present? ? env_options['ACTION'] : options['action'],
229
- task_arguments: options['task_arguments'],
230
224
  env_options: job_env_options
231
- }
232
- @jobs << job.stringify_keys
225
+ ))
226
+ @jobs << job
233
227
  end
234
228
 
235
229
  def prepare_options(options)
@@ -25,6 +25,9 @@ module CapistranoMulticonfigParallel
25
25
  # http://rubydoc.info/gems/celluloid/Celluloid/SupervisionGroup/Member
26
26
  @workers = @worker_supervisor.pool(CapistranoMulticonfigParallel::CelluloidWorker, as: :workers, size: 10)
27
27
  Actor.current.link @workers
28
+ @worker_supervisor.supervise_as(:terminal_server, CapistranoMulticonfigParallel::TerminalTable, Actor.current, @job_manager)
29
+ @worker_supervisor.supervise_as(:web_server, CapistranoMulticonfigParallel::WebServer, websocket_config)
30
+
28
31
  # Get a handle on the PoolManager
29
32
  # http://rubydoc.info/gems/celluloid/Celluloid/PoolManager
30
33
  # @workers = workers_pool.actor
@@ -33,22 +36,12 @@ module CapistranoMulticonfigParallel
33
36
  @job_to_worker = {}
34
37
  @worker_to_job = {}
35
38
  @job_to_condition = {}
36
- @worker_supervisor.supervise_as(:terminal_server, CapistranoMulticonfigParallel::TerminalTable, Actor.current, @job_manager)
37
- @worker_supervisor.supervise_as(:web_server, CapistranoMulticonfigParallel::WebServer, websocket_config)
38
- end
39
-
40
- def generate_job_id(job)
41
- @jobs[job['id']] = job
42
- job['id']
43
39
  end
44
40
 
45
41
  # call to send an actor
46
42
  # a job
47
43
  def delegate(job)
48
- job = job.stringify_keys
49
- job['id'] = generate_job_id(job) unless job_failed?(job)
50
- @jobs[job['id']] = job
51
- job['env_options'][CapistranoMulticonfigParallel::ENV_KEY_JOB_ID] = job['id']
44
+ @jobs[job.id] = job
52
45
  # debug(@jobs)
53
46
  # start work and send it to the background
54
47
  @workers.async.work(job, Actor.current)
@@ -57,18 +50,8 @@ module CapistranoMulticonfigParallel
57
50
  # call back from actor once it has received it's job
58
51
  # actor should do this asap
59
52
  def register_worker_for_job(job, worker)
60
- job = job.stringify_keys
61
- if job['id'].blank?
62
- log_to_file("job id not found. delegating again the job #{job.inspect}")
63
- delegate(job)
64
- else
65
- start_worker(job, worker)
66
- end
67
- end
68
-
69
- def start_worker(job, worker)
70
- worker.job_id = job['id'] if worker.job_id.blank?
71
- @job_to_worker[job['id']] = worker
53
+ worker.job_id = job.id if worker.job_id.blank?
54
+ @job_to_worker[job.id] = worker
72
55
  @worker_to_job[worker.mailbox.address] = job
73
56
  log_to_file("worker #{worker.job_id} registed into manager")
74
57
  Actor.current.link worker
@@ -77,7 +60,7 @@ module CapistranoMulticonfigParallel
77
60
  end
78
61
 
79
62
  def all_workers_finished?
80
- @job_to_worker.all? { |job_id, _worker| @jobs[job_id]['worker_action'] == 'finished' }
63
+ @job_to_worker.all? { |job_id, _worker| @jobs[job_id].finished? }
81
64
  end
82
65
 
83
66
  def process_jobs
@@ -105,24 +88,24 @@ module CapistranoMulticonfigParallel
105
88
  !@job_manager.can_tag_staging?
106
89
  end
107
90
 
108
- def apply_confirmation_for_worker(worker)
109
- worker.alive? && app_configuration.apply_stage_confirmation.include?(worker.env_name) && apply_confirmations?
91
+ def apply_confirmation_for_job(job)
92
+ app_configuration.apply_stage_confirmation.include?(job.stage) && apply_confirmations?
110
93
  end
111
94
 
112
- def setup_worker_conditions(worker)
113
- return unless apply_confirmation_for_worker(worker)
95
+ def setup_worker_conditions(job)
96
+ return unless apply_confirmation_for_job(job)
114
97
  hash_conditions = {}
115
98
  app_configuration.task_confirmations.each do |task|
116
99
  hash_conditions[task] = { condition: Celluloid::Condition.new, status: 'unconfirmed' }
117
100
  end
118
- @job_to_condition[worker.job_id] = hash_conditions
101
+ @job_to_condition[job.id] = hash_conditions
119
102
  end
120
103
 
121
- def mark_completed_remaining_tasks(worker)
122
- return unless apply_confirmation_for_worker(worker)
104
+ def mark_completed_remaining_tasks(job)
105
+ return unless apply_confirmation_for_job(job)
123
106
  app_configuration.task_confirmations.each_with_index do |task, _index|
124
107
  fake_result = proc { |sum| sum }
125
- task_confirmation = @job_to_condition[worker.job_id][task]
108
+ task_confirmation = @job_to_condition[job.id][task]
126
109
  if task_confirmation[:status] != 'confirmed'
127
110
  task_confirmation[:status] = 'confirmed'
128
111
  task_confirmation[:condition].signal(fake_result)
@@ -130,11 +113,12 @@ module CapistranoMulticonfigParallel
130
113
  end
131
114
  end
132
115
 
133
- def wait_task_confirmations_worker(worker)
134
- return if !apply_confirmation_for_worker(worker) || !syncronized_confirmation?
116
+ def wait_task_confirmations_worker(job)
117
+ return unless job.finished? || job.exit_status.present?
118
+ return if !apply_confirmation_for_job(job) || !syncronized_confirmation?
135
119
  app_configuration.task_confirmations.each_with_index do |task, _index|
136
- result = wait_condition_for_task(worker.job_id, task)
137
- confirm_task_approval(result, task, worker) if result.present?
120
+ result = wait_condition_for_task(job.id, task)
121
+ confirm_task_approval(result, task, job) if result.present?
138
122
  end
139
123
  end
140
124
 
@@ -157,10 +141,10 @@ module CapistranoMulticonfigParallel
157
141
  end
158
142
  end
159
143
 
160
- def print_confirm_task_approvall(result, task, worker = nil)
144
+ def print_confirm_task_approvall(result, task, job)
161
145
  return if result.is_a?(Proc)
162
146
  message = "Do you want to continue the deployment and execute #{task.upcase}"
163
- message += " for JOB #{worker.job_id}" if worker.present?
147
+ message += " for JOB #{job.id}" if job.present?
164
148
  message += '?'
165
149
  apps_symlink_confirmation = Celluloid::Actor[:terminal_server].show_confirmation(message, 'Y/N')
166
150
  until apps_symlink_confirmation.present?
@@ -169,17 +153,17 @@ module CapistranoMulticonfigParallel
169
153
  apps_symlink_confirmation
170
154
  end
171
155
 
172
- def confirm_task_approval(result, task, worker = nil)
156
+ def confirm_task_approval(result, task, job = nil)
173
157
  return unless result.present?
174
- result = print_confirm_task_approvall(result, task, worker = nil)
158
+ result = print_confirm_task_approvall(result, task, job)
175
159
  return if result.blank? || result.downcase != 'y'
176
160
  @jobs.pmap do |job_id, job|
177
161
  worker = get_worker_for_job(job_id)
178
162
  worker.publish_rake_event('approved' => 'yes',
179
- 'action' => 'invoke',
180
- 'job_id' => job['id'],
181
- 'task' => task
182
- )
163
+ 'action' => 'invoke',
164
+ 'job_id' => job.id,
165
+ 'task' => task
166
+ )
183
167
  end
184
168
  end
185
169
 
@@ -187,50 +171,25 @@ module CapistranoMulticonfigParallel
187
171
  if job.present?
188
172
  if job.is_a?(Hash)
189
173
  job = job.stringify_keys
190
- @job_to_worker[job['id']]
174
+ @job_to_worker[job.id]
191
175
  else
192
- @job_to_worker[job.to_i]
176
+ @job_to_worker[job]
193
177
  end
194
178
  else
195
179
  return nil
196
- end
197
180
  end
181
+ end
198
182
 
199
183
  def can_tag_staging?
200
184
  @job_manager.can_tag_staging? &&
201
- @jobs.find { |_job_id, job| job['env'] == 'production' }.blank?
202
- end
203
-
204
- def dispatch_new_job(job)
205
- original_env = job['env_options']
206
- env_opts = @job_manager.get_app_additional_env_options(job['app_name'], job['env'])
207
- job['env_options'] = original_env.merge(env_opts)
208
- async.delegate(job)
185
+ @jobs.find { |_job_id, job| job['env'] == 'production' }.blank?
209
186
  end
210
187
 
211
- def filtered_env_keys
212
- %w(STAGES ACTION)
213
- end
214
-
215
- def process_job(job)
216
- if job['processed']
217
- @jobs[job['job_id']]
218
- else
219
- env_options = {}
220
- job['env_options'].each do |key, value|
221
- env_options[key] = value if value.present? && !filtered_env_keys.include?(key)
222
- end
223
- {
224
- 'job_id' => job['id'],
225
- 'app_name' => job['app'],
226
- 'env_name' => job['env'],
227
- 'action_name' => job['action'],
228
- 'env_options' => env_options,
229
- 'task_arguments' => job['task_arguments'],
230
- 'job_argv' => job.fetch('job_argv', []),
231
- 'processed' => true
232
- }
233
- end
188
+ def dispatch_new_job(job, options = {})
189
+ env_opts = @job_manager.get_app_additional_env_options(job.app, job.stage)
190
+ job.env_options = options.merge(env_opts)
191
+ new_job = CapistranoMulticonfigParallel::Job.new(job.to_s)
192
+ async.delegate(new_job)
234
193
  end
235
194
 
236
195
  # lookup status of job by asking actor running it
@@ -239,29 +198,33 @@ module CapistranoMulticonfigParallel
239
198
  if job.present?
240
199
  if job.is_a?(Hash)
241
200
  job = job.stringify_keys
242
- actor = @registered_jobs[job['id']]
201
+ actor = @job_to_worker[job.id]
243
202
  status = actor.status
244
203
  else
245
- actor = @registered_jobs[job.to_i]
204
+ actor = @job_to_worker[job]
246
205
  status = actor.status
247
206
  end
248
207
  end
249
208
  status
250
209
  end
251
210
 
211
+ def job_crashed?(job)
212
+ job.action == 'deploy:rollback' || job.action == 'deploy:failed' || job_failed?(job)
213
+ end
214
+
252
215
  def job_failed?(job)
253
- job['worker_action'].present? && job['worker_action'] == 'worker_died'
216
+ job.status.present? && job.status == 'worker_died'
254
217
  end
255
218
 
256
219
  def worker_died(worker, reason)
257
220
  job = @worker_to_job[worker.mailbox.address]
258
221
  log_to_file("worker job #{job} with mailbox #{worker.mailbox.inspect} died for reason: #{reason}")
259
222
  @worker_to_job.delete(worker.mailbox.address)
260
- return if job.blank? || job_failed?(job)
261
- return unless job['action_name'] == 'deploy'
223
+ return if job.blank? || job_crashed?(job)
224
+ return unless job.action == 'deploy'
262
225
  log_to_file "restarting #{job} on new worker"
263
- job = job.merge(:action => 'deploy:rollback', 'worker_action' => 'worker_died')
264
- delegate(job)
226
+ job.status = 'worker_died'
227
+ dispatch_new_job(job, :action => 'deploy:rollback')
265
228
  end
266
229
  end
267
230
  end
@@ -27,14 +27,14 @@ module CapistranoMulticonfigParallel
27
27
  attr_accessor :job, :manager, :job_id, :app_name, :env_name, :action_name, :env_options, :machine, :client, :task_argv,
28
28
  :rake_tasks, :current_task_number, # tracking tasks
29
29
  :successfull_subscription, :subscription_channel, :publisher_channel, # for subscriptions and publishing events
30
- :job_termination_condition, :worker_state, :invocation_chain, :filename, :worker_log
30
+ :job_termination_condition, :worker_state, :invocation_chain, :filename, :worker_log, :exit_status
31
31
 
32
32
  def work(job, manager)
33
33
  @job = job
34
+ @job_id = job.id
34
35
  @worker_state = 'started'
35
36
  @manager = manager
36
37
  @job_confirmation_conditions = []
37
- process_job(job) if job.present?
38
38
  log_to_file("worker #{@job_id} received #{job.inspect}")
39
39
  @subscription_channel = "worker_#{@job_id}"
40
40
  @machine = CapistranoMulticonfigParallel::StateMachine.new(job, Actor.current)
@@ -42,7 +42,7 @@ module CapistranoMulticonfigParallel
42
42
  end
43
43
 
44
44
  def start_task
45
- @manager.setup_worker_conditions(Actor.current)
45
+ @manager.setup_worker_conditions(@job)
46
46
  log_to_file("exec worker #{@job_id} starts task with #{@job.inspect}")
47
47
  @client = CelluloidPubsub::Client.connect(actor: Actor.current, enable_debug: debug_websocket?, channel: subscription_channel)
48
48
  end
@@ -52,7 +52,7 @@ module CapistranoMulticonfigParallel
52
52
  end
53
53
 
54
54
  def rake_actor_id(data)
55
- data['action'].present? && data['action'] == 'count' ? "rake_worker_#{@job_id}_count" : "rake_worker_#{@job_id}"
55
+ "rake_worker_#{@job_id}"
56
56
  end
57
57
 
58
58
  def on_message(message)
@@ -77,29 +77,14 @@ module CapistranoMulticonfigParallel
77
77
  @invocation_chain ||= []
78
78
  end
79
79
 
80
- def cd_working_directory
81
- "cd #{detect_root}"
82
- end
83
-
84
- # def generate_command_new
85
- # <<-CMD
86
- # bundle exec ruby -e "require 'bundler' ; Bundler.with_clean_env { %x[cd #{cd_working_directory} && bundle install && RAILS_ENV=#{@env_name} bundle exec cap #{@task_argv.join(' ')}] } "
87
- # CMD
88
- # end
89
80
 
90
- def generate_command
91
- <<-CMD
92
- #{cd_working_directory} && RAILS_ENV=#{@env_name} bundle exec multi_cap #{@task_argv.join(' ')}
93
- CMD
94
- end
95
81
 
96
82
  def execute_deploy
97
83
  log_to_file("invocation chain #{@job_id} is : #{@rake_tasks.inspect}")
98
84
  check_child_proces
99
- setup_task_arguments
100
- log_to_file("worker #{@job_id} executes: #{generate_command}")
101
- @child_process.async.work(generate_command, actor: Actor.current, silent: true)
102
- @manager.wait_task_confirmations_worker(Actor.current)
85
+ log_to_file("worker #{@job_id} executes: #{@job.build_capistrano_task}")
86
+ @child_process.async.work(@job, @job.build_capistrano_task, actor: Actor.current, silent: true)
87
+ @manager.wait_task_confirmations_worker(@job)
103
88
  end
104
89
 
105
90
  def check_child_proces
@@ -117,15 +102,15 @@ module CapistranoMulticonfigParallel
117
102
  end
118
103
 
119
104
  def check_gitflow
120
- return if @env_name != 'staging' || !@manager.can_tag_staging? || !executed_task?(CapistranoMulticonfigParallel::GITFLOW_TAG_STAGING_TASK)
121
- @manager.dispatch_new_job(@job.merge('env' => 'production'))
105
+ return if @job.stage != 'staging' || !@manager.can_tag_staging? || !executed_task?(CapistranoMulticonfigParallel::GITFLOW_TAG_STAGING_TASK)
106
+ @manager.dispatch_new_job(@job,'env' => 'production')
122
107
  end
123
108
 
124
109
  def handle_subscription(message)
125
110
  if message_is_about_a_task?(message)
126
111
  check_gitflow
127
112
  save_tasks_to_be_executed(message)
128
- update_machine_state(message['task']) # if message['action'] == 'invoke'
113
+ async.update_machine_state(message['task']) # if message['action'] == 'invoke'
129
114
  log_to_file("worker #{@job_id} state is #{@machine.state}")
130
115
  task_approval(message)
131
116
  elsif message_is_for_stdout?(message)
@@ -169,77 +154,29 @@ module CapistranoMulticonfigParallel
169
154
  log_to_file("worker #{@job_id} triest to transition from #{@machine.state} to #{name}")
170
155
  @machine.transitions.on(name.to_s, @machine.state => name.to_s)
171
156
  @machine.go_to_transition(name.to_s)
172
- abort(CapistranoMulticonfigParallel::CelluloidWorker::TaskFailed, "task #{@action} failed ") if name == 'deploy:failed' # force worker to rollback
173
- end
174
-
175
- def setup_command_line(*options)
176
- @task_argv = []
177
- options.each do |option|
178
- @task_argv << option
179
- end
180
- @task_argv
181
- end
182
-
183
- def worker_stage
184
- @app_name.present? ? "#{@app_name}:#{@env_name}" : "#{@env_name}"
185
- end
186
-
187
- def worker_action
188
- "#{@action_name}[#{@task_arguments.join(',')}]"
157
+ abort(CapistranoMulticonfigParallel::CelluloidWorker::TaskFailed.new("task #{@action} failed ")) if name == 'deploy:failed' # force worker to rollback
189
158
  end
190
159
 
191
- def setup_task_arguments(*args)
192
- # stage = "#{@app_name}:#{@env_name} #{@action_name}"
193
- array_options = []
194
- @env_options.each do |key, value|
195
- array_options << "#{key}=#{value}" if value.present?
196
- end
197
- array_options << '--trace' if app_debug_enabled?
198
- args.each do |arg|
199
- array_options << arg
200
- end
201
- @manager.jobs[@job_id]['job_argv'] = array_options.clone
202
- array_options.unshift("#{worker_action}")
203
- array_options.unshift("#{worker_stage}")
204
- setup_command_line(*array_options)
205
- end
206
160
 
207
161
  def send_msg(channel, message = nil)
208
162
  publish channel, message.present? && message.is_a?(Hash) ? { job_id: @job_id }.merge(message) : { job_id: @job_id, time: Time.now }
209
163
  end
210
164
 
211
- def process_job(job)
212
- processed_job = @manager.process_job(job)
213
- @job_id = processed_job['job_id']
214
- @app_name = processed_job['app_name']
215
- @env_name = processed_job['env_name']
216
- @action_name = processed_job['action_name']
217
- @env_options = processed_job['env_options']
218
- @task_arguments = processed_job['task_arguments']
219
- end
220
-
221
- def crashed?
222
- @action_name == 'deploy:rollback' || @action_name == 'deploy:failed' || @manager.job_failed?(@job)
223
- end
224
-
225
165
  def finish_worker
226
- @manager.mark_completed_remaining_tasks(Actor.current)
227
- @manager.jobs[@job_id]['worker_action'] = 'finished'
228
- @manager.workers_terminated.signal('completed') if @manager.all_workers_finished?
166
+ @manager.mark_completed_remaining_tasks(@job)
167
+ @job.status = 'finished'
168
+ @manager.workers_terminated.signal('completed') if @manager.alive? && @manager.all_workers_finished?
229
169
  end
230
170
 
231
- def worker_finshed?
232
- @manager.jobs[@job_id]['worker_action'] == 'finished'
233
- end
234
171
 
235
172
  def notify_finished(exit_status)
236
- if exit_status.exitstatus != 0
173
+ if exit_status != 0
237
174
  log_to_file("worker #{job_id} tries to terminate")
238
- abort(CapistranoMulticonfigParallel::CelluloidWorker::TaskFailed, "task failed with exit status #{exit_status.inspect} ") # force worker to rollback
175
+ abort(CapistranoMulticonfigParallel::CelluloidWorker::TaskFailed.new("task failed with exit status #{exit_status.inspect} ")) # force worker to rollback
239
176
  else
240
- update_machine_state('FINISHED')
177
+ async.update_machine_state('FINISHED')
241
178
  log_to_file("worker #{job_id} notifies manager has finished")
242
- finish_worker
179
+ async.finish_worker
243
180
  end
244
181
  end
245
182
  end
@@ -7,14 +7,16 @@ module CapistranoMulticonfigParallel
7
7
  include Celluloid::Logger
8
8
  include CapistranoMulticonfigParallel::ApplicationHelper
9
9
 
10
- attr_accessor :actor, :pid, :exit_status, :process, :job_id
10
+ attr_accessor :options, :actor, :job_id, :exit_status, :pid, :process, :job
11
11
 
12
- finalizer :process_finalizer
12
+ finalizer :process_finalizer
13
13
 
14
- def work(cmd, options = {})
14
+ def work(job, cmd, options = {})
15
15
  @options = options
16
+ @job = job
16
17
  @actor = @options.fetch(:actor, nil)
17
- @job_id = @actor.job_id
18
+ @job_id = @job.id
19
+ @exit_status = nil
18
20
  EM.run do
19
21
  EM.next_tick do
20
22
  start_async_deploy(cmd, options)
@@ -31,14 +33,16 @@ module CapistranoMulticonfigParallel
31
33
  end
32
34
 
33
35
  def process_finalizer
34
- @timer.cancel
35
36
  EM.stop if EM.reactor_running?
36
37
  end
37
38
 
38
39
  def check_exit_status
39
- return if @exit_status.blank? || !@actor.worker_finshed?
40
- debug("worker #{@actor.job_id} startsnotify finished") if @debug_enabled
41
- @actor.notify_finished(@exit_status)
40
+ log_to_file("worker #{@job_id} checking exit status #{@exit_status.inspect}") if @exit_status.present?
41
+ return if @exit_status.blank?
42
+ @job.exit_status = @exit_status
43
+ log_to_file("worker #{@job_id} startsnotify finished")
44
+ @actor.async.notify_finished(@exit_status)
45
+ @timer.cancel
42
46
  end
43
47
 
44
48
  def start_async_deploy(cmd, options)
@@ -72,14 +76,16 @@ module CapistranoMulticonfigParallel
72
76
  end
73
77
 
74
78
  def on_exit(status)
75
- log_to_file "Child process for worker #{@job_id} on_exit disconnected due to error #{status.inspect}"
76
- @exit_status = status
79
+ @exit_status = status.exitstatus
80
+ log_to_file "Child process for worker #{@job_id} on_exit disconnected due to error #{status.inspect} and #{@exit_status.inspect}"
81
+ check_exit_status
77
82
  end
78
83
 
79
84
  def async_exception_handler(*data)
80
85
  log_to_file "Child process for worker #{@job_id} async_exception_handler disconnected due to error #{data.inspect}"
81
86
  io_callback('stderr', data)
82
87
  @exit_status = 1
88
+ check_exit_status
83
89
  end
84
90
 
85
91
  def watch_handler(process)
@@ -21,7 +21,7 @@ module CapistranoMulticonfigParallel
21
21
 
22
22
  def custom_attributes
23
23
  @publisher_channel = "worker_#{@job_id}"
24
- @action = @options['actor_id'].include?('_count') ? 'count' : 'invoke'
24
+ @action = 'invoke'
25
25
  @task = @options['task']
26
26
  end
27
27
 
@@ -27,17 +27,19 @@ module CapistranoMulticonfigParallel
27
27
  # default_headings << 'Total'
28
28
  # default_headings << 'Progress'
29
29
  table = Terminal::Table.new(title: 'Deployment Status Table', headings: default_headings)
30
- if @manager.jobs.present? && message_valid?(message)
30
+
31
+ if @manager.alive? && @manager.jobs.present? && message_valid?(message)
31
32
  count = 0
32
- @manager.jobs.each do |job_id, _job|
33
+ last_job_id = @manager.jobs.keys.last.to_i
34
+ @manager.jobs.each do |job_id, job|
33
35
  count += 1
34
- add_job_to_table(table, job_id, count)
36
+ add_job_to_table(table, job_id, job, count, last_job_id)
35
37
  end
36
38
  end
37
39
  show_terminal_screen(table)
38
40
  rescue => ex
39
- info "Terminal Table client disconnected due to error #{ex.inspect}"
40
- info ex.backtrace
41
+ log_to_file("Terminal Table client disconnected due to error #{ex.inspect}")
42
+ log_to_file(ex.backtrace)
41
43
  terminate
42
44
  end
43
45
 
@@ -62,50 +64,35 @@ module CapistranoMulticonfigParallel
62
64
  @job_manager.condition.signal('completed') if @manager.all_workers_finished?
63
65
  end
64
66
 
65
- def worker_state(worker)
67
+ def worker_state(worker, job)
66
68
  if worker.alive?
67
69
  state = worker.machine.state.to_s
68
- worker.crashed? ? state.red : state.green
70
+ @manager.job_crashed?(job) ? state.red : state.green
69
71
  else
70
72
  'dead'.upcase.red
71
73
  end
72
74
  end
73
75
 
74
- def worker_env_options(processed_job)
75
- worker_optons = ''
76
- processed_job['job_argv'].each do |elem|
77
- worker_optons << "#{elem}\n"
78
- end
79
- worker_optons
80
- end
81
-
82
- def worker_action(processed_job)
83
- processed_job['task_arguments'].present? ? "#{processed_job['action_name']}[#{processed_job['task_arguments'].join(',')}]" : processed_job['action_name']
84
- end
85
-
86
- def worker_stage(processed_job)
87
- processed_job['app_name'].present? ? "#{processed_job['app_name']}\n#{processed_job['env_name']}" : "#{processed_job['env_name']}"
88
- end
89
-
90
- def get_worker_details(job_id, worker)
91
- job = @manager.jobs[job_id]
92
- processed_job = @manager.process_job(job)
93
76
 
77
+ def get_worker_details(job_id, job, worker)
94
78
  {
95
79
  'job_id' => job_id,
96
- 'app_name' => processed_job['app_name'],
97
- 'env_name' => processed_job['env_name'],
98
- 'full_stage' => worker_stage(processed_job),
99
- 'action_name' => worker_action(processed_job),
100
- 'env_options' => worker_env_options(processed_job),
101
- 'task_arguments' => job['task_arguments'],
102
- 'state' => worker_state(worker)
80
+ 'app_name' => job.app,
81
+ 'env_name' => job.stage,
82
+ 'full_stage' => job.job_stage,
83
+ 'action_name' => job.capistrano_action,
84
+ 'env_options' => job.setup_command_line_standard.join("\n"),
85
+ 'task_arguments' => job.task_arguments,
86
+ 'state' => worker_state(worker, job),
87
+ 'processed_job' => job
103
88
  }
104
89
  end
105
90
 
106
- def add_job_to_table(table, job_id, count)
91
+ def add_job_to_table(table, job_id, job, count, last_job_id)
92
+ return unless @manager.alive?
107
93
  worker = @manager.get_worker_for_job(job_id)
108
- details = get_worker_details(job_id, worker)
94
+
95
+ details = get_worker_details(job_id, job, worker)
109
96
 
110
97
  row = [{ value: count.to_s },
111
98
  { value: job_id.to_s },
@@ -117,33 +104,33 @@ module CapistranoMulticonfigParallel
117
104
 
118
105
  # if worker.alive?
119
106
  # row << { value: worker.rake_tasks.size }
120
- # row << { value: worker_progress(details, worker) }
107
+ # row << { value: worker_progress(details['processed_job'], worker) }
121
108
  # else
122
109
  # row << { value: 0 }
123
110
  # row << { value: worker_state(worker) }
124
111
  # end
125
112
  table.add_row(row)
126
- table.add_separator if @manager.jobs.keys.last.to_i != job_id.to_i
113
+ table.add_separator if last_job_id != job_id.to_i
127
114
  end
128
115
 
129
116
  def terminal_clear
130
117
  system('cls') || system('clear') || puts("\e[H\e[2J")
131
118
  end
132
119
 
133
- def worker_progress(_details, worker)
120
+ def worker_progress(processed_job, worker)
134
121
  return worker_state(worker) unless worker.alive?
135
122
  tasks = worker.alive? ? worker.invocation_chain : []
136
123
  current_task = worker.alive? ? worker.machine.state.to_s : ''
137
- show_worker_percent(worker, tasks, current_task)
124
+ show_worker_percent(worker, tasks, current_task, processed_job)
138
125
  end
139
126
 
140
- def show_worker_percent(worker, tasks, current_task)
127
+ def show_worker_percent(worker, tasks, current_task, processed_job)
141
128
  total_tasks = worker.alive? ? tasks.size : nil
142
129
  task_index = worker.alive? ? tasks.index(current_task.to_s).to_i + 1 : 0
143
130
  percent = percent_of(task_index, total_tasks)
144
131
  result = "Progress [#{format('%.2f', percent)}%] (executed #{task_index} of #{total_tasks})"
145
132
  if worker.alive?
146
- worker.crashed? ? result.red : result.green
133
+ @manager.job_crashed?(processed_job) ? result.red : result.green
147
134
  else
148
135
  worker_state(worker)
149
136
  end
@@ -27,6 +27,7 @@ module CapistranoMulticonfigParallel
27
27
  applications = []
28
28
  end
29
29
  applications
30
+
30
31
  end
31
32
 
32
33
  private
@@ -36,15 +37,16 @@ module CapistranoMulticonfigParallel
36
37
  deps.present? && deps.is_a?(Array) ? deps.map(&:stringify_keys) : []
37
38
  end
38
39
 
39
- def all_websites_return_applications_selected
40
+ def available_apps
40
41
  applications = application_dependencies.map { |hash| hash['app'].camelcase }
41
42
  applications << 'all_frameworks'
43
+ applications
44
+ end
45
+
46
+ def all_websites_return_applications_selected
42
47
  interactive_menu = CapistranoMulticonfigParallel::InteractiveMenu.new
43
- applications_selected = interactive_menu.show_all_websites_interactive_menu(applications)
44
- applications_selected = applications_selected.gsub("\r\n", '') if applications_selected.present?
45
- applications_selected = applications_selected.delete("\n") if applications_selected.present?
46
- applications_selected = applications_selected.split(',') if applications_selected.present?
47
- applications_selected.present? ? applications_selected : []
48
+ applications_selected = interactive_menu.show_all_websites_interactive_menu(available_apps)
49
+ applications_selected.present? ? applications_selected.split(',') : []
48
50
  end
49
51
 
50
52
  def add_dependency_app(app_to_deploy, apps_dependencies, applications_to_deploy)
@@ -1,6 +1,9 @@
1
+ require_relative '../helpers/application_helper'
1
2
  module CapistranoMulticonfigParallel
2
3
  # methods used for the interactive menu where are listed all aplications
3
4
  class InteractiveMenu
5
+ include CapistranoMulticonfigParallel::ApplicationHelper
6
+
4
7
  def show_all_websites_interactive_menu(applications)
5
8
  msg = ''
6
9
  choices = []
@@ -15,7 +18,7 @@ module CapistranoMulticonfigParallel
15
18
  result += "#{option_name},"
16
19
  end
17
20
  print "#{msg}\n"
18
- result
21
+ strip_characters_from_string(result)
19
22
  end
20
23
 
21
24
  def confirm_option_selected
@@ -0,0 +1,73 @@
1
+ require 'fileutils'
2
+ require_relative '../helpers/application_helper'
3
+ module CapistranoMulticonfigParallel
4
+ # class used to find application dependencies
5
+ class Job
6
+ include FileUtils
7
+ include CapistranoMulticonfigParallel::ApplicationHelper
8
+
9
+ attr_accessor :id, :app, :stage, :action, :task_arguments, :env_options, :status, :exit_status
10
+ def initialize(options)
11
+ @id = SecureRandom.uuid
12
+ @app = options.fetch('app', '')
13
+ @stage = options.fetch('stage', '')
14
+ @action = options.fetch('action', '')
15
+ @task_arguments = options.fetch('task_arguments', [])
16
+ @env_options = {}
17
+ @env_options = options.fetch('env_options', {}).each do |key, value|
18
+ @env_options[key] = value if value.present? && !filtered_env_keys.include?(key)
19
+ end
20
+ @env_options["#{CapistranoMulticonfigParallel::ENV_KEY_JOB_ID}"] = @id
21
+ end
22
+
23
+ def filtered_env_keys
24
+ %w(STAGES ACTION)
25
+ end
26
+
27
+ def finished?
28
+ status == 'finished'
29
+ end
30
+
31
+ def job_stage
32
+ @app.present? ? "#{@app}:#{@stage}" : "#{@stage}"
33
+ end
34
+
35
+ def capistrano_action(action = @action)
36
+ argv = @task_arguments.present? ? "[#{@task_arguments}]" : ''
37
+ "#{action}#{argv}"
38
+ end
39
+
40
+ def setup_command_line_standard(*args)
41
+ array_options = []
42
+ @env_options.each do |key, value|
43
+ array_options << "#{key}=#{value}" if value.present?
44
+ end
45
+ array_options << '--trace' if app_debug_enabled?
46
+ args.each do |arg|
47
+ array_options << arg if arg.present?
48
+ end
49
+ array_options
50
+ end
51
+
52
+ def build_capistrano_task(action = nil, env = [])
53
+ action = action.present? ? action : @action
54
+ environment_options = setup_command_line_standard(env).join(" ")
55
+ "cd #{detect_root} && RAILS_ENV=#{@stage} bundle exec multi_cap #{job_stage} #{capistrano_action(action)} #{environment_options}"
56
+ end
57
+
58
+
59
+ def execute_standard_deploy(action = nil)
60
+ command = build_capistrano_task(action)
61
+ puts("\n\n\n Executing '#{command}' \n\n\n .")
62
+ sh("#{command}")
63
+ rescue => ex
64
+ log_error(ex)
65
+ execute_standard_deploy('deploy:rollback') if action.blank? && @name == 'deploy'
66
+ end
67
+
68
+ def to_s
69
+ self.to_json
70
+ end
71
+
72
+ end
73
+ end
@@ -70,7 +70,7 @@ module CapistranoMulticonfigParallel
70
70
  end
71
71
 
72
72
  def rake_actor_id
73
- @env['count_rake'].present? ? "rake_worker_#{job_id}_count" : "rake_worker_#{job_id}"
73
+ "rake_worker_#{job_id}"
74
74
  end
75
75
  end
76
76
  end
@@ -55,7 +55,7 @@ module CapistranoMulticonfigParallel
55
55
 
56
56
  def format_error(error)
57
57
  JSON.pretty_generate(class_name: error.class,
58
- message: error.respond_to?(:message) ? error.message : error.inspect,
58
+ message: error.respond_to?(:message) ? "#{error.message}#{error.message.class}" : error.inspect,
59
59
  backtrace: error.respond_to?(:backtrace) ? error.backtrace.join("\n\n") : '')
60
60
  end
61
61
 
@@ -7,8 +7,8 @@ module CapistranoMulticonfigParallel
7
7
  # module used for generating the version
8
8
  module VERSION
9
9
  MAJOR = 0
10
- MINOR = 18
11
- TINY = 2
10
+ MINOR = 19
11
+ TINY = 0
12
12
  PRE = nil
13
13
 
14
14
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capistrano_multiconfig_parallel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.2
4
+ version: 0.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - bogdanRada
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-04 00:00:00.000000000 Z
11
+ date: 2015-12-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: celluloid-pmap
@@ -39,7 +39,7 @@ dependencies:
39
39
  version: '0.0'
40
40
  - - ">="
41
41
  - !ruby/object:Gem::Version
42
- version: 0.0.21
42
+ version: 0.0.22
43
43
  type: :runtime
44
44
  prerelease: false
45
45
  version_requirements: !ruby/object:Gem::Requirement
@@ -49,7 +49,7 @@ dependencies:
49
49
  version: '0.0'
50
50
  - - ">="
51
51
  - !ruby/object:Gem::Version
52
- version: 0.0.21
52
+ version: 0.0.22
53
53
  - !ruby/object:Gem::Dependency
54
54
  name: composable_state_machine
55
55
  requirement: !ruby/object:Gem::Requirement
@@ -632,9 +632,9 @@ files:
632
632
  - lib/capistrano_multiconfig_parallel/classes/dependency_tracker.rb
633
633
  - lib/capistrano_multiconfig_parallel/classes/input_stream.rb
634
634
  - lib/capistrano_multiconfig_parallel/classes/interactive_menu.rb
635
+ - lib/capistrano_multiconfig_parallel/classes/job.rb
635
636
  - lib/capistrano_multiconfig_parallel/classes/output_stream.rb
636
637
  - lib/capistrano_multiconfig_parallel/classes/rake_hook_actor.rb
637
- - lib/capistrano_multiconfig_parallel/classes/standard_deploy.rb
638
638
  - lib/capistrano_multiconfig_parallel/cli.rb
639
639
  - lib/capistrano_multiconfig_parallel/configuration/default.yml
640
640
  - lib/capistrano_multiconfig_parallel/helpers/application_helper.rb
@@ -1,50 +0,0 @@
1
- require 'fileutils'
2
- require_relative '../helpers/application_helper'
3
- module CapistranoMulticonfigParallel
4
- # class used to find application dependencies
5
- class StandardDeploy
6
- include FileUtils
7
- include CapistranoMulticonfigParallel::ApplicationHelper
8
-
9
- attr_reader :app, :stage, :action, :task_arguments, :env_options
10
- def initialize(options)
11
- @app = options.fetch('app', '')
12
- @stage = options.fetch('env', '')
13
- @action = options.fetch('action', '')
14
- @task_arguments = options.fetch('task_arguments:', [])
15
- @env_options = options.fetch('env_options', {})
16
- execute_standard_deploy
17
- end
18
-
19
- def job_stage
20
- @app.present? ? "#{@app}:#{@stage}" : "#{@stage}"
21
- end
22
-
23
- def capistrano_action(action)
24
- argv = task_arguments.present? ? "[#{@task_arguments}]" : ''
25
- "#{action}#{argv}"
26
- end
27
-
28
- def setup_command_line_standard(options)
29
- opts = ''
30
- options.each do |key, value|
31
- opts << "#{key}=#{value} " if value.present?
32
- end
33
- opts
34
- end
35
-
36
- def build_capistrano_task(action = @action, env = {})
37
- environment_options = setup_command_line_standard(@env_options.merge(env))
38
- "bundle exec cap #{job_stage} #{capistrano_action(action)} #{environment_options} --trace"
39
- end
40
-
41
- def execute_standard_deploy(action = @action)
42
- command = build_capistrano_task(action)
43
- puts("\n\n\n Executing '#{command}' \n\n\n .")
44
- sh("#{command}")
45
- rescue => ex
46
- log_error(ex)
47
- execute_standard_deploy('deploy:rollback') if action.blank? && @name == 'deploy'
48
- end
49
- end
50
- end