aws-flow 2.4.0 → 3.0.0

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