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
@@ -6,6 +6,7 @@ include Test::Integ
6
6
  describe "Runner" do
7
7
  before(:all) do
8
8
  @swf, @domain = setup_swf
9
+ kill_executors
9
10
  end
10
11
 
11
12
  class PingUtils
@@ -333,7 +333,7 @@ describe "Runner" do
333
333
  allow_any_instance_of(AWS::Flow::WorkflowWorker).to receive(:start).and_return(nil)
334
334
  AWS::Flow::Runner.stub(:setup_domain)
335
335
  AWS::Flow::Runner.stub(:load_files)
336
- AWS::Flow::Templates.stub(:register_default_result_activity)
336
+ AWS::Flow::Templates::Utils.stub(:register_default_result_activity)
337
337
 
338
338
  # what we are testing:
339
339
  expect(AWS::Flow::Runner).to receive(:fork).exactly(3).times
@@ -352,7 +352,7 @@ describe "Runner" do
352
352
  allow_any_instance_of(AWS::Flow::WorkflowWorker).to receive(:start).and_return(nil)
353
353
  AWS::Flow::Runner.stub(:setup_domain)
354
354
  AWS::Flow::Runner.stub(:load_files)
355
- AWS::Flow::Templates.stub(:register_default_result_activity)
355
+ AWS::Flow::Templates::Utils.stub(:register_default_result_activity)
356
356
 
357
357
  # what we are testing:
358
358
  expect(AWS::Flow::Runner).to receive(:fork).exactly(0).times
@@ -373,7 +373,7 @@ describe "Runner" do
373
373
 
374
374
  AWS::Flow::Runner.stub(:setup_domain)
375
375
  AWS::Flow::Runner.stub(:load_files)
376
- AWS::Flow::Templates.stub(:register_default_result_activity)
376
+ AWS::Flow::Templates::Utils.stub(:register_default_result_activity)
377
377
 
378
378
  # what we are testing:
379
379
  expect(AWS::Flow::Runner).to receive(:fork).exactly(6).times
@@ -1,13 +1,12 @@
1
1
  require 'spec_helper'
2
- include AWS::Flow::Templates
3
2
 
4
- describe ActivityTemplate do
3
+ describe AWS::Flow::Templates::ActivityTemplate do
5
4
 
6
5
  context "#activity" do
7
6
 
8
7
  it "creates an ActivityTemplate with correct name and options" do
9
- template = activity("ActivityClass.my_activity")
10
- template.should be_kind_of(ActivityTemplate)
8
+ template = AWS::Flow::Templates::activity("ActivityClass.my_activity")
9
+ template.should be_kind_of(AWS::Flow::Templates::ActivityTemplate)
11
10
  template.name.should == "my_activity"
12
11
  template.options[:prefix_name].should == "ActivityClass"
13
12
  end
@@ -17,7 +16,7 @@ describe ActivityTemplate do
17
16
  context "#initialize" do
18
17
 
19
18
  it "assigns activity name and default options correctly" do
20
- template = ActivityTemplate.new("ActivityClass.my_activity")
19
+ template = AWS::Flow::Templates::ActivityTemplate.new("ActivityClass.my_activity")
21
20
  template.name.should == "my_activity"
22
21
  template.options[:version].should == "1.0"
23
22
  template.options[:prefix_name].should == "ActivityClass"
@@ -25,14 +24,14 @@ describe ActivityTemplate do
25
24
  end
26
25
 
27
26
  it "raises if full activity name is not given" do
28
- expect{ActivityTemplate.new("ActivityClass")}.to raise_error
27
+ expect{AWS::Flow::Templates::ActivityTemplate.new("ActivityClass")}.to raise_error
29
28
  end
30
29
 
31
30
  it "ignores irrelevant activity options" do
32
31
  options = {
33
32
  foo: "asdf"
34
33
  }
35
- template = ActivityTemplate.new("ActivityClass.my_activity", options)
34
+ template = AWS::Flow::Templates::ActivityTemplate.new("ActivityClass.my_activity", options)
36
35
  template.name.should == "my_activity"
37
36
  template.options.should_not include(:foo)
38
37
  end
@@ -45,7 +44,7 @@ describe ActivityTemplate do
45
44
  version: "2.0",
46
45
  task_list: "foo_tasklist"
47
46
  }
