aws-flow 2.4.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +8 -8
  2. data/aws-flow.gemspec +1 -0
  3. data/lib/aws/decider.rb +0 -1
  4. data/lib/aws/decider/starter.rb +6 -8
  5. data/lib/aws/decider/utilities.rb +6 -0
  6. data/lib/aws/decider/version.rb +1 -1
  7. data/lib/aws/decider/worker.rb +6 -0
  8. data/lib/aws/flow/future.rb +86 -6
  9. data/lib/aws/flow/implementation.rb +84 -13
  10. data/lib/aws/runner.rb +1 -1
  11. data/lib/aws/templates.rb +2 -0
  12. data/lib/aws/templates/activity.rb +41 -0
  13. data/lib/aws/templates/default.rb +11 -8
  14. data/lib/aws/templates/result.rb +183 -0
  15. data/lib/aws/templates/starter.rb +152 -226
  16. data/lib/aws/templates/utilities.rb +59 -0
  17. data/spec/aws/decider/integration/activity_spec.rb +1 -0
  18. data/spec/aws/decider/integration/options_spec.rb +16 -9
  19. data/spec/aws/decider/integration/starter_spec.rb +6 -7
  20. data/spec/aws/decider/unit/starter_spec.rb +2 -2
  21. data/spec/aws/decider/unit/worker_spec.rb +42 -0
  22. data/spec/aws/flow/{async_backtrace_spec.rb → unit/async_backtrace_spec.rb} +0 -0
  23. data/spec/aws/flow/{async_scope_spec.rb → unit/async_scope_spec.rb} +0 -0
  24. data/spec/aws/flow/{begin_rescue_ensure_spec.rb → unit/begin_rescue_ensure_spec.rb} +0 -0
  25. data/spec/aws/flow/unit/external_condition_variable_spec.rb +59 -0
  26. data/spec/aws/flow/{external_task_spec.rb → unit/external_task_spec.rb} +0 -0
  27. data/spec/aws/flow/{factories.rb → unit/factories.rb} +0 -0
  28. data/spec/aws/flow/{fiber_condition_variable_spec.rb → unit/fiber_condition_variable_spec.rb} +0 -0
  29. data/spec/aws/flow/{fiber_spec.rb → unit/fiber_spec.rb} +0 -0
  30. data/spec/aws/flow/{flow_spec.rb → unit/flow_spec.rb} +0 -0
  31. data/spec/aws/flow/{future_spec.rb → unit/future_spec.rb} +188 -0
  32. data/spec/aws/flow/{simple_dfa_spec.rb → unit/simple_dfa_spec.rb} +0 -0
  33. data/spec/aws/runner/integration/runner_integration_spec.rb +1 -0
  34. data/spec/aws/runner/unit/runner_unit_spec.rb +3 -3
  35. data/spec/aws/templates/unit/activity_spec.rb +9 -10
  36. data/spec/aws/templates/unit/base_spec.rb +10 -11
  37. data/spec/aws/templates/unit/default_spec.rb +23 -6
  38. data/spec/aws/templates/unit/result_spec.rb +130 -0
  39. data/spec/aws/templates/unit/starter_spec.rb +32 -105
  40. data/spec/aws/templates/unit/utilities_spec.rb +80 -0
  41. metadata +19 -13
@@ -0,0 +1,59 @@
1
+ module AWS
2
+ module Flow
3
+ module Templates
4
+
5
+ module Utils
6
+
7
+ # This method calls the given block. If an UnknownResourceFault is
8
+ # returned, then it tries to register AWS Flow defaults with the service
9
+ # and calls the block again.
10
+ def self.register_on_failure(domain, &block)
11
+ begin
12
+ block.call(domain)
13
+ rescue AWS::SimpleWorkflow::Errors::UnknownResourceFault => e
14
+ register_defaults(domain)
15
+ block.call(domain)
16
+ end
17
+ end
18
+
19
+ # Registers the relevant defaults with the Simple Workflow Service. If
20
+ # domain name is not provided, it registers the FlowDefault domain
21
+ # @api private
22
+ def self.register_defaults(name=nil)
23
+ name ||= FlowConstants.defaults[:domain]
24
+ domain = AWS::Flow::Utilities.register_domain(name)
25
+
26
+ register_default_workflow(domain)
27
+ register_default_result_activity(domain)
28
+ end
29
+
30
+ # Registers the default workflow type FlowDefaultWorkflowRuby with the
31
+ # Simple Workflow Service
32
+ # @api private
33
+ def self.register_default_workflow(domain)
34
+ AWS::Flow::WorkflowWorker.new(
35
+ domain.client,
36
+ domain,
37
+ nil,
38
+ AWS::Flow::Templates.default_workflow
39
+ ).register
40
+ end
41
+
42
+ # Registers the default result activity type FlowDefaultResultActivityRuby
43
+ # with the Simple Workflow Service
44
+ # @api private
45
+ def self.register_default_result_activity(domain)
46
+ worker = AWS::Flow::ActivityWorker.new(
47
+ domain.client,
48
+ domain,
49
+ nil,
50
+ AWS::Flow::Templates.result_activity
51
+ ) {{ use_forking: false }}
52
+ worker.register
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -5,6 +5,7 @@ describe Activities do
5
5
  @bucket = ENV['AWS_SWF_BUCKET_NAME']
