capistrano_multiconfig_parallel 0.18.2 → 0.19.0

Sign up to get free protection for your applications and to get access to all the features.
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