48
- template = ActivityTemplate.new("ActivityClass.my_activity", options)
47
+ template = AWS::Flow::Templates::ActivityTemplate.new("ActivityClass.my_activity", options)
49
48
  template.name.should == "my_activity"
50
49
  template.options[:version].should == "2.0"
51
50
  template.options[:task_list].should == "foo_tasklist"
@@ -60,7 +59,7 @@ describe ActivityTemplate do
60
59
  context "#run" do
61
60
 
62
61
  it "ensures run method calls the context" do
63
- template = activity("ActivityClass.my_activity")
62
+ template = AWS::Flow::Templates::activity("ActivityClass.my_activity")
64
63
  input = { input: "foo" }
65
64
 
66
65
  context = double
@@ -71,7 +70,7 @@ describe ActivityTemplate do
71
70
  end
72
71
 
73
72
  it "ensures activity is scheduled on the correct tasklist" do
74
- template = activity("ActivityClass.my_activity")
73
+ template = AWS::Flow::Templates::activity("ActivityClass.my_activity")
75
74
  input = { input: "foo", task_list: "bar" }
76
75
 
77
76
  context = double
@@ -1,18 +1,17 @@
1
1
  require 'spec_helper'
2
- include AWS::Flow::Templates
3
2
 
4
- describe RootTemplate do
3
+ describe AWS::Flow::Templates::RootTemplate do
5
4
 
6
5
  context "#root" do
7
6
 
8
7
  it "initializes the RootTemplate with correct step and result_step" do
9
- template = root("foo")
10
- template.should be_kind_of(RootTemplate)
8
+ template = AWS::Flow::Templates.root("foo")
9
+ template.should be_kind_of(AWS::Flow::Templates::RootTemplate)
11
10
  template.step.should == "foo"
12
11
  template.result_step.should be_nil
13
12
 
14
- template = root("foo", "bar")
15
- template.should be_kind_of(RootTemplate)
13
+ template = AWS::Flow::Templates.root("foo", "bar")
14
+ template.should be_kind_of(AWS::Flow::Templates::RootTemplate)
16
15
  template.step.should == "foo"
17
16
  template.result_step.should == "bar"
18
17
  end
@@ -26,7 +25,7 @@ describe RootTemplate do
26
25
 
27
26
  it "runs the step" do
28
27
  expect(step).to receive(:run).with("input", "context")
29
- template = root(step)
28
+ template = AWS::Flow::Templates.root(step)
30
29
  template.run("input", "context")
31
30
  end
32
31
 
@@ -34,7 +33,7 @@ describe RootTemplate do
34
33
  expect(step).to receive(:run).with("input", "context").and_return("result")
35
34
  expect(result_step).not_to receive(:run)
36
35
 
37
- template = root(step)
36
+ template = AWS::Flow::Templates.root(step)
38
37
  template.run("input", "context").should == "result"
39
38
  end
40
39
 
@@ -42,7 +41,7 @@ describe RootTemplate do
42
41
  expect(step).to receive(:run).with("input", "context").and_return("result")
43
42
  expect(result_step).to receive(:run).with("result", "context")
44
43
 
45
- template = root(step, result_step)
44
+ template = AWS::Flow::Templates.root(step, result_step)
46
45
  template.run("input", "context").should == "result"
47
46
  end
48
47
 
@@ -56,7 +55,7 @@ describe RootTemplate do
56
55
  context.should == "context"
57
56
  end
58
57
 
59
- expect { root(step, result_step).run("input", "context") }.to raise_error
58
+ expect { AWS::Flow::Templates.root(step, result_step).run("input", "context") }.to raise_error
60
59
  end
61
60
 
62
61
  it "catches exceptions and doesn't call result_step if result_step is nil" do
@@ -64,7 +63,7 @@ describe RootTemplate do
64
63
  raise "test"
65
64
  end
66
65
  expect(result_step).not_to receive(:run)
67
- expect { root(step, result_step).run("input", "context") }.to raise_error
66
+ expect { AWS::Flow::Templates.root(step, result_step).run("input", "context") }.to raise_error
68
67
  end
69
68
 
70
69
  end
@@ -122,14 +122,31 @@ describe Templates do
122
122
  klass.activities.first.name.should == "#{FlowConstants.defaults[:result_activity_prefix]}"\
123
123
  ".#{FlowConstants.defaults[:result_activity_method]}"
124
124
  klass.activities.first.version.should == FlowConstants.defaults[:version]