6
6
  ENV['AWS_SWF_BUCKET_NAME'] = nil
7
7
  @swf, @domain = setup_swf
8
+ kill_executors
8
9
  end
9
10
 
10
11
  after(:all) do
@@ -4,6 +4,7 @@ describe "task_priority" do
4
4
 
5
5
  before(:all) do
6
6
  @swf, @domain = setup_swf
7
+ kill_executors
7
8
  end
8
9
 
9
10
  context "activities and workflows" do
@@ -117,7 +118,8 @@ describe "task_priority" do
117
118
  {
118
119
  version: "1.0",
119
120
  default_task_priority: 50,
120
- default_execution_start_to_close_timeout: 60
121
+ default_execution_start_to_close_timeout: 60,
122
+ default_tag_list: ["continue_task_priority_test"]
121
123
  }
122
124
  end
123
125
  def entry_point
@@ -140,6 +142,7 @@ describe "task_priority" do
140
142
  execution.status.should == :continued_as_new
141
143
  events = execution.events.select { |x| x.event_type == "WorkflowExecutionContinuedAsNew" }
142
144
  events.first.attributes.task_priority.should == 50
145
+ @domain.workflow_executions.tagged("continue_task_priority_test").each { |x| x.terminate }
143
146
  end
144
147
 
145
148
 
@@ -162,13 +165,14 @@ describe "task_priority" do
162
165
  execution.status.should == :continued_as_new
163
166
  events = execution.events.select { |x| x.event_type == "WorkflowExecutionContinuedAsNew" }
164
167
  events.first.attributes.task_priority.should == 100
168
+ @domain.workflow_executions.tagged("continue_task_priority_test").each { |x| x.terminate }
165
169
  end
166
170
  end
167
171
 
168
172
  context "child_workflows" do
169
173
 
170
174
  it "test whether task priority is overridden for child workflow" do
171
- class ChildWorkflowsTestChildWorkflow
175
+ class ChildWorkflowsTaskPriorityTestChildWorkflow
172
176
  extend AWS::Flow::Workflows
173
177
  workflow :child do
