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 +4 -4
- data/capistrano_multiconfig_parallel.gemspec +1 -1
- data/lib/capistrano_multiconfig_parallel/application.rb +7 -13
- data/lib/capistrano_multiconfig_parallel/celluloid/celluloid_manager.rb +48 -85
- data/lib/capistrano_multiconfig_parallel/celluloid/celluloid_worker.rb +18 -81
- data/lib/capistrano_multiconfig_parallel/celluloid/child_process.rb +16 -10
- data/lib/capistrano_multiconfig_parallel/celluloid/rake_worker.rb +1 -1
- data/lib/capistrano_multiconfig_parallel/celluloid/terminal_table.rb +28 -41
- data/lib/capistrano_multiconfig_parallel/classes/dependency_tracker.rb +8 -6
- data/lib/capistrano_multiconfig_parallel/classes/interactive_menu.rb +4 -1
- data/lib/capistrano_multiconfig_parallel/classes/job.rb +73 -0
- data/lib/capistrano_multiconfig_parallel/classes/rake_hook_actor.rb +1 -1
- data/lib/capistrano_multiconfig_parallel/helpers/core_helper.rb +1 -1
- data/lib/capistrano_multiconfig_parallel/version.rb +2 -2
- metadata +5 -5
- data/lib/capistrano_multiconfig_parallel/classes/standard_deploy.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c16a337397b541e4bed172a5abda0ad5b625d81b
|
4
|
+
data.tar.gz: 2d452cdd7abcb3b9f7cbad1e134b6a6199cab0e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
+
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|
|
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
|
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
|
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
|
-
|
61
|
-
|
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]
|
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
|
109
|
-
|
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(
|
113
|
-
return unless
|
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[
|
101
|
+
@job_to_condition[job.id] = hash_conditions
|
119
102
|
end
|
120
103
|
|
121
|
-
def mark_completed_remaining_tasks(
|
122
|
-
return unless
|
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[
|
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(
|
134
|
-
return
|
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(
|
137
|
-
confirm_task_approval(result, task,
|
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,
|
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 #{
|
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,
|
156
|
+
def confirm_task_approval(result, task, job = nil)
|
173
157
|
return unless result.present?
|
174
|
-
result = print_confirm_task_approvall(result, task,
|
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
|
-
|
180
|
-
|
181
|
-
|
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
|
174
|
+
@job_to_worker[job.id]
|
191
175
|
else
|
192
|
-
@job_to_worker[job
|
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
|
-
|
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
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
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 = @
|
201
|
+
actor = @job_to_worker[job.id]
|
243
202
|
status = actor.status
|
244
203
|
else
|
245
|
-
actor = @
|
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
|
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? ||
|
261
|
-
return unless job
|
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 =
|
264
|
-
|
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(
|
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
|
-
|
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
|
-
|
100
|
-
|
101
|
-
@
|
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 @
|
121
|
-
@manager.dispatch_new_job(@job
|
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
|
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(
|
227
|
-
@
|
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
|
173
|
+
if exit_status != 0
|
237
174
|
log_to_file("worker #{job_id} tries to terminate")
|
238
|
-
abort(CapistranoMulticonfigParallel::CelluloidWorker::TaskFailed
|
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, :
|
10
|
+
attr_accessor :options, :actor, :job_id, :exit_status, :pid, :process, :job
|
11
11
|
|
12
|
-
|
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 = @
|
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
|
-
|
40
|
-
|
41
|
-
@
|
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
|
-
|
76
|
-
@
|
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)
|
@@ -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
|
-
|
30
|
+
|
31
|
+
if @manager.alive? && @manager.jobs.present? && message_valid?(message)
|
31
32
|
count = 0
|
32
|
-
@manager.jobs.
|
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
|
-
|
40
|
-
|
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
|
-
|
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' =>
|
97
|
-
'env_name' =>
|
98
|
-
'full_stage' =>
|
99
|
-
'action_name' =>
|
100
|
-
'env_options' =>
|
101
|
-
'task_arguments' => job
|
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
|
-
|
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
|
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(
|
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
|
-
|
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
|
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(
|
44
|
-
applications_selected
|
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
|
@@ -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
|
|
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.
|
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-
|
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.
|
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.
|
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
|