125
- klass.instance_methods(false).should include(:result)
125
+ klass.instance_methods(false).should include(:run)
126
126
  end
127
127
 
128
- it "correctly initializes and sets the result" do
129
- inst = klass.new
130
- expect(inst.result).to be_kind_of(AWS::Flow::Core::Future)
131
- inst.run("foo")
132
- inst.result.get.should == "foo"
128
+ context "#run" do
129
+ let(:inst) { klass.new }
130
+ let(:result) { { key: "123", result: "asdf" } }
131
+
132
+ it "raises if key or result is not present" do
133
+ expect{inst.run("adsf")}.to raise_error(ArgumentError)
134
+ end
135
+
136
+ it "passes if key and result are present" do
137
+ expect{inst.run(result)}.not_to raise_error
138
+ end
139
+
140
+ it "uses the writer to send marshalled result" do
141
+ writer = double
142
+
143
+ expect(writer).to receive(:puts) do |x|
144
+ Marshal.load(x).should == result
145
+ end
146
+
147
+ inst = klass.new(writer)
148
+ inst.run(result)
149
+ end
133
150
  end
134
151
 
135
152
  it "doesn't throw an exception if a default activity class already exists" do
@@ -0,0 +1,130 @@
1
+ require 'spec_helper'
2
+
3
+ describe AWS::Flow::Templates::ResultWorker do
4
+
5
+ context "#semaphore" do
6
+
7
+ it "is a class instance variable" do
8
+ semaphore = AWS::Flow::Templates::ResultWorker.instance_variable_get("@semaphore")
9
+ semaphore.is_a?(Mutex)
10
+ end
11
+
12
+ end
13
+
14
+ context "#start" do
15
+
16
+ it "ensure that ResultWorker is started only once per process" do
17
+
18
+ AWS::Flow::Templates::ResultWorker.stub(:start_executor)
19
+ AWS::Flow::Templates::ResultWorker.start("domain")
20
+
21
+ task_list = AWS::Flow::Templates::ResultWorker.instance_variable_get("@task_list")
22
+ results = AWS::Flow::Templates::ResultWorker.instance_variable_get("@results")
23
+ executor = AWS::Flow::Templates::ResultWorker.instance_variable_get("@executor")
24
+
25
+ AWS::Flow::Templates::ResultWorker.start("domain")
26
+ AWS::Flow::Templates::ResultWorker.instance_variable_get("@task_list").should == task_list
27
+ AWS::Flow::Templates::ResultWorker.instance_variable_get("@results").should == results
28
+ AWS::Flow::Templates::ResultWorker.instance_variable_get("@executor").should == executor
29
+
30
+ AWS::Flow::Templates::ResultWorker.stop
31
+ end
32
+
33
+ end
34
+
35
+ context "#shutdown" do
36
+
37
+ it "tests clean exit of result worker" do
38
+ AWS::Flow::Templates::ResultWorker.start("FlowDefault")
39
+ sleep 1
40
+ Thread.list.count.should == 2
41
+ AWS::Flow::Templates::ResultWorker.stop
42
+ Thread.list.count.should == 1
43
+ AWS::Flow::Templates::ResultWorker.instance_variable_get("@executor").should be_nil
44
+ end
45
+
46
+ end
47
+
48
+ context "#start_listener" do
49
+
50
+ it "starts the listener thread and correctly sets the futures" do
51
+
52
+ class AWS::Flow::Templates::ResultWorker
53
+ class << self
54
+ alias_method :start_copy, :start
55
+ def start
56
+ @results = SynchronizedHash.new
57
+ self.init
58
+ @listener_t = nil
59
+ end
60
+ end
61
+ end
62
+
63
+ AWS::Flow::Templates::ResultWorker.start
64
+
65
+ AWS::Flow::Templates::ResultWorker.results[:key1] = ExternalFuture.new
66
+ AWS::Flow::Templates::ResultWorker.results[:key2] = ExternalFuture.new
67
+ AWS::Flow::Templates::ResultWorker.results[:key3] = ExternalFuture.new
68
+
69
+ reader, writer = IO.pipe
70
+
71
+ AWS::Flow::Templates::ResultWorker.start_listener(reader)
72
+ writer.puts Marshal.dump({key: :key1, result: "result1"})
73
+ writer.puts Marshal.dump({key: :key2, result: "result2"})
74
+ writer.puts Marshal.dump({key: :key3, result: "result3"})
75
+
76
+ AWS::Flow::Templates::ResultWorker.results[:key1].get.should == "result1"
77
+ AWS::Flow::Templates::ResultWorker.results[:key2].get.should == "result2"
78
+ AWS::Flow::Templates::ResultWorker.results[:key3].get.should == "result3"
79
+
80
+ writer.close
81
+ reader.close
82
+
83
+ class AWS::Flow::Templates::ResultWorker
84
+ class << self
85
+ alias_method :start_copy, :start
86
+ end
87
+ end
88
+
89
+ end
90
+
91
+ end
92
+
93
+ context "#get_result_future" do
94
+
95
+ it "returns a future and the future is removed from the hash when it is set" do
96
+
97
+ class AWS::Flow::Templates::ResultWorker
98
+ class << self
99
+ alias_method :start_copy, :start
100
+ def start
101
+ @results = SynchronizedHash.new
102
+ end
103
+ end
104
+ end
105
+
106
+ key = "foo"
107
+ future = ExternalFuture.new
108
+
109
+ AWS::Flow::Templates::ResultWorker.results[key] = future
110
+
111
+ # Call get_result and check that the result is set
112
+ result = AWS::Flow::Templates::ResultWorker.get_result_future(key)
113
+
114
+ result.should == future
115
+
116
+ future.set(nil)
117
+
118
+ AWS::Flow::Templates::ResultWorker.results[key].should be_nil
119
+
120
+ class AWS::Flow::Templates::ResultWorker
121
+ class << self
122
+ alias_method :start_copy, :start
123
+ end
124
+ end
125
+
126
+ end
127
+
128
+ end
129
+
130
+ end
@@ -123,147 +123,74 @@ describe "AWS::Flow::Templates" do
123
123
 
