floe 0.17.1 → 0.18.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/floe.gemspec +4 -3
- data/lib/floe/builtin_runner/methods.rb +2 -2
- data/lib/floe/builtin_runner/runner.rb +2 -2
- data/lib/floe/container_runner/docker.rb +2 -2
- data/lib/floe/container_runner/kubernetes.rb +2 -2
- data/lib/floe/validation_mixin.rb +0 -4
- data/lib/floe/version.rb +1 -1
- data/lib/floe/workflow/catcher.rb +1 -1
- data/lib/floe/workflow/choice_rule/data.rb +76 -51
- data/lib/floe/workflow/choice_rule.rb +1 -1
- data/lib/floe/workflow/context.rb +10 -1
- data/lib/floe/workflow/intrinsic_function/transformer.rb +5 -5
- data/lib/floe/workflow/payload_template.rb +1 -1
- data/lib/floe/workflow/states/choice.rb +1 -1
- data/lib/floe/workflow/states/map.rb +1 -1
- data/lib/floe/workflow/states/non_terminal_mixin.rb +1 -1
- data/lib/floe/workflow_base.rb +5 -1
- metadata +45 -31
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 40373ebff63056265508d03a1f3d12c3f10c9933b0dbcaffee700140838797cb
|
|
4
|
+
data.tar.gz: 51df9c1251e26551c8eb07e3bf16ed17775753b5f62b8407f83b813349063a12
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 57201f74d63b63a34c250f615b06013e9355828ed8a8e0007ba229f9b0b49d370428db87f067e0860dbef59bbd349e274a4573b661b34ca69c69ce5d65df1c01
|
|
7
|
+
data.tar.gz: e50628793fc80d43fa51fc266b26e1dc8dfeee6c98b9ffbed8c2a0d495ade115c4b77c68d9bcb2d059e1b0f5d573e40315530b837805d8e5b90bbfd9ddef291a
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,21 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.18.0] - 2025-10-24
|
|
8
|
+
### Added
|
|
9
|
+
- Declare active support dependency ([#316](https://github.com/ManageIQ/floe/pull/316))
|
|
10
|
+
- Add Context equality comparison ([#317](https://github.com/ManageIQ/floe/pull/317))
|
|
11
|
+
- Choice rule payload validation ([#189](https://github.com/ManageIQ/floe/pull/189))
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
- In ChoiceRule use Regexp.union ([#323](https://github.com/ManageIQ/floe/pull/323))
|
|
15
|
+
- Remove special case for choice rule IsPresent ([#322](https://github.com/ManageIQ/floe/pull/322))
|
|
16
|
+
- Rename data operation parameters ([#324](https://github.com/ManageIQ/floe/pull/324))
|
|
17
|
+
- Refactor workflow state check to not use payload ([#326](https://github.com/ManageIQ/floe/pull/326))
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
- Fix missing parameters in item_batcher_spec ([#325](https://github.com/ManageIQ/floe/pull/325))
|
|
21
|
+
|
|
7
22
|
## [0.17.1] - 2025-08-25
|
|
8
23
|
### Changed
|
|
9
24
|
- Default Method=GET for floe://http method parameter ([#315](https://github.com/ManageIQ/floe/pull/315))
|
|
@@ -302,7 +317,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
302
317
|
### Added
|
|
303
318
|
- Initial release
|
|
304
319
|
|
|
305
|
-
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.
|
|
320
|
+
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.18.0...HEAD
|
|
321
|
+
[0.18.0]: https://github.com/ManageIQ/floe/compare/v0.17.1...v0.18.0
|
|
322
|
+
[0.17.1]: https://github.com/ManageIQ/floe/compare/v0.17.0...v0.17.1
|
|
323
|
+
[0.17.0]: https://github.com/ManageIQ/floe/compare/v0.16.0...v0.17.0
|
|
306
324
|
[0.16.0]: https://github.com/ManageIQ/floe/compare/v0.16.0...v0.17.0
|
|
307
325
|
[0.16.0]: https://github.com/ManageIQ/floe/compare/v0.15.1...v0.16.0
|
|
308
326
|
[0.15.1]: https://github.com/ManageIQ/floe/compare/v0.15.0...v0.15.1
|
data/floe.gemspec
CHANGED
|
@@ -31,15 +31,16 @@ Gem::Specification.new do |spec|
|
|
|
31
31
|
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
32
32
|
spec.require_paths = ["lib"]
|
|
33
33
|
|
|
34
|
+
spec.add_dependency "activesupport", ">5.2"
|
|
34
35
|
spec.add_dependency "awesome_spawn", "~>1.6"
|
|
36
|
+
spec.add_dependency "faraday"
|
|
37
|
+
spec.add_dependency "faraday-follow_redirects"
|
|
35
38
|
spec.add_dependency "io-wait"
|
|
39
|
+
spec.add_dependency "json", "~>2.10"
|
|
36
40
|
spec.add_dependency "jsonpath", "~>1.1"
|
|
37
41
|
spec.add_dependency "kubeclient", "~>4.7"
|
|
38
42
|
spec.add_dependency "optimist", "~>3.0"
|
|
39
43
|
spec.add_dependency "parslet", "~>2.0"
|
|
40
|
-
spec.add_dependency "json", "~>2.10"
|
|
41
|
-
spec.add_dependency "faraday"
|
|
42
|
-
spec.add_dependency "faraday-follow_redirects"
|
|
43
44
|
|
|
44
45
|
spec.add_development_dependency "manageiq-style", ">= 1.5.2"
|
|
45
46
|
spec.add_development_dependency "rake", "~> 13.0"
|
|
@@ -59,8 +59,8 @@ module Floe
|
|
|
59
59
|
connection.response(:follow_redirects)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
-
response = connection.send(method.downcase) do |
|
|
63
|
-
|
|
62
|
+
response = connection.send(method.downcase) do |faraday_request|
|
|
63
|
+
faraday_request.body = body if body
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
output = {"Status" => response.status, "Body" => response.body, "Headers" => response.headers}
|
|
@@ -23,7 +23,7 @@ module Floe
|
|
|
23
23
|
method_name = runner_context["method"]
|
|
24
24
|
raise ArgumentError if method_name.nil?
|
|
25
25
|
|
|
26
|
-
cleanup_method = "#{method_name}_cleanup"
|
|
26
|
+
cleanup_method = :"#{method_name}_cleanup"
|
|
27
27
|
return unless Methods.respond_to?(cleanup_method, true)
|
|
28
28
|
|
|
29
29
|
Methods.send(cleanup_method, runner_context)
|
|
@@ -34,7 +34,7 @@ module Floe
|
|
|
34
34
|
raise ArgumentError if method_name.nil?
|
|
35
35
|
return if runner_context["running"] == false
|
|
36
36
|
|
|
37
|
-
Methods.send("#{method_name}_status!", runner_context)
|
|
37
|
+
Methods.send(:"#{method_name}_status!", runner_context)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def running?(runner_context)
|
|
@@ -176,14 +176,14 @@ module Floe
|
|
|
176
176
|
when "die", "destroy"
|
|
177
177
|
:delete
|
|
178
178
|
else
|
|
179
|
-
:
|
|
179
|
+
:unknown
|
|
180
180
|
end
|
|
181
181
|
end
|
|
182
182
|
|
|
183
183
|
def inspect_container(container_id)
|
|
184
184
|
JSON.parse(docker!("inspect", container_id).output).first
|
|
185
185
|
rescue AwesomeSpawn::CommandResultError => err
|
|
186
|
-
raise Floe::ExecutionError
|
|
186
|
+
raise Floe::ExecutionError, "Failed to get status for container #{container_id}: #{err}"
|
|
187
187
|
end
|
|
188
188
|
|
|
189
189
|
def delete_container(container_id)
|
|
@@ -11,7 +11,7 @@ module Floe
|
|
|
11
11
|
FAILURE_REASONS = %w[CrashLoopBackOff ImagePullBackOff ErrImagePull].freeze
|
|
12
12
|
|
|
13
13
|
def initialize(options = {})
|
|
14
|
-
require "active_support/core_ext/hash/keys"
|
|
14
|
+
require "active_support/core_ext/hash/keys" # deep_stringify_keys
|
|
15
15
|
require "awesome_spawn"
|
|
16
16
|
require "securerandom"
|
|
17
17
|
require "base64"
|
|
@@ -156,7 +156,7 @@ module Floe
|
|
|
156
156
|
def pod_info(pod_name)
|
|
157
157
|
kubeclient.get_pod(pod_name, namespace)
|
|
158
158
|
rescue Kubeclient::HttpError => err
|
|
159
|
-
raise Floe::ExecutionError
|
|
159
|
+
raise Floe::ExecutionError, "Failed to get status for pod #{namespace}/#{pod_name}: #{err}"
|
|
160
160
|
end
|
|
161
161
|
|
|
162
162
|
def pod_running?(context)
|
|
@@ -22,10 +22,6 @@ module Floe
|
|
|
22
22
|
raise Floe::ExecutionError.new(self.class.field_error_text(name, field_name, field_value, comment), floe_error)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
-
def workflow_state?(field_value, workflow)
|
|
26
|
-
workflow.payload["States"] ? workflow.payload["States"].include?(field_value) : true
|
|
27
|
-
end
|
|
28
|
-
|
|
29
25
|
def wrap_parser_error(field_name, field_value)
|
|
30
26
|
yield
|
|
31
27
|
rescue ArgumentError, InvalidWorkflowError => error
|
data/lib/floe/version.rb
CHANGED
|
@@ -24,7 +24,7 @@ module Floe
|
|
|
24
24
|
|
|
25
25
|
def validate_state_next!(workflow)
|
|
26
26
|
missing_field_error!("Next") if @next.nil?
|
|
27
|
-
invalid_field_error!("Next", @next, "is not found in \"States\"") if @next && !
|
|
27
|
+
invalid_field_error!("Next", @next, "is not found in \"States\"") if @next && !workflow.state?(@next)
|
|
28
28
|
end
|
|
29
29
|
end
|
|
30
30
|
end
|
|
@@ -6,12 +6,14 @@ module Floe
|
|
|
6
6
|
class Data < Floe::Workflow::ChoiceRule
|
|
7
7
|
TYPES = ["String", "Numeric", "Boolean", "Timestamp", "Present", "Null"].freeze
|
|
8
8
|
COMPARES = ["Equals", "LessThan", "GreaterThan", "LessThanEquals", "GreaterThanEquals", "Matches"].freeze
|
|
9
|
+
OPERATIONS = TYPES.each_with_object({}) { |dt, a| a[dt] = :"is_#{dt.downcase}?" }
|
|
10
|
+
.merge(COMPARES.each_with_object({}) { |op, a| a[op] = :"op_#{op.downcase}?" }).freeze
|
|
9
11
|
# e.g.: (Is)(String), (Is)(Present)
|
|
10
|
-
TYPE_CHECK = /^
|
|
12
|
+
TYPE_CHECK = /^Is(#{Regexp.union(TYPES)})$/
|
|
11
13
|
# e.g.: (String)(LessThan)(Path), (Numeric)(GreaterThanEquals)()
|
|
12
|
-
OPERATION = /^(#{(TYPES - %w[Null Present])
|
|
14
|
+
OPERATION = /^(#{Regexp.union(TYPES - %w[Null Present])})(#{Regexp.union(COMPARES)})(Path)?$/
|
|
13
15
|
|
|
14
|
-
attr_reader :variable, :compare_key, :
|
|
16
|
+
attr_reader :variable, :compare_key, :operator, :type, :compare_predicate, :path
|
|
15
17
|
|
|
16
18
|
def initialize(_workflow, _name, payload)
|
|
17
19
|
super
|
|
@@ -20,62 +22,80 @@ module Floe
|
|
|
20
22
|
parse_compare_key
|
|
21
23
|
end
|
|
22
24
|
|
|
25
|
+
# Evaluate whether this rule is true for the given context and input (runtime)
|
|
26
|
+
#
|
|
27
|
+
# @param context [Context] The workflow execution context
|
|
28
|
+
# @param input [Hash] The current state input
|
|
29
|
+
# @return [Boolean] true if the rule evaluate to true
|
|
23
30
|
def true?(context, input)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
31
|
+
# Payload pattern is: {"Variable": $lhs, $operator: $rhs}
|
|
32
|
+
# Example:
|
|
33
|
+
#
|
|
34
|
+
# {"Variable": "$.foo", "IsNumeric": true}
|
|
35
|
+
# lhs = input["$.foo"]
|
|
36
|
+
# rhs = true
|
|
37
|
+
# is_numeric?(lhs, rhs)
|
|
38
|
+
#
|
|
39
|
+
# {"Variable": "$.foo", "GreaterThanString": "aaa"}
|
|
40
|
+
# lhs = input["$.foo"]
|
|
41
|
+
# rhs = "aaa"
|
|
42
|
+
# op_greaterthan?(lhs, rhs)
|
|
43
|
+
#
|
|
44
|
+
# {"Variable": "$.foo", "GreaterThanNumericPath": "$.bar"}
|
|
45
|
+
# lhs = input["$.foo"]
|
|
46
|
+
# rhs = input["$.bar"]
|
|
47
|
+
# op_greaterthan?(lhs, rhs)
|
|
48
|
+
#
|
|
49
|
+
# NOTE: IsPresent works a little differently as lhs might raise a PathError.
|
|
50
|
+
# See the exception handler below. This is why we process the rhs before the lhs.
|
|
27
51
|
rhs = compare_value(context, input)
|
|
28
|
-
|
|
52
|
+
lhs = variable_value(context, input)
|
|
53
|
+
send(OPERATIONS[operator], lhs, rhs)
|
|
54
|
+
rescue Floe::PathError
|
|
55
|
+
# For IsPresent, we can expect the lhs to not be present in some cases,
|
|
56
|
+
# This throws a PathError. We handle that special case here.
|
|
57
|
+
# Example:
|
|
58
|
+
#
|
|
59
|
+
# {"Variable": "$.foo", "IsPresent": false}
|
|
60
|
+
# lhs = input["$.foo"], but variable is not present. (The variable lookup threw PathError)
|
|
61
|
+
# rhs = false
|
|
62
|
+
return is_present?(:not_present, rhs) if operator == "Present"
|
|
63
|
+
|
|
64
|
+
# for non "IsPresent" checks, share that lhs or rhs is not found.
|
|
65
|
+
raise
|
|
29
66
|
end
|
|
30
67
|
|
|
31
68
|
private
|
|
32
69
|
|
|
33
|
-
def presence_check(context, input)
|
|
34
|
-
# Get the right hand side for {"Variable": "$.foo", "IsPresent": true} i.e.: true
|
|
35
|
-
# If true then return true when present.
|
|
36
|
-
# If false then return true when not present.
|
|
37
|
-
predicate = compare_value(context, input)
|
|
38
|
-
# Don't need the variable_value, just need to see if the path finds the value.
|
|
39
|
-
variable_value(context, input)
|
|
40
|
-
|
|
41
|
-
# The variable_value is present
|
|
42
|
-
# If predicate is true, then presence check was successful, return true.
|
|
43
|
-
predicate
|
|
44
|
-
rescue Floe::PathError
|
|
45
|
-
# variable_value is not present. (the path lookup threw an error)
|
|
46
|
-
# If predicate is false, then it successfully wasn't present, return true.
|
|
47
|
-
!predicate
|
|
48
|
-
end
|
|
49
|
-
|
|
50
70
|
# rubocop:disable Naming/PredicateName
|
|
51
71
|
# rubocop:disable Style/OptionalBooleanParameter
|
|
52
|
-
def is_null?(value,
|
|
53
|
-
value.nil? ==
|
|
72
|
+
def is_null?(value, expectation)
|
|
73
|
+
value.nil? == expectation
|
|
54
74
|
end
|
|
55
75
|
|
|
56
|
-
def is_present?(value,
|
|
57
|
-
|
|
76
|
+
def is_present?(value, expectation)
|
|
77
|
+
(value != :not_present) == expectation
|
|
58
78
|
end
|
|
59
79
|
|
|
60
|
-
def is_numeric?(value,
|
|
61
|
-
value.kind_of?(Numeric) ==
|
|
80
|
+
def is_numeric?(value, expectation)
|
|
81
|
+
value.kind_of?(Numeric) == expectation
|
|
62
82
|
end
|
|
63
83
|
|
|
64
|
-
def is_string?(value,
|
|
65
|
-
value.kind_of?(String) ==
|
|
84
|
+
def is_string?(value, expectation)
|
|
85
|
+
value.kind_of?(String) == expectation
|
|
66
86
|
end
|
|
67
87
|
|
|
68
|
-
def is_boolean?(value,
|
|
69
|
-
[true, false].include?(value) ==
|
|
88
|
+
def is_boolean?(value, expectation)
|
|
89
|
+
[true, false].include?(value) == expectation
|
|
70
90
|
end
|
|
71
91
|
|
|
72
|
-
def is_timestamp?(value,
|
|
92
|
+
def is_timestamp?(value, expectation)
|
|
73
93
|
require "date"
|
|
74
94
|
|
|
75
95
|
DateTime.rfc3339(value)
|
|
76
|
-
|
|
96
|
+
expectation
|
|
77
97
|
rescue TypeError, Date::Error
|
|
78
|
-
!
|
|
98
|
+
!expectation
|
|
79
99
|
end
|
|
80
100
|
# rubocop:enable Naming/PredicateName
|
|
81
101
|
# rubocop:enable Style/OptionalBooleanParameter
|
|
@@ -100,8 +120,8 @@ module Floe
|
|
|
100
120
|
lhs >= rhs
|
|
101
121
|
end
|
|
102
122
|
|
|
103
|
-
def op_matches?(
|
|
104
|
-
|
|
123
|
+
def op_matches?(value, pattern)
|
|
124
|
+
value.match?(Regexp.escape(pattern).gsub('\*', '.*?'))
|
|
105
125
|
end
|
|
106
126
|
|
|
107
127
|
# parse the compare key at initialization time
|
|
@@ -110,26 +130,26 @@ module Floe
|
|
|
110
130
|
# e.g. (String)(GreaterThan)(Path)
|
|
111
131
|
if (match_values = OPERATION.match(key))
|
|
112
132
|
@compare_key = key
|
|
113
|
-
@type, operator, @path = match_values.captures
|
|
114
|
-
@operation = "op_#{operator.downcase}?".to_sym
|
|
133
|
+
@type, @operator, @path = match_values.captures
|
|
115
134
|
@compare_predicate = parse_predicate(type)
|
|
116
135
|
break
|
|
117
|
-
end
|
|
118
136
|
# e.g. (Is)(String)
|
|
119
|
-
|
|
137
|
+
elsif (match_value = TYPE_CHECK.match(key))
|
|
120
138
|
@compare_key = key
|
|
121
|
-
|
|
139
|
+
@operator = match_value.captures.first
|
|
122
140
|
# type: nil means no runtime type checking.
|
|
123
141
|
@type = @path = nil
|
|
124
|
-
@operation = "is_#{type.downcase}?".to_sym
|
|
125
142
|
@compare_predicate = parse_predicate("Boolean")
|
|
126
143
|
break
|
|
127
144
|
end
|
|
128
145
|
end
|
|
129
|
-
parser_error!("requires a compare key") if compare_key.nil? ||
|
|
146
|
+
parser_error!("requires a compare key") if compare_key.nil? || operator.nil?
|
|
130
147
|
end
|
|
131
148
|
|
|
132
|
-
# parse predicate at
|
|
149
|
+
# parse predicate at initialization time
|
|
150
|
+
# @param data_type [String] the data type of the variable
|
|
151
|
+
# When parsing operations (IntegerGreaterThan), this will be the operation data type (e.g.: Integer)
|
|
152
|
+
# When parsing type checks (IsString), this will always be a Boolean
|
|
133
153
|
# @return the right predicate attached to the compare key
|
|
134
154
|
def parse_predicate(data_type)
|
|
135
155
|
path ? parse_path(compare_key) : parse_field(compare_key, data_type)
|
|
@@ -140,13 +160,13 @@ module Floe
|
|
|
140
160
|
path ? fetch_path(compare_key, compare_predicate, context, input) : compare_predicate
|
|
141
161
|
end
|
|
142
162
|
|
|
143
|
-
#
|
|
144
|
-
# @return variable value (left hand side
|
|
163
|
+
# fetch the variable value at runtime
|
|
164
|
+
# @return variable value (left hand side)
|
|
145
165
|
def variable_value(context, input)
|
|
146
166
|
fetch_path("Variable", variable, context, input)
|
|
147
167
|
end
|
|
148
168
|
|
|
149
|
-
# parse path at
|
|
169
|
+
# parse path at initialization time
|
|
150
170
|
# helper method to parse a path from the payload
|
|
151
171
|
def parse_path(field_name)
|
|
152
172
|
value = payload[field_name]
|
|
@@ -155,6 +175,10 @@ module Floe
|
|
|
155
175
|
end
|
|
156
176
|
|
|
157
177
|
# parse predicate field at initialization time
|
|
178
|
+
# @param field_name [String] the compare key
|
|
179
|
+
# @param data_type [String] the data type of the variable
|
|
180
|
+
# When parsing operations (IntegerGreaterThan), this will be the operation data type (e.g.: Integer)
|
|
181
|
+
# When parsing type checks (IsString), this will always be a Boolean
|
|
158
182
|
def parse_field(field_name, data_type)
|
|
159
183
|
value = payload[field_name]
|
|
160
184
|
return value if correct_type?(value, data_type)
|
|
@@ -165,6 +189,7 @@ module Floe
|
|
|
165
189
|
# fetch a path at runtime
|
|
166
190
|
def fetch_path(field_name, field_path, context, input)
|
|
167
191
|
value = field_path.value(context, input)
|
|
192
|
+
# if this is an operation (GreaterThanPath), ensure the value is the correct type
|
|
168
193
|
return value if type.nil? || correct_type?(value, type)
|
|
169
194
|
|
|
170
195
|
runtime_field_error!(field_name, field_path.to_s, "required to point to a #{type}")
|
|
@@ -173,7 +198,7 @@ module Floe
|
|
|
173
198
|
# if we have runtime checking, check against that type
|
|
174
199
|
# otherwise assume checking a TYPE_CHECK predicate and check against Boolean
|
|
175
200
|
def correct_type?(value, data_type)
|
|
176
|
-
send(
|
|
201
|
+
send(OPERATIONS[data_type], value, true)
|
|
177
202
|
end
|
|
178
203
|
end
|
|
179
204
|
end
|
|
@@ -51,7 +51,7 @@ module Floe
|
|
|
51
51
|
elsif !@next
|
|
52
52
|
# top level nodes require a next
|
|
53
53
|
missing_field_error!("Next")
|
|
54
|
-
elsif !
|
|
54
|
+
elsif !workflow.state?(@next)
|
|
55
55
|
# top level nodes require a next field that is found
|
|
56
56
|
invalid_field_error!("Next", @next, "is not found in \"States\"")
|
|
57
57
|
end
|
|
@@ -140,13 +140,22 @@ module Floe
|
|
|
140
140
|
end
|
|
141
141
|
|
|
142
142
|
def inspect
|
|
143
|
-
|
|
143
|
+
"#<#{self.class.name}: #{safe_context.inspect}>"
|
|
144
144
|
end
|
|
145
145
|
|
|
146
146
|
def to_h
|
|
147
147
|
safe_context
|
|
148
148
|
end
|
|
149
149
|
|
|
150
|
+
def ==(other)
|
|
151
|
+
other.kind_of?(self.class) && other.instance_variable_get(:@context) == @context
|
|
152
|
+
end
|
|
153
|
+
alias eql? ==
|
|
154
|
+
|
|
155
|
+
def hash
|
|
156
|
+
@context.hash
|
|
157
|
+
end
|
|
158
|
+
|
|
150
159
|
private
|
|
151
160
|
|
|
152
161
|
def safe_context
|
|
@@ -77,7 +77,7 @@ module Floe
|
|
|
77
77
|
rule(:number => simple(:v)) { v.match(/[eE.]/) ? Float(v) : Integer(v) }
|
|
78
78
|
rule(:jsonpath => simple(:v)) { Floe::Workflow::Path.value(v.to_s, context, input) }
|
|
79
79
|
|
|
80
|
-
STATES_FORMAT_PLACEHOLDER = /(?<!\\)\{\}
|
|
80
|
+
STATES_FORMAT_PLACEHOLDER = /(?<!\\)\{\}/
|
|
81
81
|
|
|
82
82
|
rule(:states_format => {:args => subtree(:args)}) do
|
|
83
83
|
args = Transformer.process_args(args(), "States.Format", [String, VariadicArgs[[String, TrueClass, FalseClass, Numeric, NilClass]]])
|
|
@@ -236,15 +236,15 @@ module Floe
|
|
|
236
236
|
|
|
237
237
|
rule(:states_string_split => {:args => subtree(:args)}) do
|
|
238
238
|
args = Transformer.process_args(args(), "States.StringSplit", [String, String])
|
|
239
|
-
str,
|
|
239
|
+
str, delimiter = *args
|
|
240
240
|
|
|
241
|
-
case
|
|
241
|
+
case delimiter.size
|
|
242
242
|
when 0
|
|
243
243
|
str.empty? ? [] : [str]
|
|
244
244
|
when 1
|
|
245
|
-
str.split(
|
|
245
|
+
str.split(delimiter)
|
|
246
246
|
else
|
|
247
|
-
str.split(/[#{Regexp.escape(
|
|
247
|
+
str.split(/[#{Regexp.escape(delimiter)}]+/)
|
|
248
248
|
end
|
|
249
249
|
end
|
|
250
250
|
|
|
@@ -48,7 +48,7 @@ module Floe
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def check_dynamic_datatype(key, value)
|
|
51
|
-
unless value.
|
|
51
|
+
unless value.kind_of?(String)
|
|
52
52
|
raise Floe::InvalidWorkflowError, "The value for the field \"#{key}\" must be a String that contains a valid Reference Path or Intrinsic Function expression"
|
|
53
53
|
end
|
|
54
54
|
end
|
|
@@ -49,7 +49,7 @@ module Floe
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
def validate_state_default!(workflow)
|
|
52
|
-
invalid_field_error!("Default", @default, "is not found in \"States\"") if @default && !
|
|
52
|
+
invalid_field_error!("Default", @default, "is not found in \"States\"") if @default && !workflow.state?(@default)
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
end
|
|
@@ -13,7 +13,7 @@ module Floe
|
|
|
13
13
|
|
|
14
14
|
def validate_state_next!(workflow)
|
|
15
15
|
missing_field_error!("Next") if @next.nil? && !@end
|
|
16
|
-
invalid_field_error!("Next", @next, "is not found in \"States\"") if @next && !
|
|
16
|
+
invalid_field_error!("Next", @next, "is not found in \"States\"") if @next && !workflow.state?(@next)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
end
|
data/lib/floe/workflow_base.rb
CHANGED
|
@@ -77,6 +77,10 @@ module Floe
|
|
|
77
77
|
context.output.to_json if end?(context)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
|
+
def state?(state_name)
|
|
81
|
+
@payload["States"].include?(state_name)
|
|
82
|
+
end
|
|
83
|
+
|
|
80
84
|
private
|
|
81
85
|
|
|
82
86
|
def step!(context)
|
|
@@ -102,7 +106,7 @@ module Floe
|
|
|
102
106
|
def validate_workflow!
|
|
103
107
|
missing_field_error!("States") if @states.empty?
|
|
104
108
|
missing_field_error!("StartAt") if @start_at.nil?
|
|
105
|
-
invalid_field_error!("StartAt", @start_at, "is not found in \"States\"") unless
|
|
109
|
+
invalid_field_error!("StartAt", @start_at, "is not found in \"States\"") unless state?(@start_at)
|
|
106
110
|
end
|
|
107
111
|
end
|
|
108
112
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: floe
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.18.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ManageIQ Developers
|
|
@@ -9,6 +9,20 @@ bindir: exe
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: activesupport
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '5.2'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '5.2'
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: awesome_spawn
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -24,7 +38,7 @@ dependencies:
|
|
|
24
38
|
- !ruby/object:Gem::Version
|
|
25
39
|
version: '1.6'
|
|
26
40
|
- !ruby/object:Gem::Dependency
|
|
27
|
-
name:
|
|
41
|
+
name: faraday
|
|
28
42
|
requirement: !ruby/object:Gem::Requirement
|
|
29
43
|
requirements:
|
|
30
44
|
- - ">="
|
|
@@ -38,103 +52,103 @@ dependencies:
|
|
|
38
52
|
- !ruby/object:Gem::Version
|
|
39
53
|
version: '0'
|
|
40
54
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
55
|
+
name: faraday-follow_redirects
|
|
42
56
|
requirement: !ruby/object:Gem::Requirement
|
|
43
57
|
requirements:
|
|
44
|
-
- - "
|
|
58
|
+
- - ">="
|
|
45
59
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '
|
|
60
|
+
version: '0'
|
|
47
61
|
type: :runtime
|
|
48
62
|
prerelease: false
|
|
49
63
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
64
|
requirements:
|
|
51
|
-
- - "
|
|
65
|
+
- - ">="
|
|
52
66
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
67
|
+
version: '0'
|
|
54
68
|
- !ruby/object:Gem::Dependency
|
|
55
|
-
name:
|
|
69
|
+
name: io-wait
|
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|
|
57
71
|
requirements:
|
|
58
|
-
- - "
|
|
72
|
+
- - ">="
|
|
59
73
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
74
|
+
version: '0'
|
|
61
75
|
type: :runtime
|
|
62
76
|
prerelease: false
|
|
63
77
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
78
|
requirements:
|
|
65
|
-
- - "
|
|
79
|
+
- - ">="
|
|
66
80
|
- !ruby/object:Gem::Version
|
|
67
|
-
version: '
|
|
81
|
+
version: '0'
|
|
68
82
|
- !ruby/object:Gem::Dependency
|
|
69
|
-
name:
|
|
83
|
+
name: json
|
|
70
84
|
requirement: !ruby/object:Gem::Requirement
|
|
71
85
|
requirements:
|
|
72
86
|
- - "~>"
|
|
73
87
|
- !ruby/object:Gem::Version
|
|
74
|
-
version: '
|
|
88
|
+
version: '2.10'
|
|
75
89
|
type: :runtime
|
|
76
90
|
prerelease: false
|
|
77
91
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
92
|
requirements:
|
|
79
93
|
- - "~>"
|
|
80
94
|
- !ruby/object:Gem::Version
|
|
81
|
-
version: '
|
|
95
|
+
version: '2.10'
|
|
82
96
|
- !ruby/object:Gem::Dependency
|
|
83
|
-
name:
|
|
97
|
+
name: jsonpath
|
|
84
98
|
requirement: !ruby/object:Gem::Requirement
|
|
85
99
|
requirements:
|
|
86
100
|
- - "~>"
|
|
87
101
|
- !ruby/object:Gem::Version
|
|
88
|
-
version: '
|
|
102
|
+
version: '1.1'
|
|
89
103
|
type: :runtime
|
|
90
104
|
prerelease: false
|
|
91
105
|
version_requirements: !ruby/object:Gem::Requirement
|
|
92
106
|
requirements:
|
|
93
107
|
- - "~>"
|
|
94
108
|
- !ruby/object:Gem::Version
|
|
95
|
-
version: '
|
|
109
|
+
version: '1.1'
|
|
96
110
|
- !ruby/object:Gem::Dependency
|
|
97
|
-
name:
|
|
111
|
+
name: kubeclient
|
|
98
112
|
requirement: !ruby/object:Gem::Requirement
|
|
99
113
|
requirements:
|
|
100
114
|
- - "~>"
|
|
101
115
|
- !ruby/object:Gem::Version
|
|
102
|
-
version: '
|
|
116
|
+
version: '4.7'
|
|
103
117
|
type: :runtime
|
|
104
118
|
prerelease: false
|
|
105
119
|
version_requirements: !ruby/object:Gem::Requirement
|
|
106
120
|
requirements:
|
|
107
121
|
- - "~>"
|
|
108
122
|
- !ruby/object:Gem::Version
|
|
109
|
-
version: '
|
|
123
|
+
version: '4.7'
|
|
110
124
|
- !ruby/object:Gem::Dependency
|
|
111
|
-
name:
|
|
125
|
+
name: optimist
|
|
112
126
|
requirement: !ruby/object:Gem::Requirement
|
|
113
127
|
requirements:
|
|
114
|
-
- - "
|
|
128
|
+
- - "~>"
|
|
115
129
|
- !ruby/object:Gem::Version
|
|
116
|
-
version: '0'
|
|
130
|
+
version: '3.0'
|
|
117
131
|
type: :runtime
|
|
118
132
|
prerelease: false
|
|
119
133
|
version_requirements: !ruby/object:Gem::Requirement
|
|
120
134
|
requirements:
|
|
121
|
-
- - "
|
|
135
|
+
- - "~>"
|
|
122
136
|
- !ruby/object:Gem::Version
|
|
123
|
-
version: '0'
|
|
137
|
+
version: '3.0'
|
|
124
138
|
- !ruby/object:Gem::Dependency
|
|
125
|
-
name:
|
|
139
|
+
name: parslet
|
|
126
140
|
requirement: !ruby/object:Gem::Requirement
|
|
127
141
|
requirements:
|
|
128
|
-
- - "
|
|
142
|
+
- - "~>"
|
|
129
143
|
- !ruby/object:Gem::Version
|
|
130
|
-
version: '0'
|
|
144
|
+
version: '2.0'
|
|
131
145
|
type: :runtime
|
|
132
146
|
prerelease: false
|
|
133
147
|
version_requirements: !ruby/object:Gem::Requirement
|
|
134
148
|
requirements:
|
|
135
|
-
- - "
|
|
149
|
+
- - "~>"
|
|
136
150
|
- !ruby/object:Gem::Version
|
|
137
|
-
version: '0'
|
|
151
|
+
version: '2.0'
|
|
138
152
|
- !ruby/object:Gem::Dependency
|
|
139
153
|
name: manageiq-style
|
|
140
154
|
requirement: !ruby/object:Gem::Requirement
|