floe 0.13.0 → 0.14.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +19 -1
- data/lib/floe/container_runner/docker.rb +11 -10
- data/lib/floe/container_runner/kubernetes.rb +10 -8
- data/lib/floe/container_runner/podman.rb +8 -5
- data/lib/floe/validation_mixin.rb +9 -1
- data/lib/floe/version.rb +1 -1
- data/lib/floe/workflow/choice_rule/data.rb +55 -26
- data/lib/floe/workflow/choice_rule.rb +31 -6
- data/lib/floe/workflow/intrinsic_function/transformer.rb +2 -6
- data/lib/floe/workflow/state.rb +2 -2
- data/lib/floe/workflow/states/choice.rb +1 -0
- data/lib/floe/workflow.rb +4 -2
- data/lib/floe.rb +12 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0e0b86c08d322e2ebc8e115e5193d0493f8257c831f7377b18c32d4284ae6f2
|
4
|
+
data.tar.gz: 1bf5ed62abafe2e1af6025dbaf47d9faa3b1c93ffec8fd88fbeacd4445efc795
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 34f64555f2472c121fcea9dda8baac7fbe853cdbc5e949c831d7b7f070878d3ef2f86ed90d907b5a7033539fcad6aeea64a4dee6475a752d7f89e4f679a4b269
|
7
|
+
data.tar.gz: 378c5bcd562d4bbeb7e3eb13eb55b1d5d8e7aed0ea4749c2b65f951cf3be0e5f13d3e288cdbf63b4a5729e98c5010656d6dca3db60ffbf8463988008980edd83
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,22 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
5
5
|
## [Unreleased]
|
6
6
|
|
7
|
+
## [0.14.0] - 2024-08-20
|
8
|
+
### Added
|
9
|
+
- Implement "IsNumeric": false ([#266](https://github.com/ManageIQ/floe/pull/266))
|
10
|
+
- Support choices that do not have a Default defined ([#267](https://github.com/ManageIQ/floe/pull/267))
|
11
|
+
- Label containers/pods with workflow Execution ID ([#268](https://github.com/ManageIQ/floe/pull/268))
|
12
|
+
- Allow for Execution Id to be passed in ([#269](https://github.com/ManageIQ/floe/pull/269))
|
13
|
+
|
14
|
+
## [0.13.1] - 2024-08-16
|
15
|
+
### Fixed
|
16
|
+
- Fix podman/docker container_ref trailing newline ([#265](https://github.com/ManageIQ/floe/pull/265))
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
- Improve type check for States.Hash ([#261](https://github.com/ManageIQ/floe/pull/261))
|
20
|
+
- Use Numeric over Integer || Float ([#264](https://github.com/ManageIQ/floe/pull/264))
|
21
|
+
- In ChoiceRule::Data, parse compare key and variable ([#257](https://github.com/ManageIQ/floe/pull/257))
|
22
|
+
|
7
23
|
## [0.13.0] - 2024-08-12
|
8
24
|
### Added
|
9
25
|
- Choice rule payload validation path ([#253](https://github.com/ManageIQ/floe/pull/253))
|
@@ -233,7 +249,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
233
249
|
### Added
|
234
250
|
- Initial release
|
235
251
|
|
236
|
-
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.
|
252
|
+
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.14.0...HEAD
|
253
|
+
[0.14.0]: https://github.com/ManageIQ/floe/compare/v0.13.1...v0.14.0
|
254
|
+
[0.13.1]: https://github.com/ManageIQ/floe/compare/v0.13.0...v0.13.1
|
237
255
|
[0.13.0]: https://github.com/ManageIQ/floe/compare/v0.12.0...v0.13.0
|
238
256
|
[0.12.0]: https://github.com/ManageIQ/floe/compare/v0.11.3...v0.12.0
|
239
257
|
[0.11.3]: https://github.com/ManageIQ/floe/compare/v0.11.2...v0.11.3
|
@@ -18,11 +18,11 @@ module Floe
|
|
18
18
|
@pull_policy = options["pull-policy"]
|
19
19
|
end
|
20
20
|
|
21
|
-
def run_async!(resource, env
|
21
|
+
def run_async!(resource, env, secrets, context)
|
22
22
|
raise ArgumentError, "Invalid resource" unless resource&.start_with?("docker://")
|
23
23
|
|
24
|
-
image
|
25
|
-
|
24
|
+
image = resource.sub("docker://", "")
|
25
|
+
execution_id = context.execution["Id"]
|
26
26
|
runner_context = {}
|
27
27
|
|
28
28
|
if secrets && !secrets.empty?
|
@@ -30,7 +30,7 @@ module Floe
|
|
30
30
|
end
|
31
31
|
|
32
32
|
begin
|
33
|
-
runner_context["container_ref"] = run_container(image, env, runner_context["secrets_ref"])
|
33
|
+
runner_context["container_ref"] = run_container(image, env, execution_id, runner_context["secrets_ref"])
|
34
34
|
runner_context
|
35
35
|
rescue AwesomeSpawn::CommandResultError => err
|
36
36
|
cleanup(runner_context)
|
@@ -123,22 +123,23 @@ module Floe
|
|
123
123
|
|
124
124
|
attr_reader :network
|
125
125
|
|
126
|
-
def run_container(image, env, secrets_file)
|
127
|
-
params = run_container_params(image, env, secrets_file)
|
126
|
+
def run_container(image, env, execution_id, secrets_file)
|
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)}")
|
130
130
|
|
131
131
|
result = docker!(*params)
|
132
|
-
result.output
|
132
|
+
result.output.chomp
|
133
133
|
end
|
134
134
|
|
135
|
-
def run_container_params(image, env, secrets_file)
|
135
|
+
def run_container_params(image, env, execution_id, secrets_file)
|
136
136
|
params = ["run"]
|
137
137
|
params << :detach
|
138
138
|
params += env.map { |k, v| [:e, "#{k}=#{v}"] }
|
139
139
|
params << [:e, "_CREDENTIALS=/run/secrets"] if secrets_file
|
140
140
|
params << [:pull, @pull_policy] if @pull_policy
|
141
141
|
params << [:net, "host"] if @network == "host"
|
142
|
+
params << [:label, "execution_id=#{execution_id}"]
|
142
143
|
params << [:v, "#{secrets_file}:/run/secrets:z"] if secrets_file
|
143
144
|
params << [:name, container_name(image)]
|
144
145
|
params << image
|
@@ -157,11 +158,11 @@ module Floe
|
|
157
158
|
event = docker_event_status_to_event(status)
|
158
159
|
running = event != :delete
|
159
160
|
|
160
|
-
name, exit_code = notice.dig("Actor", "Attributes")&.values_at("name", "exitCode")
|
161
|
+
name, exit_code, execution_id = notice.dig("Actor", "Attributes")&.values_at("name", "exitCode", "execution_id")
|
161
162
|
|
162
163
|
runner_context = {"container_ref" => name, "container_state" => {"Running" => running, "ExitCode" => exit_code.to_i}}
|
163
164
|
|
164
|
-
[event, runner_context]
|
165
|
+
[event, {"execution_id" => execution_id, "runner_context" => runner_context}]
|
165
166
|
rescue JSON::ParserError
|
166
167
|
[]
|
167
168
|
end
|
@@ -45,17 +45,17 @@ module Floe
|
|
45
45
|
super
|
46
46
|
end
|
47
47
|
|
48
|
-
def run_async!(resource, env
|
48
|
+
def run_async!(resource, env, secrets, context)
|
49
49
|
raise ArgumentError, "Invalid resource" unless resource&.start_with?("docker://")
|
50
50
|
|
51
51
|
image = resource.sub("docker://", "")
|
52
52
|
name = container_name(image)
|
53
53
|
secret = create_secret!(secrets) if secrets && !secrets.empty?
|
54
|
-
|
54
|
+
execution_id = context.execution["Id"]
|
55
55
|
runner_context = {"container_ref" => name, "container_state" => {"phase" => "Pending"}, "secrets_ref" => secret}
|
56
56
|
|
57
57
|
begin
|
58
|
-
create_pod!(name, image, env, secret)
|
58
|
+
create_pod!(name, image, env, execution_id, secret)
|
59
59
|
runner_context
|
60
60
|
rescue Kubeclient::HttpError => err
|
61
61
|
cleanup(runner_context)
|
@@ -171,13 +171,14 @@ module Floe
|
|
171
171
|
failed_container_states(context).any?
|
172
172
|
end
|
173
173
|
|
174
|
-
def pod_spec(name, image, env, secret = nil)
|
174
|
+
def pod_spec(name, image, env, execution_id, secret = nil)
|
175
175
|
spec = {
|
176
176
|
:kind => "Pod",
|
177
177
|
:apiVersion => "v1",
|
178
178
|
:metadata => {
|
179
179
|
:name => name,
|
180
|
-
:namespace => namespace
|
180
|
+
:namespace => namespace,
|
181
|
+
:labels => {"execution_id" => execution_id}
|
181
182
|
},
|
182
183
|
:spec => {
|
183
184
|
:containers => [
|
@@ -219,8 +220,8 @@ module Floe
|
|
219
220
|
spec
|
220
221
|
end
|
221
222
|
|
222
|
-
def create_pod!(name, image, env, secret = nil)
|
223
|
-
kubeclient.create_pod(pod_spec(name, image, env, secret))
|
223
|
+
def create_pod!(name, image, env, execution_id, secret = nil)
|
224
|
+
kubeclient.create_pod(pod_spec(name, image, env, execution_id, secret))
|
224
225
|
end
|
225
226
|
|
226
227
|
def delete_pod!(name)
|
@@ -294,9 +295,10 @@ module Floe
|
|
294
295
|
|
295
296
|
pod = notice.object
|
296
297
|
container_ref = pod.metadata.name
|
298
|
+
execution_id = pod.metadata.labels["execution_id"]
|
297
299
|
container_state = pod.to_h[:status].deep_stringify_keys
|
298
300
|
|
299
|
-
{"container_ref" => container_ref, "container_state" => container_state}
|
301
|
+
{"execution_id" => execution_id, "runner_context" => {"container_ref" => container_ref, "container_state" => container_state}}
|
300
302
|
end
|
301
303
|
|
302
304
|
def kubeclient
|
@@ -30,13 +30,14 @@ module Floe
|
|
30
30
|
|
31
31
|
private
|
32
32
|
|
33
|
-
def run_container_params(image, env, secret)
|
33
|
+
def run_container_params(image, env, execution_id, secret)
|
34
34
|
params = ["run"]
|
35
35
|
params << :detach
|
36
36
|
params += env.map { |k, v| [:e, "#{k}=#{v}"] }
|
37
37
|
params << [:e, "_CREDENTIALS=/run/secrets/#{secret}"] if secret
|
38
38
|
params << [:pull, @pull_policy] if @pull_policy
|
39
39
|
params << [:net, "host"] if @network == "host"
|
40
|
+
params << [:label, "execution_id=#{execution_id}"]
|
40
41
|
params << [:secret, secret] if secret
|
41
42
|
params << [:name, container_name(image)]
|
42
43
|
params << image
|
@@ -55,14 +56,16 @@ module Floe
|
|
55
56
|
end
|
56
57
|
|
57
58
|
def parse_notice(notice)
|
58
|
-
|
59
|
+
notice = JSON.parse(notice)
|
60
|
+
id, status, exit_code, attributes = notice.values_at("ID", "Status", "ContainerExitCode", "Attributes")
|
59
61
|
|
60
|
-
|
61
|
-
|
62
|
+
execution_id = attributes&.dig("execution_id")
|
63
|
+
event = podman_event_status_to_event(status)
|
64
|
+
running = event != :delete
|
62
65
|
|
63
66
|
runner_context = {"container_ref" => id, "container_state" => {"Running" => running, "ExitCode" => exit_code.to_i}}
|
64
67
|
|
65
|
-
[event, runner_context]
|
68
|
+
[event, {"execution_id" => execution_id, "runner_context" => runner_context}]
|
66
69
|
rescue JSON::ParserError
|
67
70
|
[]
|
68
71
|
end
|
@@ -18,6 +18,10 @@ module Floe
|
|
18
18
|
self.class.invalid_field_error!(name, field_name, field_value, comment)
|
19
19
|
end
|
20
20
|
|
21
|
+
def runtime_field_error!(field_name, field_value, comment, floe_error: "States.Runtime")
|
22
|
+
raise Floe::ExecutionError.new(self.class.field_error_text(name, field_name, field_value, comment), floe_error)
|
23
|
+
end
|
24
|
+
|
21
25
|
def workflow_state?(field_value, workflow)
|
22
26
|
workflow.payload["States"] ? workflow.payload["States"].include?(field_value) : true
|
23
27
|
end
|
@@ -39,10 +43,14 @@ module Floe
|
|
39
43
|
end
|
40
44
|
|
41
45
|
def invalid_field_error!(name, field_name, field_value, comment)
|
46
|
+
raise Floe::InvalidWorkflowError, field_error_text(name, field_name, field_value, comment)
|
47
|
+
end
|
48
|
+
|
49
|
+
def field_error_text(name, field_name, field_value, comment = nil)
|
42
50
|
# instead of displaying a large hash or array, just displaying the word Hash or Array
|
43
51
|
field_value = field_value.class if field_value.kind_of?(Hash) || field_value.kind_of?(Array)
|
44
52
|
|
45
|
-
|
53
|
+
"#{Array(name).join(".")} field \"#{field_name}\"#{" value \"#{field_value}\"" unless field_value.nil?} #{comment}"
|
46
54
|
end
|
47
55
|
end
|
48
56
|
end
|
data/lib/floe/version.rb
CHANGED
@@ -4,6 +4,18 @@ 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
|
8
|
+
|
9
|
+
attr_reader :variable, :compare_key, :value, :path
|
10
|
+
|
11
|
+
def initialize(_workflow, _name, payload)
|
12
|
+
super
|
13
|
+
|
14
|
+
@variable = parse_path("Variable", payload)
|
15
|
+
parse_compare_key
|
16
|
+
@value = path ? parse_path(compare_key, payload) : payload[compare_key]
|
17
|
+
end
|
18
|
+
|
7
19
|
def true?(context, input)
|
8
20
|
return presence_check(context, input) if compare_key == "IsPresent"
|
9
21
|
|
@@ -11,11 +23,11 @@ module Floe
|
|
11
23
|
rhs = compare_value(context, input)
|
12
24
|
|
13
25
|
case compare_key
|
14
|
-
when "IsNull" then is_null?(lhs)
|
15
|
-
when "IsNumeric" then is_numeric?(lhs)
|
16
|
-
when "IsString" then is_string?(lhs)
|
17
|
-
when "IsBoolean" then is_boolean?(lhs)
|
18
|
-
when "IsTimestamp" then is_timestamp?(lhs)
|
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)
|
19
31
|
when "StringEquals", "StringEqualsPath",
|
20
32
|
"NumericEquals", "NumericEqualsPath",
|
21
33
|
"BooleanEquals", "BooleanEqualsPath",
|
@@ -50,54 +62,71 @@ module Floe
|
|
50
62
|
# Get the right hand side for {"Variable": "$.foo", "IsPresent": true} i.e.: true
|
51
63
|
# If true then return true when present.
|
52
64
|
# If false then return true when not present.
|
53
|
-
|
65
|
+
predicate = compare_value(context, input)
|
54
66
|
# Don't need the variable_value, just need to see if the path finds the value.
|
55
67
|
variable_value(context, input)
|
56
68
|
|
57
69
|
# The variable_value is present
|
58
|
-
# If
|
59
|
-
|
70
|
+
# If predicate is true, then presence check was successful, return true.
|
71
|
+
predicate
|
60
72
|
rescue Floe::PathError
|
61
73
|
# variable_value is not present. (the path lookup threw an error)
|
62
|
-
# If
|
63
|
-
!
|
74
|
+
# If predicate is false, then it successfully wasn't present, return true.
|
75
|
+
!predicate
|
64
76
|
end
|
65
77
|
|
66
|
-
|
67
|
-
|
78
|
+
# rubocop:disable Naming/PredicateName
|
79
|
+
# rubocop:disable Style/OptionalBooleanParameter
|
80
|
+
def is_null?(value, predicate = true)
|
81
|
+
value.nil? == predicate
|
68
82
|
end
|
69
83
|
|
70
|
-
def is_present?(value
|
71
|
-
!value.nil?
|
84
|
+
def is_present?(value, predicate = true)
|
85
|
+
!value.nil? == predicate
|
72
86
|
end
|
73
87
|
|
74
|
-
def is_numeric?(value
|
75
|
-
value.kind_of?(
|
88
|
+
def is_numeric?(value, predicate = true)
|
89
|
+
value.kind_of?(Numeric) == predicate
|
76
90
|
end
|
77
91
|
|
78
|
-
def is_string?(value
|
79
|
-
value.kind_of?(String)
|
92
|
+
def is_string?(value, predicate = true)
|
93
|
+
value.kind_of?(String) == predicate
|
80
94
|
end
|
81
95
|
|
82
|
-
def is_boolean?(value
|
83
|
-
[true, false].include?(value)
|
96
|
+
def is_boolean?(value, predicate = true)
|
97
|
+
[true, false].include?(value) == predicate
|
84
98
|
end
|
85
99
|
|
86
|
-
def is_timestamp?(value
|
100
|
+
def is_timestamp?(value, predicate = true)
|
87
101
|
require "date"
|
88
102
|
|
89
103
|
DateTime.rfc3339(value)
|
90
|
-
|
104
|
+
predicate
|
91
105
|
rescue TypeError, Date::Error
|
92
|
-
|
106
|
+
!predicate
|
93
107
|
end
|
108
|
+
# rubocop:enable Naming/PredicateName
|
109
|
+
# rubocop:enable Style/OptionalBooleanParameter
|
94
110
|
|
95
|
-
def
|
96
|
-
@compare_key
|
111
|
+
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
|
114
|
+
|
115
|
+
@path = compare_key.end_with?("Path")
|
97
116
|
end
|
98
117
|
|
99
118
|
def compare_value(context, input)
|
100
|
-
|
119
|
+
path ? value.value(context, input) : value
|
120
|
+
end
|
121
|
+
|
122
|
+
def variable_value(context, input)
|
123
|
+
variable.value(context, input)
|
124
|
+
end
|
125
|
+
|
126
|
+
def parse_path(field_name, payload)
|
127
|
+
value = payload[field_name]
|
128
|
+
missing_field_error!(field_name) unless value
|
129
|
+
wrap_parser_error(field_name, value) { Path.new(value) }
|
101
130
|
end
|
102
131
|
end
|
103
132
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
module Floe
|
4
4
|
class Workflow
|
5
5
|
class ChoiceRule
|
6
|
+
include ValidationMixin
|
7
|
+
|
6
8
|
class << self
|
7
9
|
def build(workflow, name, payload)
|
8
10
|
if (sub_payloads = payload["Not"])
|
@@ -25,15 +27,15 @@ module Floe
|
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
28
|
-
attr_reader :next, :payload, :
|
30
|
+
attr_reader :next, :payload, :children, :name
|
29
31
|
|
30
|
-
def initialize(
|
32
|
+
def initialize(workflow, name, payload, children = nil)
|
31
33
|
@name = name
|
32
34
|
@payload = payload
|
33
35
|
@children = children
|
36
|
+
@next = payload["Next"]
|
34
37
|
|
35
|
-
|
36
|
-
@variable = payload["Variable"]
|
38
|
+
validate_next!(workflow)
|
37
39
|
end
|
38
40
|
|
39
41
|
def true?(*)
|
@@ -42,8 +44,31 @@ module Floe
|
|
42
44
|
|
43
45
|
private
|
44
46
|
|
45
|
-
def
|
46
|
-
|
47
|
+
def validate_next!(workflow)
|
48
|
+
if is_child?
|
49
|
+
# non-top level nodes don't allow a next
|
50
|
+
invalid_field_error!("Next", @next, "not allowed in a child rule") if @next
|
51
|
+
elsif !@next
|
52
|
+
# top level nodes require a next
|
53
|
+
missing_field_error!("Next")
|
54
|
+
elsif !workflow_state?(@next, workflow)
|
55
|
+
# top level nodes require a next field that is found
|
56
|
+
invalid_field_error!("Next", @next, "is not found in \"States\"")
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# returns true if this is a child rule underneath an And/Or/Not
|
61
|
+
# {
|
62
|
+
# "Or": [
|
63
|
+
# {"Variable": "$.foo", "IsString": true},
|
64
|
+
# {"Variable": "$.foo", "IsBoolean": true}
|
65
|
+
# ], "Next": "Finished"
|
66
|
+
# }
|
67
|
+
#
|
68
|
+
# The Or node, has no conjunction parent, so it is not a child (requires a Next)
|
69
|
+
# The 2 Data nodes have a conjunction parent, so each one is a child (do not allow a Next)
|
70
|
+
def is_child? # rubocop:disable Naming/PredicateName
|
71
|
+
!(%w[And Or Not] & name[0..-2]).empty?
|
47
72
|
end
|
48
73
|
end
|
49
74
|
end
|
@@ -80,7 +80,7 @@ module Floe
|
|
80
80
|
STATES_FORMAT_PLACEHOLDER = /(?<!\\)\{\}/.freeze
|
81
81
|
|
82
82
|
rule(:states_format => {:args => subtree(:args)}) do
|
83
|
-
args = Transformer.process_args(args(), "States.Format", [String, VariadicArgs[[String, TrueClass, FalseClass,
|
83
|
+
args = Transformer.process_args(args(), "States.Format", [String, VariadicArgs[[String, TrueClass, FalseClass, Numeric, NilClass]]])
|
84
84
|
str, *rest = *args
|
85
85
|
|
86
86
|
# TODO: Handle templates with escaped characters, including invalid templates
|
@@ -191,13 +191,9 @@ module Floe
|
|
191
191
|
end
|
192
192
|
|
193
193
|
rule(:states_hash => {:args => subtree(:args)}) do
|
194
|
-
args = Transformer.process_args(args(), "States.Hash", [
|
194
|
+
args = Transformer.process_args(args(), "States.Hash", [[String, TrueClass, FalseClass, Numeric, Array, Hash], String])
|
195
195
|
data, algorithm = *args
|
196
196
|
|
197
|
-
if data.nil?
|
198
|
-
raise ArgumentError, "invalid value for argument 1 to States.Hash (given null, expected non-null)"
|
199
|
-
end
|
200
|
-
|
201
197
|
algorithms = %w[MD5 SHA-1 SHA-256 SHA-384 SHA-512]
|
202
198
|
unless algorithms.include?(algorithm)
|
203
199
|
raise ArgumentError, "invalid value for argument 2 to States.Hash (given #{algorithm.inspect}, expected one of #{algorithms.map(&:inspect).join(", ")})"
|
data/lib/floe/workflow/state.rb
CHANGED
@@ -48,7 +48,7 @@ module Floe
|
|
48
48
|
return Errno::EAGAIN unless ready?(context)
|
49
49
|
|
50
50
|
finish(context)
|
51
|
-
rescue Floe::
|
51
|
+
rescue Floe::ExecutionError => e
|
52
52
|
mark_error(context, e)
|
53
53
|
end
|
54
54
|
|
@@ -82,7 +82,7 @@ module Floe
|
|
82
82
|
def mark_error(context, exception)
|
83
83
|
# InputPath or OutputPath were bad.
|
84
84
|
context.next_state = nil
|
85
|
-
context.output = {"Error" =>
|
85
|
+
context.output = {"Error" => exception.floe_error, "Cause" => exception.message}
|
86
86
|
# Since finish threw an exception, super was never called. Calling that now.
|
87
87
|
mark_finished(context)
|
88
88
|
end
|
@@ -23,6 +23,7 @@ module Floe
|
|
23
23
|
output = output_path.value(context, input)
|
24
24
|
next_state = choices.detect { |choice| choice.true?(context, output) }&.next || default
|
25
25
|
|
26
|
+
runtime_field_error!("Default", nil, "not defined and no match found", :floe_error => "States.NoChoiceMatched") if next_state.nil?
|
26
27
|
context.next_state = next_state
|
27
28
|
context.output = output
|
28
29
|
super
|
data/lib/floe/workflow.rb
CHANGED
@@ -63,9 +63,11 @@ module Floe
|
|
63
63
|
|
64
64
|
loop do
|
65
65
|
# Block until an event is raised
|
66
|
-
event,
|
66
|
+
event, data = queue.pop
|
67
67
|
break if event.nil?
|
68
68
|
|
69
|
+
_execution_id, runner_context = data.values_at("execution_id", "runner_context")
|
70
|
+
|
69
71
|
# If the event is for one of our workflows set the updated runner_context
|
70
72
|
workflows.each do |workflow|
|
71
73
|
next unless workflow.context.state.dig("RunnerContext", "container_ref") == runner_context["container_ref"]
|
@@ -175,7 +177,7 @@ module Floe
|
|
175
177
|
context.state["Input"] = context.execution["Input"].dup
|
176
178
|
context.state["Guid"] = SecureRandom.uuid
|
177
179
|
|
178
|
-
context.execution["Id"]
|
180
|
+
context.execution["Id"] ||= SecureRandom.uuid
|
179
181
|
context.execution["StartTime"] = Time.now.utc.iso8601
|
180
182
|
|
181
183
|
self
|
data/lib/floe.rb
CHANGED
@@ -43,7 +43,18 @@ module Floe
|
|
43
43
|
class Error < StandardError; end
|
44
44
|
class InvalidWorkflowError < Error; end
|
45
45
|
class InvalidExecutionInput < Error; end
|
46
|
-
|
46
|
+
|
47
|
+
class ExecutionError < Error
|
48
|
+
attr_reader :floe_error
|
49
|
+
|
50
|
+
def initialize(message, floe_error = "States.Runtime")
|
51
|
+
super(message)
|
52
|
+
@floe_error = floe_error
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class PathError < ExecutionError
|
57
|
+
end
|
47
58
|
|
48
59
|
def self.logger
|
49
60
|
@logger ||= NullLogger.new
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: floe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ManageIQ Developers
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-08-
|
11
|
+
date: 2024-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: awesome_spawn
|