124
124
  end
125
125
 
126
- it "doesn't initialize result_step when wait is false" do
126
+ it "doesn't initialize result_step when get_result is false" do
127
127
 
128
128
  expect(AWS::Flow).to receive(:start_workflow) do |input, options|
129
129
  input[:definition].result_step.should be_nil
130
130
  end
131
131
 
132
- options = {
133
- wait: false
134
- }
135
-
136
- AWS::Flow::start("HelloWorld.hello", {input: "input"}, options)
137
-
138
- end
139
-
140
- it "initializes result_step and calls get_result when wait is true" do
141
-
142
- expect(AWS::Flow).to receive(:start_workflow) do |input, options|
143
- input[:definition].result_step.should_not be_nil
144
- end
145
-
146
- expect(AWS::Flow::Templates).to receive(:get_result)
132
+ expect(AWS::Flow::Templates::ResultWorker).to_not receive(:start)
133
+ expect(AWS::Flow::Templates::ResultWorker).to_not receive(:get_result_future)
134
+ expect(AWS::Flow::Templates::Starter).to_not receive(:set_result_activity)
147
135
 
148
136
  options = {
149
- wait: true
137
+ get_result: false
150
138
  }
151
139
 
152
140
  AWS::Flow::start("HelloWorld.hello", {input: "input"}, options)
153
141
 
154
142
  end
155
143
 
156
- it "calls get_result with timeout value when wait & wait_timeout are set" do
144
+ it "initializes result_step and calls get_result when get_result is true" do
157
145
 
158
- expect(AWS::Flow).to receive(:start_workflow) do |input, options|
159
- input[:definition].result_step.should_not be_nil
160
- end
161
-
162
- expect(AWS::Flow::Templates).to receive(:get_result) do |tasklist, domain, timeout|
163
- timeout.should == 10
164
- end
146
+ expect(AWS::Flow::Templates::ResultWorker).to receive(:start)
147
+ expect(AWS::Flow::Templates::ResultWorker).to receive(:get_result_future)
148
+ expect(AWS::Flow::Templates::Starter).to receive(:set_result_activity)
165
149
 
166
150
  options = {
167
- wait: true,
168
- wait_timeout: 10
151
+ get_result: true
169
152
  }
170
153
 
171
154
  AWS::Flow::start("HelloWorld.hello", {input: "input"}, options)
172
155
 
173
156
  end
174
157
 
175
-
176
158
  end
177
159
 
178
- context "#get_result" do
179
-
180
- it "starts an activity worker and sets the future" do
160
+ context "#set_result_activity" do
181
161
 
182
- tasklist = "result_tasklist: foo"
162
+ it "sets the result step correctly and creates a new ExternalFuture" do
163
+ root = double
183
164
 
