floe 0.14.0 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e0e0b86c08d322e2ebc8e115e5193d0493f8257c831f7377b18c32d4284ae6f2
4
- data.tar.gz: 1bf5ed62abafe2e1af6025dbaf47d9faa3b1c93ffec8fd88fbeacd4445efc795
3
+ metadata.gz: 6f21cf2fcc6a7fa9f7536e0cca328d101a867c389f53f768e4746b6e69f643a5
4
+ data.tar.gz: f3d82401842f66504afdeaa4b051db01d5fcb9e79b704af368712d68b4950251
5
5
  SHA512:
6
- metadata.gz: 34f64555f2472c121fcea9dda8baac7fbe853cdbc5e949c831d7b7f070878d3ef2f86ed90d907b5a7033539fcad6aeea64a4dee6475a752d7f89e4f679a4b269
7
- data.tar.gz: 378c5bcd562d4bbeb7e3eb13eb55b1d5d8e7aed0ea4749c2b65f951cf3be0e5f13d3e288cdbf63b4a5729e98c5010656d6dca3db60ffbf8463988008980edd83
6
+ metadata.gz: 3e73dbdd868ec57b8138a9e4b0b529c0f2c44cdb21d43c0ae29ca9094512f4044cc621a38e4ac217bf6b17e13f1ef3a361cf5917023eab87d30c7b4cc90eccfc
7
+ data.tar.gz: e174fabde62d6348f8517f7c2d019600b34612f5b8d822eb83c2c5579f5e224596b6edaec383bfe9cce75c3260e50a4a0b744764119cd96e2a973f92b49b7d5a
data/.codeclimate.yml CHANGED
@@ -1,3 +1,4 @@
1
+ version: '2'
1
2
  prepare:
2
3
  fetch:
3
4
  - url: https://raw.githubusercontent.com/ManageIQ/manageiq-style/master/.rubocop_base.yml
@@ -8,9 +9,28 @@ prepare:
8
9
  path: styles/base.yml
9
10
  - url: https://raw.githubusercontent.com/ManageIQ/manageiq-style/master/styles/cc_base.yml
10
11
  path: styles/cc_base.yml
12
+ checks:
13
+ argument-count:
14
+ enabled: false
15
+ complex-logic:
16
+ enabled: false
17
+ file-lines:
18
+ enabled: false
19
+ method-complexity:
20
+ config:
21
+ threshold: 11
22
+ method-count:
23
+ enabled: false
24
+ method-lines:
25
+ enabled: false
26
+ nested-control-flow:
27
+ enabled: false
28
+ return-statements:
29
+ enabled: false
11
30
  plugins:
12
31
  rubocop:
13
32
  enabled: true
14
33
  config: ".rubocop_cc.yml"
15
34
  channel: rubocop-1-56-3
