aws-flow 2.3.1 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +8 -8
- data/aws-flow.gemspec +3 -2
- data/bin/aws-flow-ruby +1 -1
- data/bin/aws-flow-utils +5 -0
- data/lib/aws/decider.rb +7 -0
- data/lib/aws/decider/async_retrying_executor.rb +1 -1
- data/lib/aws/decider/data_converter.rb +161 -0
- data/lib/aws/decider/decider.rb +27 -14
- data/lib/aws/decider/flow_defaults.rb +28 -0
- data/lib/aws/decider/implementation.rb +0 -1
- data/lib/aws/decider/options.rb +2 -2
- data/lib/aws/decider/starter.rb +207 -0
- data/lib/aws/decider/task_poller.rb +4 -4
- data/lib/aws/decider/utilities.rb +38 -0
- data/lib/aws/decider/version.rb +1 -1
- data/lib/aws/decider/worker.rb +8 -7
- data/lib/aws/decider/workflow_definition_factory.rb +1 -1
- data/lib/aws/runner.rb +146 -65
- data/lib/aws/templates.rb +4 -0
- data/lib/aws/templates/activity.rb +69 -0
- data/lib/aws/templates/base.rb +87 -0
- data/lib/aws/templates/default.rb +146 -0
- data/lib/aws/templates/starter.rb +256 -0
- data/lib/aws/utils.rb +270 -0
- data/spec/aws/decider/integration/activity_spec.rb +7 -1
- data/spec/aws/decider/integration/data_converter_spec.rb +39 -0
- data/spec/aws/decider/integration/integration_spec.rb +12 -5
- data/spec/aws/decider/integration/options_spec.rb +23 -9
- data/spec/aws/decider/integration/starter_spec.rb +209 -0
- data/spec/aws/decider/unit/data_converter_spec.rb +276 -0
- data/spec/aws/decider/unit/decider_spec.rb +1360 -1386
- data/spec/aws/decider/unit/options_spec.rb +21 -22
- data/spec/aws/decider/unit/retry_spec.rb +8 -0
- data/spec/aws/decider/unit/starter_spec.rb +159 -0
- data/spec/aws/runner/integration/runner_integration_spec.rb +2 -3
- data/spec/aws/runner/unit/runner_unit_spec.rb +128 -38
- data/spec/aws/templates/unit/activity_spec.rb +89 -0
- data/spec/aws/templates/unit/base_spec.rb +72 -0
- data/spec/aws/templates/unit/default_spec.rb +141 -0
- data/spec/aws/templates/unit/starter_spec.rb +271 -0
- data/spec/spec_helper.rb +9 -11
- metadata +41 -4
@@ -136,6 +136,7 @@ module AWS
|
|
136
136
|
#
|
137
137
|
def initialize(service, domain, task_list, activity_definition_map, executor, options=nil)
|
138
138
|
@service = service
|
139
|
+
@service_opts = @service.config.to_h
|
139
140
|
@domain = domain
|
140
141
|
@task_list = task_list
|
141
142
|
@activity_definition_map = activity_definition_map
|
@@ -319,10 +320,9 @@ module AWS
|
|
319
320
|
# Since we can't change the pool of an already existing NetHttpHandler,
|
320
321
|
# we also create a new NetHttpHandler in order to use the new pool.
|
321
322
|
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
@service = AWS::SimpleWorkflow.new(options).client
|
323
|
+
@service_opts[:connection_pool] = AWS::Core::Http::ConnectionPool.build(@service_opts[:http_handler].pool.options)
|
324
|
+
@service_opts[:http_handler] = AWS::Core::Http::NetHttpHandler.new(@service_opts)
|
325
|
+
@service = @service.with_options(@service_opts)
|
326
326
|
|
327
327
|
begin
|
328
328
|
begin
|
@@ -92,9 +92,28 @@ module AWS
|
|
92
92
|
client_options
|
93
93
|
end
|
94
94
|
|
95
|
+
# This method is used to register a domain with the Simple Workflow
|
96
|
+
# Service.
|
97
|
+
# @param name Name of the domain
|
98
|
+
# @param retention Workflow execution retention period in days
|
95
99
|
# @api private
|
100
|
+
def self.register_domain(name, retention=nil)
|
101
|
+
swf = AWS::SimpleWorkflow.new
|
102
|
+
retention ||= FlowConstants::RETENTION_DEFAULT
|
103
|
+
begin
|
104
|
+
swf.client.register_domain({
|
105
|
+
name: name.to_s,
|
106
|
+
workflow_execution_retention_period_in_days: retention.to_s
|
107
|
+
})
|
108
|
+
rescue AWS::SimpleWorkflow::Errors::DomainAlreadyExistsFault => e
|
109
|
+
# possible log an INFO/WARN if the domain already exists.
|
110
|
+
end
|
111
|
+
return AWS::SimpleWorkflow::Domain.new(name.to_s)
|
112
|
+
end
|
113
|
+
|
96
114
|
# This method is used to truncate Activity and Workflow exceptions to
|
97
115
|
# fit them into responses to the SWF service.
|
116
|
+
# @api private
|
98
117
|
def self.check_and_truncate_exception error, converter
|
99
118
|
|
100
119
|
# serialize the exception so that we can check the actual size of the
|
@@ -228,6 +247,25 @@ module AWS
|
|
228
247
|
return true
|
229
248
|
end
|
230
249
|
|
250
|
+
# @api private
|
251
|
+
def self.add_workflow_worker_to_spec(spec, klass, tasklist)
|
252
|
+
add_worker_to_spec(spec, 'workflow', klass, tasklist)
|
253
|
+
end
|
254
|
+
|
255
|
+
# @api private
|
256
|
+
def self.add_activity_worker_to_spec(spec, klass, tasklist)
|
257
|
+
add_worker_to_spec(spec, 'activity', klass, tasklist)
|
258
|
+
end
|
259
|
+
|
260
|
+
# @api private
|
261
|
+
def self.add_worker_to_spec(spec, type, klass, tasklist)
|
262
|
+
spec["#{type}_workers"] << {
|
263
|
+
"#{type}_classes" => ["#{klass}"],
|
264
|
+
"task_list" => "#{tasklist}"
|
265
|
+
}
|
266
|
+
spec
|
267
|
+
end
|
268
|
+
|
231
269
|
# @api private
|
232
270
|
module SelfMethods
|
233
271
|
# @api private
|
data/lib/aws/decider/version.rb
CHANGED
data/lib/aws/decider/worker.rb
CHANGED
@@ -287,12 +287,13 @@ module AWS
|
|
287
287
|
@activity_type_options = []
|
288
288
|
@options = Utilities::interpret_block_for_options(WorkerOptions, block)
|
289
289
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
290
|
+
if @options
|
291
|
+
@logger = @options.logger || Utilities::LogFactory.make_logger(self)
|
292
|
+
@options.logger ||= @logger
|
293
|
+
max_workers = @options.execution_workers
|
294
|
+
@options.use_forking = false if (max_workers && max_workers.zero?)
|
295
|
+
end
|
296
|
+
max_workers = 20 if (max_workers.nil?)
|
296
297
|
@executor = ForkingExecutor.new(
|
297
298
|
:max_workers => max_workers,
|
298
299
|
:logger => @logger
|
@@ -338,7 +339,7 @@ module AWS
|
|
338
339
|
registration_difference = default_options.sort.to_a - previous_registration.sort.to_a
|
339
340
|
|
340
341
|
unless registration_difference.empty?
|
341
|
-
raise "There is a difference between the types you have registered previously and the types you are currently registering, but you haven't changed the version. These new changes will not be picked up. In particular, these options are different #{Hash[registration_difference]}"
|
342
|
+
raise "Activity [#{activity_type_options[:name]}]: There is a difference between the types you have registered previously and the types you are currently registering, but you haven't changed the version. These new changes will not be picked up. In particular, these options are different #{Hash[registration_difference]}"
|
342
343
|
end
|
343
344
|
# Purposefully eaten up, the alternative is to check first, and who
|
344
345
|
# wants to do two trips when one will do?
|
data/lib/aws/runner.rb
CHANGED
@@ -41,6 +41,7 @@ module AWS
|
|
41
41
|
|
42
42
|
# Import the necessary gems to run Ruby Flow code.
|
43
43
|
require 'aws/decider'
|
44
|
+
require 'aws/templates'
|
44
45
|
include AWS::Flow
|
45
46
|
require 'json'
|
46
47
|
require 'optparse'
|
@@ -51,21 +52,16 @@ module AWS
|
|
51
52
|
# @api private
|
52
53
|
def self.setup_domain(json_config)
|
53
54
|
|
54
|
-
|
55
|
+
set_user_agent(json_config)
|
56
|
+
|
57
|
+
# If domain is not provided, use the default ruby flow domain
|
58
|
+
domain = json_config['domain'] || { 'name' => FlowConstants.defaults[:domain] }
|
55
59
|
|
56
|
-
domain = json_config['domain']
|
57
60
|
# If retention period is not provided, default it to 7 days
|
58
61
|
retention = domain['retention_in_days'] || FlowConstants::RETENTION_DEFAULT
|
59
62
|
|
60
|
-
|
61
|
-
|
62
|
-
name: domain['name'],
|
63
|
-
workflow_execution_retention_period_in_days: retention.to_s
|
64
|
-
})
|
65
|
-
rescue AWS::SimpleWorkflow::Errors::DomainAlreadyExistsFault => e
|
66
|
-
# possible log an INFO/WARN if the domain already exists.
|
67
|
-
end
|
68
|
-
return AWS::SimpleWorkflow::Domain.new( domain['name'] )
|
63
|
+
AWS::Flow::Utilities.register_domain(domain['name'], retention.to_s)
|
64
|
+
|
69
65
|
end
|
70
66
|
|
71
67
|
# @api private
|
@@ -104,26 +100,19 @@ module AWS
|
|
104
100
|
classes
|
105
101
|
end
|
106
102
|
|
107
|
-
# Used to add implementations to workers; see [get_classes] for more
|
108
|
-
# information.
|
109
|
-
#
|
110
|
-
# @api private
|
111
|
-
def self.add_implementations(worker, json_fragment, what)
|
112
|
-
classes = get_classes(json_fragment, what)
|
113
|
-
classes.each { |c| worker.add_implementation(c) }
|
114
|
-
end
|
115
|
-
|
116
103
|
# Spawns the workers.
|
117
104
|
#
|
118
105
|
# @api private
|
119
106
|
def self.spawn_and_start_workers(json_fragment, process_name, worker)
|
120
107
|
workers = []
|
121
108
|
num_of_workers = json_fragment['number_of_workers'] || FlowConstants::NUM_OF_WORKERS_DEFAULT
|
109
|
+
should_register = true
|
122
110
|
num_of_workers.times do
|
123
111
|
workers << fork do
|
124
112
|
set_process_name(process_name)
|
125
|
-
worker.start()
|
113
|
+
worker.start(should_register)
|
126
114
|
end
|
115
|
+
should_register = false
|
127
116
|
end
|
128
117
|
workers
|
129
118
|
end
|
@@ -153,7 +142,7 @@ module AWS
|
|
153
142
|
#
|
154
143
|
# json_config: the content of the config
|
155
144
|
#
|
156
|
-
# what: what should loaded. This is a hash expected to contain two keys:
|
145
|
+
# what: what should be loaded. This is a hash expected to contain two keys:
|
157
146
|
#
|
158
147
|
# - :default_file : the file to load unless a specific list is provided
|
159
148
|
#
|
@@ -177,34 +166,92 @@ module AWS
|
|
177
166
|
# section of [the runner specification file][], or that are loaded from
|
178
167
|
# `require` statements in the `workflows.rb` file.
|
179
168
|
#
|
169
|
+
# If the 'activity' classes are regular ruby classes, this method will
|
170
|
+
# create a proxy AWS::Flow::Activities class for each regular ruby class
|
171
|
+
# loaded and add the proxy implementation to the ActivityWorker.
|
172
|
+
#
|
180
173
|
# @api private
|
181
|
-
def self.start_activity_workers(swf, domain = nil,
|
174
|
+
def self.start_activity_workers(swf, domain = nil, json_config)
|
182
175
|
workers = []
|
183
|
-
# load all classes for the activities
|
184
|
-
load_files(config_path, json_config, {config_key: 'activity_paths',
|
185
|
-
default_file: File.join('flow', 'activities.rb')})
|
186
176
|
domain = setup_domain(json_config) if domain.nil?
|
187
177
|
|
178
|
+
# This will be used later to start default workflow workers. If the
|
179
|
+
# 'activity_workers' and 'default_workers' keys are not specified in the
|
180
|
+
# json spec, then we don't start any default workers. Hence this value
|
181
|
+
# is defaulted to 0.
|
182
|
+
number_of_default_workers = 0
|
183
|
+
|
188
184
|
# TODO: logger
|
189
185
|
# start the workers for each spec
|
190
|
-
json_config['activity_workers']
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
186
|
+
if json_config['activity_workers']
|
187
|
+
json_config['activity_workers'].each do |w|
|
188
|
+
# If number of forks is not provided, it will automatically default to 20
|
189
|
+
# within the ActivityWorker
|
190
|
+
fork_count = w['number_of_forks_per_worker']
|
191
|
+
task_list = expand_task_list(w['task_list']) if w['task_list']
|
192
|
+
|
193
|
+
# Get activity classes
|
194
|
+
classes = get_classes(w, {config_key: 'activity_classes',
|
195
|
+
clazz: AWS::Flow::Activities})
|
196
|
+
|
197
|
+
# If task_list is not provided, use the name of the first class as the
|
198
|
+
# task_list for this worker
|
199
|
+
task_list ||= "#{classes.first}"
|
200
|
+
|
201
|
+
# Create a worker
|
202
|
+
worker = ActivityWorker.new(swf.client, domain, task_list) {{ execution_workers: fork_count }}
|
203
|
+
|
204
|
+
classes.each do |c|
|
205
|
+
c = AWS::Flow::Templates.make_activity_class(c) unless c.is_a?(AWS::Flow::Activities)
|
206
|
+
worker.add_implementation(c)
|
207
|
+
end
|
208
|
+
|
209
|
+
# We add 1 default worker for each activity worker.
|
210
|
+
number_of_default_workers += w['number_of_workers'] || FlowConstants::NUM_OF_WORKERS_DEFAULT
|
211
|
+
|
212
|
+
# start as many workers as desired in child processes
|
213
|
+
workers << spawn_and_start_workers(w, "activity-worker", worker)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Create the config for default workers if it's not passed in the
|
217
|
+
# json_config
|
218
|
+
if json_config['default_workers'].nil? || json_config['default_workers']['number_of_workers'].nil?
|
219
|
+
json_config['default_workers'] = {
|
220
|
+
'number_of_workers' => number_of_default_workers
|
221
|
+
}
|
222
|
+
end
|
203
223
|
end
|
204
224
|
|
225
|
+
# Start the default workflow workers
|
226
|
+
workers << start_default_workers(swf, domain, json_config)
|
227
|
+
|
205
228
|
return workers
|
206
229
|
end
|
207
230
|
|
231
|
+
# Starts workflow workers for the default workflow type 'FlowDefaultWorkflowRuby'.
|
232
|
+
# If 'default_workers' key is not set in the json spec, we set the number
|
233
|
+
# of workers equal to the number of activity workers
|
234
|
+
# Default workers are used to for processing workflow templates
|
235
|
+
#
|
236
|
+
# @api private
|
237
|
+
def self.start_default_workers(swf, domain = nil, json_config)
|
238
|
+
workers = []
|
239
|
+
domain = setup_domain(json_config) if domain.nil?
|
240
|
+
|
241
|
+
if json_config['default_workers']
|
242
|
+
# Also register the default result activity type in the given domain
|
243
|
+
AWS::Flow::Templates.register_default_result_activity(domain)
|
244
|
+
|
245
|
+
klass = AWS::Flow::Templates.default_workflow
|
246
|
+
task_list = FlowConstants.defaults[:task_list]
|
247
|
+
# Create a worker
|
248
|
+
worker = WorkflowWorker.new(swf.client, domain, task_list, klass)
|
249
|
+
# This will take care of both registering and starting the default workers
|
250
|
+
workers << spawn_and_start_workers(json_config['default_workers'], "default-worker", worker)
|
251
|
+
end
|
252
|
+
workers
|
253
|
+
end
|
254
|
+
|
208
255
|
# Starts the workflow workers.
|
209
256
|
#
|
210
257
|
# The workflows run by the workers consist of each class that extends
|
@@ -213,37 +260,42 @@ module AWS
|
|
213
260
|
# `require` statements in the `workflows.rb` file.
|
214
261
|
#
|
215
262
|
# @api private
|
216
|
-
def self.start_workflow_workers(swf, domain = nil,
|
263
|
+
def self.start_workflow_workers(swf, domain = nil, json_config)
|
217
264
|
workers = []
|
218
|
-
# load all the classes for the workflows
|
219
|
-
load_files(config_path, json_config, {config_key: 'workflow_paths',
|
220
|
-
default_file: File.join('flow', 'workflows.rb')})
|
221
265
|
domain = setup_domain(json_config) if domain.nil?
|
222
266
|
|
223
267
|
# TODO: logger
|
224
268
|
# start the workers for each spec
|
225
|
-
json_config['workflow_workers']
|
226
|
-
|
269
|
+
if json_config['workflow_workers']
|
270
|
+
json_config['workflow_workers'].each do |w|
|
271
|
+
task_list = expand_task_list(w['task_list'])
|
272
|
+
|
273
|
+
# Get workflow classes
|
274
|
+
classes = get_classes(w, {config_key: 'workflow_classes',
|
275
|
+
clazz: AWS::Flow::Workflows})
|
227
276
|
|
228
|
-
|
229
|
-
|
230
|
-
add_implementations(worker, w, {config_key: 'workflow_classes',
|
231
|
-
clazz: AWS::Flow::Workflows})
|
277
|
+
# Create a worker
|
278
|
+
worker = WorkflowWorker.new(swf.client, domain, task_list, *classes)
|
232
279
|
|
233
|
-
|
234
|
-
|
280
|
+
# Start as many workers as desired in child processes
|
281
|
+
workers << spawn_and_start_workers(w, "workflow-worker", worker)
|
282
|
+
end
|
235
283
|
end
|
236
284
|
|
237
285
|
return workers
|
238
286
|
end
|
239
287
|
|
240
288
|
# @api private
|
241
|
-
def self.
|
289
|
+
def self.set_user_agent(json_config)
|
242
290
|
# set the UserAgent prefix for all clients
|
243
291
|
if json_config['user_agent_prefix'] then
|
244
292
|
AWS.config(user_agent_prefix: json_config['user_agent_prefix'])
|
245
293
|
end
|
294
|
+
end
|
246
295
|
|
296
|
+
# @api private
|
297
|
+
def self.create_service_client(json_config)
|
298
|
+
set_user_agent(json_config)
|
247
299
|
swf = AWS::SimpleWorkflow.new
|
248
300
|
end
|
249
301
|
|
@@ -251,18 +303,36 @@ module AWS
|
|
251
303
|
# worker processes.
|
252
304
|
#
|
253
305
|
# @api private
|
254
|
-
def self.start_workers(domain = nil,
|
306
|
+
def self.start_workers(domain = nil, json_config)
|
307
|
+
|
255
308
|
workers = []
|
256
309
|
|
257
310
|
swf = create_service_client(json_config)
|
258
311
|
|
259
|
-
workers << start_activity_workers(swf, domain,
|
260
|
-
workers << start_workflow_workers(swf, domain,
|
312
|
+
workers << start_activity_workers(swf, domain, json_config)
|
313
|
+
workers << start_workflow_workers(swf, domain, json_config)
|
261
314
|
|
262
315
|
# needed to avoid returning nested arrays based on the calls above
|
263
316
|
workers.flatten!
|
264
317
|
end
|
265
318
|
|
319
|
+
# Loads activity and workflow classes
|
320
|
+
#
|
321
|
+
# config_path: the path where the config file is, to be able to
|
322
|
+
# resolve relative references
|
323
|
+
#
|
324
|
+
# json_config: the content of the config
|
325
|
+
#
|
326
|
+
# @api private
|
327
|
+
def self.load_classes(config_path, json_config)
|
328
|
+
# load all classes for the activities
|
329
|
+
load_files(config_path, json_config, {config_key: 'activity_paths',
|
330
|
+
default_file: File.join('flow', 'activities.rb')})
|
331
|
+
# load all the classes for the workflows
|
332
|
+
load_files(config_path, json_config, {config_key: 'workflow_paths',
|
333
|
+
default_file: File.join('flow', 'workflows.rb')})
|
334
|
+
end
|
335
|
+
|
266
336
|
# Sets up forwarding of signals to child processes to facilitate and
|
267
337
|
# support orderly shutdown.
|
268
338
|
#
|
@@ -325,23 +395,35 @@ module AWS
|
|
325
395
|
return options
|
326
396
|
end
|
327
397
|
|
398
|
+
|
328
399
|
#
|
329
|
-
# Invoked from the
|
400
|
+
# Invoked from code. This is a helper method that can be used to start the
|
401
|
+
# runner from code. This is especially helpful for debugging purposes.
|
330
402
|
#
|
331
|
-
#
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
config = load_config_json( config_path )
|
336
|
-
add_dir_to_load_path( Pathname.new(config_path).dirname )
|
337
|
-
domain = setup_domain(config)
|
338
|
-
workers = start_workers(domain, config_path, config)
|
403
|
+
# worker_spec: Hash representation of the json worker spec
|
404
|
+
#
|
405
|
+
def self.run(worker_spec)
|
406
|
+
workers = start_workers(worker_spec)
|
339
407
|
setup_signal_handling(workers)
|
340
408
|
|
341
409
|
# Hang there until killed: this process is used to relay signals to
|
342
410
|
# children to support and facilitate an orderly shutdown.
|
343
411
|
wait_for_child_processes(workers)
|
344
412
|
end
|
413
|
+
|
414
|
+
#
|
415
|
+
# Invoked from the shell.
|
416
|
+
#
|
417
|
+
# @api private
|
418
|
+
def self.main
|
419
|
+
options = parse_command_line
|
420
|
+
config_path = options[:file]
|
421
|
+
worker_spec = load_config_json(config_path)
|
422
|
+
add_dir_to_load_path(Pathname.new(config_path).dirname)
|
423
|
+
load_classes(config_path, worker_spec)
|
424
|
+
run(worker_spec)
|
425
|
+
end
|
426
|
+
|
345
427
|
end
|
346
428
|
end
|
347
429
|
end
|
@@ -350,4 +432,3 @@ if __FILE__ == $0
|
|
350
432
|
AWS::Flow::Runner.main()
|
351
433
|
end
|
352
434
|
|
353
|
-
|