184
- # Get the result activity class
185
- klass = AWS::Flow::Templates.result_activity
186
- instance = klass.new
187
-
188
- expect(klass).to receive(:new).and_return(instance)
189
-
190
- expect_any_instance_of(AWS::Flow::ActivityWorker).to receive(:add_implementation) do |k|
191
- k.class.should == AWS::Flow::Templates.const_get("#{FlowConstants.defaults[:result_activity_prefix]}")
192
- k.result.should be_kind_of(AWS::Flow::Core::Future)
165
+ class AWS::Flow::Templates::ResultWorker
166
+ class << self
167
+ alias_method :start_copy, :start
168
+ def start
169
+ @results = SynchronizedHash.new
170
+ end
171
+ end
193
172
  end
194
173
 
195
- expect_any_instance_of(AWS::Flow::ActivityWorker).to receive(:run_once) do
196
- # Manually call the activity
197
- instance.send(FlowConstants.defaults[:result_activity_method].to_sym, {name: "foo"} )
198
- end
174
+ AWS::Flow::Templates::ResultWorker.start
199
175
 
200
- # Call get_result and check that the result is set
201
- result = AWS::Flow::Templates.get_result(tasklist, "domain")
176
+ expect(SecureRandom).to receive(:uuid).and_return("foo")
202
177
 
203
- result.should include(name: "foo")
204
-
205
- end
206
-
207
- it "times out correctly" do
208
-
209
- tasklist = "result_tasklist: foo"
210
-
211
- # Get the result activity class
212
- klass = AWS::Flow::Templates.result_activity
213
- instance = klass.new
214
-
215
- expect(klass).to receive(:new).and_return(instance)
216
-
217
- expect_any_instance_of(AWS::Flow::ActivityWorker).to receive(:run_once) do
218
- # Manually call the activity
219
- sleep 5
220
- instance.send(FlowConstants.defaults[:result_activity_method].to_sym, {name: "foo"} )
178
+ expect(root).to receive(:result_step=) do |x|
179
+ x.is_a?(AWS::Flow::Templates::ResultActivityTemplate)
221
180
  end
222
181
 
223
- # Call get_result and check that the result is set
224
- result = AWS::Flow::Templates.get_result(tasklist, "domain", 1)
225
- result.should be_nil
182
+ AWS::Flow::Templates::Starter.set_result_activity("task_list", root)
226
183
 
227
- end
184
+ result = AWS::Flow::Templates::ResultWorker.results["result_key: foo"]
185
+ result.should_not be_nil
186
+ result.should be_a(AWS::Flow::Core::ExternalFuture)
228
187
 
229
-
230
- end
231
-
232
- context "#register_default_domain" do
233
-
234
- it "registers the default domain" do
235
- expect(AWS::Flow::Utilities).to receive(:register_domain) do |name|
236
- name.should == FlowConstants.defaults[:domain]
188
+ class AWS::Flow::Templates::ResultWorker
189
+ class << self
190
+ alias_method :start_copy, :start
191
+ end
237
192
  end
238
- AWS::Flow::Templates.register_default_domain
239
- end
240
193
 
241
- end
242
-
243
- context "#register_default_workflow" do
244
-
245
- it "registers the default workflow" do
246
- domain = double
247
- allow(domain).to receive(:client).and_return(domain)
248
- expect_any_instance_of(AWS::Flow::WorkflowWorker).to receive(:add_implementation) do |k|
249
- k.should == AWS::Flow::Templates.const_get("#{FlowConstants.defaults[:prefix_name]}")
250
- end
251
- expect_any_instance_of(AWS::Flow::WorkflowWorker).to receive(:register)
252
- AWS::Flow::Templates.register_default_workflow(domain)
253
- end
254
-
255
- end
256
-
257
- context "#register_default_result_activity" do
258
-
259
- it "registers the default result activity" do
260
- domain = double
261
- allow(domain).to receive(:client).and_return(domain)
262
- expect_any_instance_of(AWS::Flow::ActivityWorker).to receive(:add_implementation) do |k|
263
- k.should == AWS::Flow::Templates.const_get("#{FlowConstants.defaults[:result_activity_prefix]}")
264
- end
265
- expect_any_instance_of(AWS::Flow::ActivityWorker).to receive(:register)
266
- AWS::Flow::Templates.register_default_result_activity(domain)
267
194
  end
268
195
 
269
196
  end