16
- version: '2'
35
+ exclude_patterns:
36
+ - spec/
data/CHANGELOG.md CHANGED
@@ -4,6 +4,29 @@ This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.15.1] - 2024-11-21
8
+ ### Fixed
9
+ - Fix Map/Parallel States checking container_runner#status! of finished states ([#296](https://github.com/ManageIQ/floe/pull/296))
10
+ - Fix child workflow mixin tight loop ([#297](https://github.com/ManageIQ/floe/pull/297))
11
+
12
+ ## [0.15.0] - 2024-10-28
13
+ ### Added
14
+ - Add WorkflowBase base class for Workflow ([#279](https://github.com/ManageIQ/floe/pull/279))
15
+ - Add tool for using the aws stepfunctions simulator ([#244](https://github.com/ManageIQ/floe/pull/244))
16
+ - Implement Map state ([#184](https://github.com/ManageIQ/floe/pull/184))
17
+ - Add Map State Tolerated Failure ([#282](https://github.com/ManageIQ/floe/pull/282))
18
+ - Run Map iterations in parallel up to MaxConcurrency ([#283](https://github.com/ManageIQ/floe/pull/283))
19
+ - Implement Parallel State ([#291](https://github.com/ManageIQ/floe/pull/291))
20
+
21
+ ### Changed
22
+ - More granular compare_key and determine path at initialization time ([#274](https://github.com/ManageIQ/floe/pull/274))
23
+ - For Choice validation, use instance variables and not payload ([#277](https://github.com/ManageIQ/floe/pull/277))
24
+ - Return ExceedToleratedFailureThreshold if ToleratedFailureCount/Percentage is present ([#285](https://github.com/ManageIQ/floe/pull/285))
25
+
26
+ ### Fixed
27
+ - Fix case on log messages ([#280](https://github.com/ManageIQ/floe/pull/280))
28
+ - Handle either ToleratedFailureCount or ToleratedFailurePercentage ([#284](https://github.com/ManageIQ/floe/pull/284))
29
+
7
30
  ## [0.14.0] - 2024-08-20
8
31
  ### Added
9
32
  - Implement "IsNumeric": false ([#266](https://github.com/ManageIQ/floe/pull/266))
@@ -249,7 +272,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
249
272
  ### Added
250
273
  - Initial release
251
274
 
252
- [Unreleased]: https://github.com/ManageIQ/floe/compare/v0.14.0...HEAD
275
+ [Unreleased]: https://github.com/ManageIQ/floe/compare/v0.15.1...HEAD
276
+ [0.15.1]: https://github.com/ManageIQ/floe/compare/v0.15.0...v0.15.1
277
+ [0.15.0]: https://github.com/ManageIQ/floe/compare/v0.14.0...v0.15.0
253
278
  [0.14.0]: https://github.com/ManageIQ/floe/compare/v0.13.1...v0.14.0
254
279
  [0.13.1]: https://github.com/ManageIQ/floe/compare/v0.13.0...v0.13.1
255
280
  [0.13.0]: https://github.com/ManageIQ/floe/compare/v0.12.0...v0.13.0
data/README.md CHANGED
@@ -197,6 +197,14 @@ Options supported by the kubernetes docker runner are:
197
197
  * `ca_file` - Path to a certificate-authority file for the kubernetes API, only valid if server and token are passed. If present `/run/secrets/kubernetes.io/serviceaccount/ca.crt` will be used
198
198
  * `verify_ssl` - Controls if the kubernetes API certificate-authority should be verified, defaults to "true", only vaild if server and token are passed
199
199
 
200
+ ## Features Not Yet Supported
201
+
202
+ The following are not yet supported:
203
+ - Map State Fields:
204
+ - ItemReader
205
+ - ItemSelector/ItemBatcher
206
+ - ResultWriter
207
+
200
208
  ## Development
201
209
 
202
210
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/examples/map.asl ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "Comment": "Using Map state in Inline mode",
3
+ "StartAt": "Pass",
4
+ "States": {
5
+ "Pass": {
6
+ "Type": "Pass",
7
+ "Next": "Map demo",
8
+ "Result": {
9
+ "foo": "bar",
10
+ "colors": [
11
+ "red",
12
+ "green",
13
+ "blue",
14
+ "yellow",
15
+ "white"
16
+ ]
17
+ }
18
+ },
19
+ "Map demo": {
20
+ "Type": "Map",
21
+ "ItemsPath": "$.colors",
22
+ "MaxConcurrency": 2,
23
+ "ItemProcessor": {
24
+ "ProcessorConfig": {
25
+ "Mode": "INLINE"
26
+ },
27
+ "StartAt": "Generate UUID",
28
+ "States": {
29
+ "Generate UUID": {
30
+ "Type": "Pass",
31
+ "Next": "Sleep",
32
+ "Parameters": {
33
+ "uuid.$": "States.UUID()"
34
+ }
35
+ },
36
+ "Sleep": {
37
+ "Type": "Task",
38
+ "Resource": "docker://docker.io/agrare/sleep:latest",
39
+ "End": true
40
+ }
41
+ }
42
+ },
43
+ "End": true
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "Comment": "Parallel Example.",
3
+ "StartAt": "FunWithMath",
4
+ "States": {
5
+ "FunWithMath": {
6
+ "Type": "Parallel",
7
+ "End": true,
8
+ "Branches": [
9
+ {
10
+ "StartAt": "Add",
11
+ "States": {
12
+ "Add": {
13
+ "Type": "Task",
14
+ "Resource": "docker://docker.io/agrare/sleep:latest",
15
+ "End": true
16
+ }
17
+ }
18
+ },
19
+ {
20
+ "StartAt": "Subtract",
21
+ "States": {
22
+ "Subtract": {
23
+ "Type": "Task",
24
+ "Resource": "docker://docker.io/agrare/sleep:latest",
25
+ "End": true
26
+ }
27
+ }
28
+ }
29
+ ]
30
+ }
31
+ }
32
+ }
data/lib/floe/cli.rb CHANGED
@@ -1,9 +1,12 @@
1
+ require "floe"
2
+ require "floe/container_runner"
3
+
1
4
  module Floe
2
5
  class CLI
6
+ include Logging
7
+
3
8
  def initialize
4
9
  require "optimist"
5
- require "floe"
6
- require "floe/container_runner"
7
10
  require "logger"
8
11
 
9
12
  Floe.logger = Logger.new($stdout)
@@ -13,25 +16,29 @@ module Floe
13
16
  def run(args = ARGV)
14
17
  workflows_inputs, opts = parse_options!(args)
15
18
 
16
- credentials =
17
- if opts[:credentials_given]
18
- opts[:credentials] == "-" ? $stdin.read : opts[:credentials]
19
- elsif opts[:credentials_file_given]
20
- File.read(opts[:credentials_file])
21
- end
19
+ credentials = create_credentials(opts)
22
20
 
23
21
  workflows =
24
22
  workflows_inputs.each_slice(2).map do |workflow, input|
25
- context = Floe::Workflow::Context.new(opts[:context], :input => input, :credentials => credentials)
26
- Floe::Workflow.load(workflow, context)
23
+ create_workflow(workflow, opts[:context], input, credentials)
27
24
  end
28
25
 
29
- Floe::Workflow.wait(workflows, &:run_nonblock)
26
+ output_streams = create_loggers(workflows, opts[:segment_output])
27
+
28
+ logger.info("Checking #{workflows.count} workflows...")
29
+ ready = Floe::Workflow.wait(workflows, &:run_nonblock)
30
+ logger.info("Checking #{workflows.count} workflows...Complete - #{ready.count} ready")
30
31
 
31
32
  # Display status
32
33
  workflows.each do |workflow|
33
- puts "", "#{workflow.name}#{" (#{workflow.status})" unless workflow.context.success?}", "===" if workflows.size > 1
34
- puts workflow.output
34
+ if workflows.size > 1
35
+ logger.info("")
36
+ logger.info("#{workflow.name}#{" (#{workflow.status})" unless workflow.context.success?}")
37
+ logger.info("===")
38
+ end
39
+
40
+ logger.info(output_streams[workflow].string) if output_streams[workflow]
41
+ logger.info(workflow.output)
35
42
  end
36
43
 
37
44
  workflows.all? { |workflow| workflow.context.success? }
@@ -55,6 +62,7 @@ module Floe
55
62
  opt :context, "JSON payload of the Context", :type => :string
56
63
  opt :credentials, "JSON payload with Credentials", :type => :string
57
64
  opt :credentials_file, "Path to a file with Credentials", :type => :string
65
+ opt :segment_output, "Segment output by each worker", :default => false
58
66
 
59
67
  Floe::ContainerRunner.cli_options(self)
60
68
 
@@ -82,5 +90,30 @@ module Floe
82
90
 
83
91
  return workflows_inputs, opts
84
92
  end
93
+
94
+ def create_credentials(opts)
95
+ if opts[:credentials_given]
96
+ opts[:credentials] == "-" ? $stdin.read : opts[:credentials]
97
+ elsif opts[:credentials_file_given]
98
+ File.read(opts[:credentials_file])
99
+ end
100
+ end
101
+
102
+ def create_workflow(workflow, context_payload, input, credentials)
103
+ context = Floe::Workflow::Context.new(context_payload, :input => input, :credentials => credentials)
104
+ Floe::Workflow.load(workflow, context)
105
+ end
106
+
107
+ def create_loggers(workflows, segment_output)
108
+ if workflows.size == 1 || !segment_output
109
+ # no extra work necessary
110
+ {}
111
+ else
112
+ workflows.each_with_object({}) do |workflow, h|
113
+ workflow.context.logger = Logger.new(output = StringIO.new)
114
+ h[workflow] = output
115
+ end
116
+ end
117
+ end
85
118
  end
86
119
  end
@@ -30,7 +30,7 @@ module Floe
30
30
  end
31
31
 
32
32
  begin
33
- runner_context["container_ref"] = run_container(image, env, execution_id, runner_context["secrets_ref"])
33
+ runner_context["container_ref"] = run_container(image, env, execution_id, runner_context["secrets_ref"], context.logger)
34
34
  runner_context
35
35
  rescue AwesomeSpawn::CommandResultError => err
36
36
  cleanup(runner_context)
@@ -123,7 +123,7 @@ module Floe
123
123
 
124
124
  attr_reader :network
125
125
 
126
- def run_container(image, env, execution_id, secrets_file)
126
+ def run_container(image, env, execution_id, secrets_file, logger)
127
127
  params = run_container_params(image, env, execution_id, secrets_file)
128
128
 
129
129
  logger.debug("Running #{AwesomeSpawn.build_command_line(self.class::DOCKER_COMMAND, params)}")
@@ -182,8 +182,8 @@ module Floe
182
182
 
183
183
  def inspect_container(container_id)
184
184
  JSON.parse(docker!("inspect", container_id).output).first
185
- rescue
186
- nil
185
+ rescue AwesomeSpawn::CommandResultError => err
186
+ raise Floe::ExecutionError.new("Failed to get status for container #{container_id}: #{err}")
187
187
  end
188
188
 
189
189
  def delete_container(container_id)
@@ -155,6 +155,8 @@ module Floe
155
155
 
156
156
  def pod_info(pod_name)
157
157
  kubeclient.get_pod(pod_name, namespace)
158
+ rescue Kubeclient::HttpError => err
159
+ raise Floe::ExecutionError.new("Failed to get status for pod #{namespace}/#{pod_name}: #{err}")
158
160
  end
159
161
 
160
162
  def pod_running?(context)
@@ -285,7 +287,8 @@ module Floe
285
287
  code = notice.object&.code
286
288
  reason = notice.object&.reason
287
289
 
288
- logger.warn("Received [#{code} #{reason}], [#{message}]")
290
+ # This feels like a global concern and not an end user's concern
291
+ Floe.logger.warn("Received [#{code} #{reason}], [#{message}]")
289
292
 
290
293
  true
291
294
  end
data/lib/floe/logging.rb CHANGED
@@ -7,7 +7,11 @@ module Floe
7
7
  end
8
8
 
9
9
  def logger
10
- Floe.logger
10
+ @logger || Floe.logger
11
+ end
12
+
13
+ def logger=(logger)
14
+ @logger = logger
11
15
  end
12
16
  end
13
17
  end
data/lib/floe/runner.rb CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  module Floe
4
4
  class Runner
5
- include Logging
6
-
7
5
  OUTPUT_MARKER = "__FLOE_OUTPUT__\n"
8
6
 
9
7
  def initialize(_options = {}) # rubocop:disable Style/RedundantInitialize
data/lib/floe/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Floe
4
- VERSION = "0.14.0"
4
+ VERSION = "0.15.1"
5
5
  end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Floe
4
+ class Workflow
5
+ class Branch < Floe::WorkflowBase
6
+ end
7
+ end
8
+ end
@@ -4,16 +4,20 @@ module Floe
4
4
  class Workflow
5
5
  class ChoiceRule
6
6
  class Data < Floe::Workflow::ChoiceRule
7
- COMPARE_KEYS = %w[IsNull IsPresent IsNumeric IsString IsBoolean IsTimestamp String Numeric Boolean Timestamp].freeze
7
+ TYPES = ["String", "Numeric", "Boolean", "Timestamp", "Present", "Null"].freeze
8
+ COMPARES = ["Equals", "LessThan", "GreaterThan", "LessThanEquals", "GreaterThanEquals", "Matches"].freeze
9
+ # e.g.: (Is)(String), (Is)(Present)
10
+ TYPE_CHECK = /^(Is)(#{TYPES.join("|")})$/.freeze
11
+ # e.g.: (String)(LessThan)(Path), (Numeric)(GreaterThanEquals)()
12
+ OPERATION = /^(#{(TYPES - %w[Null Present]).join("|")})(#{COMPARES.join("|")})(Path)?$/.freeze
8
13
 
9
- attr_reader :variable, :compare_key, :value, :path
14
+ attr_reader :variable, :compare_key, :operation, :type, :compare_predicate, :path
10
15
 
11
16
  def initialize(_workflow, _name, payload)
12
17
  super
13
18
 
14
- @variable = parse_path("Variable", payload)
19
+ @variable = parse_path("Variable")
15
20
  parse_compare_key
16
- @value = path ? parse_path(compare_key, payload) : payload[compare_key]
17
21
  end
18
22
 
19
23
  def true?(context, input)
@@ -21,39 +25,7 @@ module Floe
21
25
 
22
26
  lhs = variable_value(context, input)
23
27
  rhs = compare_value(context, input)
24
-
25
- case compare_key
26
- when "IsNull" then is_null?(lhs, rhs)
27
- when "IsNumeric" then is_numeric?(lhs, rhs)
28
- when "IsString" then is_string?(lhs, rhs)
29
- when "IsBoolean" then is_boolean?(lhs, rhs)
30
- when "IsTimestamp" then is_timestamp?(lhs, rhs)
31
- when "StringEquals", "StringEqualsPath",
32
- "NumericEquals", "NumericEqualsPath",
33
- "BooleanEquals", "BooleanEqualsPath",
34
- "TimestampEquals", "TimestampEqualsPath"
35
- lhs == rhs
36
- when "StringLessThan", "StringLessThanPath",
37
- "NumericLessThan", "NumericLessThanPath",
38
- "TimestampLessThan", "TimestampLessThanPath"
39
- lhs < rhs
40
- when "StringGreaterThan", "StringGreaterThanPath",
41
- "NumericGreaterThan", "NumericGreaterThanPath",
42
- "TimestampGreaterThan", "TimestampGreaterThanPath"
43
- lhs > rhs
44
- when "StringLessThanEquals", "StringLessThanEqualsPath",
45
- "NumericLessThanEquals", "NumericLessThanEqualsPath",
46
- "TimestampLessThanEquals", "TimestampLessThanEqualsPath"
47
- lhs <= rhs
48
- when "StringGreaterThanEquals", "StringGreaterThanEqualsPath",
49
- "NumericGreaterThanEquals", "NumericGreaterThanEqualsPath",
50
- "TimestampGreaterThanEquals", "TimestampGreaterThanEqualsPath"
51
- lhs >= rhs
52
- when "StringMatches"
53
- lhs.match?(Regexp.escape(rhs).gsub('\*', '.*?'))
54
- else
55
- raise Floe::InvalidWorkflowError, "Invalid choice [#{compare_key}]"
56
- end
28
+ send(operation, lhs, rhs)
57
29
  end
58
30
 
59
31
  private
@@ -108,26 +80,101 @@ module Floe
108
80
  # rubocop:enable Naming/PredicateName
109
81
  # rubocop:enable Style/OptionalBooleanParameter
110
82
 
83
+ def equals?(lhs, rhs)
84
+ lhs == rhs
85
+ end
86
+
87
+ def lessthan?(lhs, rhs)
88
+ lhs < rhs
89
+ end
90
+
91
+ def greaterthan?(lhs, rhs)
92
+ lhs > rhs
93
+ end
94
+
95
+ def lessthanequals?(lhs, rhs)
96
+ lhs <= rhs
97
+ end
98
+
99
+ def greaterthanequals?(lhs, rhs)
100
+ lhs >= rhs
101
+ end
102
+
103
+ def matches?(lhs, rhs)
104
+ lhs.match?(Regexp.escape(rhs).gsub('\*', '.*?'))
105
+ end
106
+
107
+ # parse the compare key at initialization time
111
108
  def parse_compare_key
112
- @compare_key = payload.keys.detect { |key| key.match?(/^(#{COMPARE_KEYS.join("|")})/) }
113
- parser_error!("requires a compare key") unless compare_key
109
+ payload.each_key do |key|
110
+ # e.g. (String)(GreaterThan)(Path)
111
+ if (match_values = OPERATION.match(key))
112
+ @compare_key = key
113
+ @type, operator, @path = match_values.captures
114
+ @operation = "#{operator.downcase}?".to_sym
115
+ @compare_predicate = parse_predicate(type)
116
+ break
117
+ end
118
+ # e.g. (Is)(String)
119
+ if (match_value = TYPE_CHECK.match(key))
120
+ @compare_key = key
121
+ _operator, type = match_value.captures
122
+ # type: nil means no runtime type checking.
123
+ @type = @path = nil
124
+ @operation = "is_#{type.downcase}?".to_sym
125
+ @compare_predicate = parse_predicate("Boolean")
126
+ break
127
+ end
128
+ end
129
+ parser_error!("requires a compare key") if compare_key.nil? || operation.nil?
130
+ end
114
131
 
115
- @path = compare_key.end_with?("Path")
132
+ # parse predicate at initilization time
133
+ # @return the right predicate attached to the compare key
134
+ def parse_predicate(data_type)
135
+ path ? parse_path(compare_key) : parse_field(compare_key, data_type)
116
136
  end
117
137
 
138
+ # @return right hand predicate - input path or static payload value)
118
139
  def compare_value(context, input)
119
- path ? value.value(context, input) : value
140
+ path ? fetch_path(compare_key, compare_predicate, context, input) : compare_predicate
120
141
  end
121
142
 
143
+ # feth the variable value at runtime
144
+ # @return variable value (left hand side )
122
145
  def variable_value(context, input)
123
- variable.value(context, input)
146
+ fetch_path("Variable", variable, context, input)
124
147
  end
125
148
 
126
- def parse_path(field_name, payload)
149
+ # parse path at initilization time
150
+ # helper method to parse a path from the payload
151
+ def parse_path(field_name)
127
152
  value = payload[field_name]
128
153
  missing_field_error!(field_name) unless value
129
154
  wrap_parser_error(field_name, value) { Path.new(value) }
130
155
  end
156
+
157
+ # parse predicate field at initialization time
158
+ def parse_field(field_name, data_type)
159
+ value = payload[field_name]
160
+ return value if correct_type?(value, data_type)
161
+
162
+ invalid_field_error!(field_name, value, "required to be a #{data_type}")
163
+ end
164
+
165
+ # fetch a path at runtime
166
+ def fetch_path(field_name, field_path, context, input)
167
+ value = field_path.value(context, input)
168
+ return value if type.nil? || correct_type?(value, type)
169
+
170
+ runtime_field_error!(field_name, field_path.to_s, "required to point to a #{type}")
171
+ end
172
+
173
+ # if we have runtime checking, check against that type
174
+ # otherwise assume checking a TYPE_CHECK predicate and check against Boolean
175
+ def correct_type?(value, data_type)
176
+ send("is_#{data_type.downcase}?".to_sym, value)
177
+ end
131
178
  end
132
179
  end
133
180
  end
@@ -3,11 +3,13 @@
3
3
  module Floe
4
4
  class Workflow
5
5
  class Context
6
+ include Logging
7
+
6
8
  attr_accessor :credentials
7
9
 
8
10
  # @param context [Json|Hash] (default, create another with input and execution params)
9
11
  # @param input [Hash] (default: {})
10
- def initialize(context = nil, input: nil, credentials: {})
12
+ def initialize(context = nil, input: nil, credentials: {}, logger: nil)
11
13
  context = JSON.parse(context) if context.kind_of?(String)
12
14
  input = JSON.parse(input || "{}")
13
15
 
@@ -20,6 +22,8 @@ module Floe
20
22
  self["Task"] ||= {}
21
23
 
22
24
  @credentials = credentials || {}
25
+
26
+ self.logger = logger if logger
23
27
  rescue JSON::ParserError => err
24
28
  raise Floe::InvalidExecutionInput, "Invalid State Machine Execution Input: #{err}: was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')"
25
29
  end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Floe
4
+ class Workflow
5
+ class ItemProcessor < Floe::WorkflowBase
6
+ attr_reader :processor_config
7
+
8
+ def initialize(payload, name = nil)
9
+ super
10
+ @processor_config = payload.fetch("ProcessorConfig", "INLINE")
11
+ end
12
+ end
13
+ end
14
+ end
@@ -3,7 +3,6 @@
3
3
  module Floe
4
4
  class Workflow
5
5
  class State
6
- include Logging
7
6
  include ValidationMixin
8
7
 
9
8
  class << self
@@ -56,14 +55,22 @@ module Floe
56
55
  mark_started(context)
57
56
  end
58
57
 
58
+ def started?(context)
59
+ context.state_started?
60
+ end
61
+
59
62
  def finish(context)
60
63
  mark_finished(context)
61
64
  end
62
65
 
66
+ def finished?(context)
67
+ context.state_finished?
68
+ end
69
+
63
70
  def mark_started(context)
64
71
  context.state["EnteredTime"] = Time.now.utc.iso8601
65
72
 
66
- logger.info("Running state: [#{long_name}] with input [#{context.json_input}]...")
73
+ context.logger.info("Running state: [#{long_name}] with input [#{context.json_input}]...")
67
74
  end
68
75
 
69
76
  def mark_finished(context)
@@ -74,7 +81,7 @@ module Floe
74
81
  context.state["Duration"] = finished_time - entered_time
75
82
 
76
83
  level = context.failed? ? :error : :info
77
- logger.public_send(level, "Running state: [#{long_name}] with input [#{context.json_input}]...Complete #{context.next_state ? "- next state [#{context.next_state}]" : "workflow -"} output: [#{context.json_output}]")
84
+ context.logger.public_send(level, "Running state: [#{long_name}] with input [#{context.json_input}]...Complete #{context.next_state ? "- next state [#{context.next_state}]" : "workflow -"} output: [#{context.json_output}]")
78
85
 
79
86
  0
80
87
  end
@@ -88,7 +95,7 @@ module Floe
88
95
  end
89
96
 
90
97
  def ready?(context)
91
- !context.state_started? || !running?(context)
98
+ !started?(context) || !running?(context)
92
99
  end
93
100
 
94
101
  def running?(context)