174
178
  {
@@ -181,7 +185,7 @@ describe "task_priority" do
181
185
  def child; sleep 1; end
182
186
  end
183
187
 
184
- class ChildWorkflowsTestParentWorkflow
188
+ class ChildWorkflowsTaskPriorityTestParentWorkflow
185
189
  extend AWS::Flow::Workflows
186
190
  workflow :parent do
187
191
  {
@@ -192,15 +196,14 @@ describe "task_priority" do
192
196
  end
193
197
  def parent
194
198
  domain = get_test_domain
195
- client = AWS::Flow::workflow_client(domain.client, domain) { { from_class: "ChildWorkflowsTestChildWorkflow", task_list: "test2" } }
196
- client.send_async(:start_execution)
197
- client.send_async(:start_execution)
199
+ client = AWS::Flow::workflow_client(domain.client, domain) { { from_class: "ChildWorkflowsTaskPriorityTestChildWorkflow", task_list: "test2" } }
200
+ client.start_execution
198
201
  end
199
202
  end
200
203
 
201
- parent_client = AWS::Flow::workflow_client(@domain.client, @domain) { { from_class: "ChildWorkflowsTestParentWorkflow" } }
202
- @child_worker = WorkflowWorker.new(@domain.client, @domain, "test2", ChildWorkflowsTestChildWorkflow)
203
- @parent_worker = WorkflowWorker.new(@domain.client, @domain, "test", ChildWorkflowsTestParentWorkflow)
204
+ parent_client = AWS::Flow::workflow_client(@domain.client, @domain) { { from_class: "ChildWorkflowsTaskPriorityTestParentWorkflow" } }
205
+ @child_worker = WorkflowWorker.new(@domain.client, @domain, "test2", ChildWorkflowsTaskPriorityTestChildWorkflow)
206
+ @parent_worker = WorkflowWorker.new(@domain.client, @domain, "test", ChildWorkflowsTaskPriorityTestParentWorkflow)
204
207
  @child_worker.register
205
208
  @parent_worker.register
206
209
 
@@ -221,4 +224,8 @@ describe "task_priority" do
221
224
 
222
225
  end
223
226
 
227
+ after(:all) do
228
+ Test::Integ.kill_executors
229
+ end
230
+
224
231
  end
@@ -54,20 +54,17 @@ describe "AWS::Flow" do
54
54
  task_list: "bar",
55
55
  version: "2.0",
56
56
  tag_list: ['overriden_test'],
57
- wait: true,
57
+ get_result: true,
58
58
  domain: @domain.name
59
59
  }
60
60
 
61
- executor = ForkingExecutor.new
62
- executor.execute { AWS::Flow::start("StarterTestActivity.foo", {input: "Hello"}, options) }
63
-
64
- executor.shutdown(1)
61
+ future = AWS::Flow::start("StarterTestActivity.foo", {input: "Hello"}, options)
65
62
 
66
63
  until @domain.workflow_executions.count.count > 0
67
64
  sleep 2
68
65
  end
69
66
 
70
- @domain.workflow_executions.each do |x|
67
+ @domain.workflow_executions.tagged("overriden_test").each do |x|
71
68
  x.execution_start_to_close_timeout.should == 100
72
69
  x.workflow_type.name.should == "#{FlowConstants.defaults[:prefix_name]}.#{FlowConstants.defaults[:execution_method]}"
73
70
  x.workflow_type.version.should == "#{FlowConstants.defaults[:version]}"
@@ -82,7 +79,7 @@ describe "AWS::Flow" do
82
79
  root.should be_kind_of(AWS::Flow::Templates::RootTemplate)
83
80
  root.result_step.should_not be_nil
84
81
  result = root.result_step
85
- result.should be_kind_of(AWS::Flow::Templates::ActivityTemplate)
82
+ result.should be_kind_of(AWS::Flow::Templates::ResultActivityTemplate)
86
83
 
87
84
  activity = root.step
88
85
  activity.should be_kind_of(AWS::Flow::Templates::ActivityTemplate)
@@ -100,6 +97,8 @@ describe "AWS::Flow" do
100
97
  x.terminate
101
98
  end
102
99
 
100
+ Test::Integ.kill_executors
101
+
103
102
  end
104
103
 
105
104
  end
@@ -4,9 +4,9 @@ describe "AWS::Flow" do
4
4
 
5
5
  context "#start" do
6
6
 
7
- it "calls AWS::Flow::Templates#start" do
7
+ it "calls AWS::Flow::Templates::Starter#start" do
8
8
  AWS::Flow::stub(:start_workflow)
9
- expect(AWS::Flow::Templates).to receive(:start)
9
+ expect(AWS::Flow::Templates::Starter).to receive(:start)
10
10
  AWS::Flow::start("HelloWorld.hello", { foo: "foo" })
11
11
  end
12
12
 
@@ -180,6 +180,48 @@ end
180
180
 
181
181
  describe ActivityWorker do
182
182
 
183
+ context "#initialize" do
184
+
185
+ context "windows platform" do
186
+
187
+ before(:all) do
188
+ @platform = RbConfig::CONFIG['host_os'] =~ /mswin|mingw/
189
+ RbConfig::CONFIG['host_os'] = 'mswin'
190
+ end
191
+
192
+ after(:all) do
193
+ RbConfig::CONFIG['host_os'] = @platform
194
+ end
195
+
196
+ it "turns forking off by default" do
197
+ ActivityWorker.any_instance.stub(:add_implementation)
198
+ worker = AWS::Flow::ActivityWorker.new(nil, nil, "task_list")
199
+ options = worker.instance_variable_get("@options")
200
+ options.use_forking.should be_false
201
+ options.execution_workers.should be_zero
202
+ end
203
+
204
+ it "turns forking on if user explicitely specifies execution workers" do
205
+ ActivityWorker.any_instance.stub(:add_implementation)
206
+ worker = AWS::Flow::ActivityWorker.new(nil, nil, "task_list") { { execution_workers: 5 }}
207
+ options = worker.instance_variable_get("@options")
208
+ options.use_forking.should be_true
209
+ options.execution_workers.should == 5
210
+ end
211
+ end
212
+
213
+ # This test is to ensure that the default behavior of forking is overriden
214
+ # by setting execution_workers to 0. This is added to ensure that forking
215
+ # can be turned off from the json spec for the runner.
216
+ it "turns forking on if execution_workers is 0 and use_forking is true" do
217
+ worker = AWS::Flow::ActivityWorker.new(nil, nil, "task_list") { { execution_workers: 0, use_forking: true }}
218
+ options = worker.instance_variable_get("@options")
219
+ options.use_forking.should be_false
220
+ options.execution_workers.should be_zero
221
+ end
222
+
223
+ end
224
+
183
225
  context "#register" do
184
226
  it "ensures that worker uses the right task list to register type" do
185
227
  class DefaultTasklistTestActivity
@@ -0,0 +1,59 @@
1
+ ##
2
+ # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License").
5
+ # You may not use this file except in compliance with the License.
6
+ # A copy of the License is located at
7
+ #
8
+ # http://aws.amazon.com/apache2.0
9
+ #
10
+ # or in the "license" file accompanying this file. This file is distributed
11
+ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12
+ # express or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ ##
15
+
16
+ require 'spec_helper'
17
+
18
+ describe ExternalConditionVariable do
19
+ let(:condition) { ExternalConditionVariable.new }
20
+
21
+ it "blocks a thread and wakes it up on signal" do
22
+ t = Thread.new { condition.wait }
23
+ sleep 1
24
+ t.status.should == "sleep"
25
+ condition.signal
26
+ t.status.should == false
27
+ end
28
+
29
+ it "blocks multiple threads and wakes one up on signal" do
30
+ t1 = Thread.new { condition.wait }
31
+ t2 = Thread.new { condition.wait }
32
+ sleep 1
33
+ t1.status.should == "sleep"
34
+ t2.status.should == "sleep"
35
+ condition.signal
36
+ (t1.status == false && t2.status == false).should == false
37
+ condition.signal
38
+ (t1.status == false && t2.status == false).should == true
39
+ end
40
+
41
+ it "blocks a thread and wakes it up on broadcast" do
42
+ t = Thread.new { condition.wait }
43
+ sleep 1
44
+ t.status.should == "sleep"
45
+ condition.broadcast
46
+ t.status.should == false
47
+ end
48
+
49
+ it "blocks multiple threads and wakes them up on broadcast" do
50
+ t1 = Thread.new { condition.wait }
51
+ t2 = Thread.new { condition.wait }
52
+ sleep 1
53
+ t1.status.should == "sleep"
54
+ t2.status.should == "sleep"
55
+ condition.broadcast
56
+ (t1.status == false && t2.status == false).should == true
57
+ end
58
+
59
+ end
@@ -1,3 +1,5 @@
1
+ require 'spec_helper'
2
+
1
3
  ##
2
4
  # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
5
  #
@@ -208,3 +210,189 @@ describe Future do
208
210
  end
209
211
 
210
212
  end
213
+
214
+ describe ExternalFuture do
215
+ let(:future) { ExternalFuture.new }
216
+ let(:trace) { [] }
217
+ context "#set" do
218
+ it "throws an exception when set multiple times" do
219
+ future.set(:foo)
220
+ expect { future.set(:bar) }.to raise_error AlreadySetException
221
+ end
222
+ end
223
+
224
+ context "#get" do
225
+
226
+ it "returns the value which was set" do
227
+ value = :foo
228
+ future.set(value)
229
+ future.get.should == value
230
+ end
231
+
232
+ it "blocks a thread until a value is ready" do
233
+ future = ExternalFuture.new
234
+
235
+ t1 = Thread.new { future.get }
236
+ sleep 1
237
+ t1.status.should == "sleep"
238
+ future.set
239
+ t1.status.should == false
240
+ end
241
+
242
+ it "works with an already set future" do
243
+ future = ExternalFuture.new.set
244
+ future.get
245
+ # future.get will be a non blocking call if it is already set. If not,
246
+ # then the next line will never be executed
247
+ future.set?.should == true
248
+ end
249
+
250
+ it "blocks multiple threads until a value is ready" do
251
+ future = ExternalFuture.new
252
+ t1 = Thread.new { future.get }
253
+ t2 = Thread.new { future.get }
254
+ t3 = Thread.new { future.get }
255
+
256
+ sleep 1
257
+
258
+ t1.status.should == "sleep"
259
+ t2.status.should == "sleep"
260
+ t3.status.should == "sleep"
261
+
262
+ future.set
263
+
264
+ t1.status.should == false
265
+ t2.status.should == false
266
+ t3.status.should == false
267
+ end
268
+
269
+ context "#timeout" do
270
+ it "raises an exception if timeout occurs and leaves future unset" do
271
+ future = ExternalFuture.new
272
+ expect{future.get(1)}.to raise_error(Timeout::Error)
273
+ future.set?.should == false
274
+ end
275
+
276
+ it "raises an exception if timeout occurs multiple times" do
277
+ future = ExternalFuture.new
278
+ expect{future.get(1)}.to raise_error(Timeout::Error)
279
+ expect{future.get(1)}.to raise_error(Timeout::Error)
280
+ expect{future.get(1)}.to raise_error(Timeout::Error)
281
+ end
282
+
283
+ it "nil timeout will block until the future is set" do
284
+ future = ExternalFuture.new
285
+ t = Thread.new { future.get(nil) }
286
+ sleep 1
287
+ t.status.should == "sleep"
288
+ future.set
289
+ t.status.should == false
290
+ end
291
+
292
+ end
293
+
294
+ end
295
+
296
+ context "#wait_for_any" do
297
+
298
+ it "works with multiple futures" do
299
+ f1 = ExternalFuture.new
300
+ f2 = ExternalFuture.new
301
+ f3 = ExternalFuture.new
302
+ t = Thread.new do
303
+ f1.set("foo")
304
+ end
305
+ r1, r2, r3 = AWS::Flow::Core.wait_for_any(f1, f2, f3)
306
+ r1.get.should == "foo"
307
+ f2.set?.should be_false
308
+ f3.set?.should be_false
309
+ end
310
+
311
+ it "works with no futures" do
312
+ wait_for_any
313
+ # Just need to check if the test completes
314
+ true.should == true
315
+ end
316
+
317
+ it "works with an already set future" do
318
+ future = ExternalFuture.new.set
319
+ wait_for_any(future)
320
+ future.set?.should == true
321
+ end
322
+
323
+ it "works with one set and one unset future" do
324
+ future = ExternalFuture.new.set
325
+ future2 = ExternalFuture.new
326
+ wait_for_any(future, future2)
327
+ future.set?.should == true
328
+ future2.set?.should be_false
329
+ end
330
+
331
+ end
332
+
333
+ context "#wait_for_all" do
334
+
335
+ it "works with multiple futures" do
336
+ f1 = ExternalFuture.new
337
+ f2 = ExternalFuture.new
338
+ f3 = ExternalFuture.new
339
+ t = Thread.new do
340
+ f1.set("foo")
341
+ f2.set("bar")
342
+ f3.set("baz")
343
+ end
344
+ r1, r2, r3 = AWS::Flow::Core.wait_for_all(f1, f2, f3)
345
+ r1.get.should == "foo"
346
+ r2.get.should == "bar"
347
+ r3.get.should == "baz"
348
+ end
349
+
350
+ it "works with no futures" do
351
+ wait_for_all
352
+ # Just need to check if the test completes
353
+ true.should == true
354
+ end
355
+
356
+ it "works with an already set future" do
357
+ future = ExternalFuture.new.set
358
+ wait_for_all(future)
359
+ future.set?.should == true
360
+ end
361
+
362
+ it "works with one set and one unset future" do
363
+ future = ExternalFuture.new.set
364
+ future2 = ExternalFuture.new
365
+ Thread.new { future2.set }
366
+ wait_for_all(future, future2)
367
+ future.set?.should == true
368
+ future2.set?.should == true
369
+ end
370
+
371
+ end
372
+
373
+
374
+ [:timed_wait_for_all, :timed_wait_for_any].each do |method|
375
+
376
+ context "#{method}" do
377
+
378
+ it "raises when timeout expires" do
379
+ future = ExternalFuture.new
380
+ expect { AWS::Flow.send(method, 1, future) }.to raise_error(Timeout::Error)
381
+ end
382
+
383
+ it "doesn't raise when timeout doesn't expires" do
384
+ future = ExternalFuture.new
385
+ Thread.new { sleep 1; future.set }
386
+ expect { AWS::Flow.send(method, 10, future) }.to_not raise_error
387
+ end
388
+
389
+ it "doesn't raise when timeout is nil" do
390
+ future = ExternalFuture.new
391
+ Thread.new { sleep 1; future.set }
392
+ expect { AWS::Flow.send(method, nil, future) }.to_not raise_error
393
+ end
394
+
395
+ end
396
+ end
397
+
398
+ end