floe 0.11.2 → 0.12.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/.codeclimate.yml +16 -0
- data/.yamllint +1 -3
- data/CHANGELOG.md +51 -1
- data/Gemfile +1 -1
- data/LICENSE.txt +202 -0
- data/README.md +5 -1
- data/exe/floe +3 -72
- data/floe.gemspec +5 -4
- data/lib/floe/cli.rb +86 -0
- data/lib/floe/container_runner/docker_mixin.rb +3 -0
- data/lib/floe/runner.rb +9 -4
- data/lib/floe/validation_mixin.rb +49 -0
- data/lib/floe/version.rb +1 -1
- data/lib/floe/workflow/catcher.rb +17 -3
- data/lib/floe/workflow/choice_rule.rb +14 -9
- data/lib/floe/workflow/context.rb +27 -5
- data/lib/floe/workflow/error_matcher_mixin.rb +17 -0
- data/lib/floe/workflow/intrinsic_function/parser.rb +100 -0
- data/lib/floe/workflow/intrinsic_function/transformer.rb +196 -0
- data/lib/floe/workflow/intrinsic_function.rb +34 -0
- data/lib/floe/workflow/path.rb +7 -1
- data/lib/floe/workflow/payload_template.rb +7 -4
- data/lib/floe/workflow/reference_path.rb +2 -5
- data/lib/floe/workflow/retrier.rb +11 -4
- data/lib/floe/workflow/state.rb +33 -46
- data/lib/floe/workflow/states/choice.rb +12 -11
- data/lib/floe/workflow/states/fail.rb +3 -3
- data/lib/floe/workflow/states/input_output_mixin.rb +8 -8
- data/lib/floe/workflow/states/non_terminal_mixin.rb +6 -6
- data/lib/floe/workflow/states/pass.rb +7 -6
- data/lib/floe/workflow/states/succeed.rb +12 -3
- data/lib/floe/workflow/states/task.rb +35 -30
- data/lib/floe/workflow/states/wait.rb +8 -7
- data/lib/floe/workflow.rb +75 -23
- data/lib/floe.rb +6 -0
- metadata +31 -22
data/lib/floe/workflow.rb
CHANGED
@@ -6,6 +6,7 @@ require "json"
|
|
6
6
|
module Floe
|
7
7
|
class Workflow
|
8
8
|
include Logging
|
9
|
+
include ValidationMixin
|
9
10
|
|
10
11
|
class << self
|
11
12
|
def load(path_or_io, context = nil, credentials = {}, name = nil)
|
@@ -85,61 +86,73 @@ module Floe
|
|
85
86
|
end
|
86
87
|
end
|
87
88
|
|
88
|
-
attr_reader :context, :
|
89
|
+
attr_reader :context, :payload, :states, :states_by_name, :start_at, :name, :comment
|
89
90
|
|
90
|
-
def initialize(payload, context = nil, credentials =
|
91
|
+
def initialize(payload, context = nil, credentials = nil, name = nil)
|
91
92
|
payload = JSON.parse(payload) if payload.kind_of?(String)
|
92
93
|
credentials = JSON.parse(credentials) if credentials.kind_of?(String)
|
93
94
|
context = Context.new(context) unless context.kind_of?(Context)
|
94
95
|
|
95
|
-
|
96
|
-
|
97
|
-
|
96
|
+
# backwards compatibility
|
97
|
+
# caller should really put credentials into context and not pass that variable
|
98
|
+
context.credentials = credentials if credentials
|
98
99
|
|
99
|
-
|
100
|
+
# NOTE: this is a string, and states use an array
|
101
|
+
@name = name || "State Machine"
|
100
102
|
@payload = payload
|
101
103
|
@context = context
|
102
|
-
@credentials = credentials || {}
|
103
104
|
@comment = payload["Comment"]
|
104
105
|
@start_at = payload["StartAt"]
|
105
106
|
|
106
|
-
|
107
|
-
|
107
|
+
# NOTE: Everywhere else we include our name (i.e.: parent name) when building the child name.
|
108
|
+
# When creating the states, we are dropping our name (i.e.: the workflow name)
|
109
|
+
@states = payload["States"].to_a.map { |state_name, state| State.build!(self, ["States", state_name], state) }
|
108
110
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
111
|
+
validate_workflow
|
112
|
+
|
113
|
+
@states_by_name = @states.each_with_object({}) { |state, result| result[state.short_name] = state }
|
114
|
+
rescue Floe::Error
|
115
|
+
raise
|
116
|
+
rescue => err
|
114
117
|
raise Floe::InvalidWorkflowError, err.message
|
115
118
|
end
|
116
119
|
|
117
120
|
def run_nonblock
|
121
|
+
start_workflow
|
118
122
|
loop while step_nonblock == 0 && !end?
|
119
123
|
self
|
120
124
|
end
|
121
125
|
|
126
|
+
# NOTE: If running manually, make sure to call start_workflow at startup
|
122
127
|
def step_nonblock
|
123
128
|
return Errno::EPERM if end?
|
124
129
|
|
125
|
-
|
126
|
-
|
130
|
+
result = current_state.run_nonblock!(context)
|
131
|
+
return result if result != 0
|
132
|
+
|
133
|
+
# if it completed the step
|
134
|
+
context.state_history << context.state
|
135
|
+
context.next_state ? step! : end_workflow!
|
136
|
+
|
137
|
+
result
|
127
138
|
end
|
128
139
|
|
140
|
+
# if this hasn't started (and we have no current_state yet), assume it is ready
|
129
141
|
def step_nonblock_wait(timeout: nil)
|
130
|
-
current_state.wait(:timeout => timeout)
|
142
|
+
context.started? ? current_state.wait(context, :timeout => timeout) : 0
|
131
143
|
end
|
132
144
|
|
145
|
+
# if this hasn't started (and we have no current_state yet), assume it is ready
|
133
146
|
def step_nonblock_ready?
|
134
|
-
current_state.ready?
|
147
|
+
!context.started? || current_state.ready?(context)
|
135
148
|
end
|
136
149
|
|
137
150
|
def waiting?
|
138
|
-
current_state.waiting?
|
151
|
+
current_state.waiting?(context)
|
139
152
|
end
|
140
153
|
|
141
154
|
def wait_until
|
142
|
-
current_state.wait_until
|
155
|
+
current_state.wait_until(context)
|
143
156
|
end
|
144
157
|
|
145
158
|
def status
|
@@ -147,21 +160,60 @@ module Floe
|
|
147
160
|
end
|
148
161
|
|
149
162
|
def output
|
150
|
-
context.
|
163
|
+
context.json_output if end?
|
151
164
|
end
|
152
165
|
|
153
166
|
def end?
|
154
167
|
context.ended?
|
155
168
|
end
|
156
169
|
|
170
|
+
# setup a workflow
|
171
|
+
def start_workflow
|
172
|
+
return if context.state_name
|
173
|
+
|
174
|
+
context.state["Name"] = start_at
|
175
|
+
context.state["Input"] = context.execution["Input"].dup
|
176
|
+
context.state["Guid"] = SecureRandom.uuid
|
177
|
+
|
178
|
+
context.execution["Id"] = SecureRandom.uuid
|
179
|
+
context.execution["StartTime"] = Time.now.utc.iso8601
|
180
|
+
|
181
|
+
self
|
182
|
+
end
|
183
|
+
|
184
|
+
# NOTE: Expecting the context to be initialized (via start_workflow) before this
|
157
185
|
def current_state
|
158
186
|
@states_by_name[context.state_name]
|
159
187
|
end
|
160
188
|
|
189
|
+
# backwards compatibility. Caller should access directly from context
|
190
|
+
def credentials
|
191
|
+
@context.credentials
|
192
|
+
end
|
193
|
+
|
161
194
|
private
|
162
195
|
|
163
|
-
def
|
164
|
-
|
196
|
+
def validate_workflow
|
197
|
+
missing_field_error!("States") if @states.empty?
|
198
|
+
missing_field_error!("StartAt") if @start_at.nil?
|
199
|
+
invalid_field_error!("StartAt", @start_at, "is not found in \"States\"") unless workflow_state?(@start_at, self)
|
200
|
+
end
|
201
|
+
|
202
|
+
def step!
|
203
|
+
next_state = {"Name" => context.next_state, "Guid" => SecureRandom.uuid, "PreviousStateGuid" => context.state["Guid"]}
|
204
|
+
|
205
|
+
# if rerunning due to an error (and we are using Retry)
|
206
|
+
if context.state_name == context.next_state && context.failed? && context.state.key?("Retrier")
|
207
|
+
next_state.merge!(context.state.slice("RetryCount", "Input", "Retrier"))
|
208
|
+
else
|
209
|
+
next_state["Input"] = context.output
|
210
|
+
end
|
211
|
+
|
212
|
+
context.state = next_state
|
213
|
+
end
|
214
|
+
|
215
|
+
def end_workflow!
|
216
|
+
context.execution["EndTime"] = context.state["FinishedTime"]
|
165
217
|
end
|
166
218
|
end
|
167
219
|
end
|
data/lib/floe.rb
CHANGED
@@ -7,7 +7,9 @@ require_relative "floe/logging"
|
|
7
7
|
|
8
8
|
require_relative "floe/runner"
|
9
9
|
|
10
|
+
require_relative "floe/validation_mixin"
|
10
11
|
require_relative "floe/workflow"
|
12
|
+
require_relative "floe/workflow/error_matcher_mixin"
|
11
13
|
require_relative "floe/workflow/catcher"
|
12
14
|
require_relative "floe/workflow/choice_rule"
|
13
15
|
require_relative "floe/workflow/choice_rule/not"
|
@@ -15,6 +17,9 @@ require_relative "floe/workflow/choice_rule/or"
|
|
15
17
|
require_relative "floe/workflow/choice_rule/and"
|
16
18
|
require_relative "floe/workflow/choice_rule/data"
|
17
19
|
require_relative "floe/workflow/context"
|
20
|
+
require_relative "floe/workflow/intrinsic_function"
|
21
|
+
require_relative "floe/workflow/intrinsic_function/parser"
|
22
|
+
require_relative "floe/workflow/intrinsic_function/transformer"
|
18
23
|
require_relative "floe/workflow/path"
|
19
24
|
require_relative "floe/workflow/payload_template"
|
20
25
|
require_relative "floe/workflow/reference_path"
|
@@ -37,6 +42,7 @@ require "time"
|
|
37
42
|
module Floe
|
38
43
|
class Error < StandardError; end
|
39
44
|
class InvalidWorkflowError < Error; end
|
45
|
+
class InvalidExecutionInput < Error; end
|
40
46
|
|
41
47
|
def self.logger
|
42
48
|
@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.12.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-
|
11
|
+
date: 2024-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: awesome_spawn
|
@@ -80,20 +80,34 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: parslet
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '2.0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: manageiq-style
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
86
100
|
requirements:
|
87
101
|
- - ">="
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
103
|
+
version: 1.5.2
|
90
104
|
type: :development
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
108
|
- - ">="
|
95
109
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
110
|
+
version: 1.5.2
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: rake
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -122,20 +136,6 @@ dependencies:
|
|
122
136
|
- - ">="
|
123
137
|
- !ruby/object:Gem::Version
|
124
138
|
version: '0'
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
|
-
name: rubocop
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
- - ">="
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: '0'
|
132
|
-
type: :development
|
133
|
-
prerelease: false
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
135
|
-
requirements:
|
136
|
-
- - ">="
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: '0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: simplecov
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,13 +164,14 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
-
description:
|
167
|
+
description: Floe is a runner for Amazon States Language workflows.
|
168
168
|
email:
|
169
169
|
executables:
|
170
170
|
- floe
|
171
171
|
extensions: []
|
172
172
|
extra_rdoc_files: []
|
173
173
|
files:
|
174
|
+
- ".codeclimate.yml"
|
174
175
|
- ".rspec"
|
175
176
|
- ".rubocop.yml"
|
176
177
|
- ".rubocop_cc.yml"
|
@@ -178,6 +179,7 @@ files:
|
|
178
179
|
- ".yamllint"
|
179
180
|
- CHANGELOG.md
|
180
181
|
- Gemfile
|
182
|
+
- LICENSE.txt
|
181
183
|
- README.md
|
182
184
|
- Rakefile
|
183
185
|
- examples/set-credential.asl
|
@@ -185,6 +187,7 @@ files:
|
|
185
187
|
- exe/floe
|
186
188
|
- floe.gemspec
|
187
189
|
- lib/floe.rb
|
190
|
+
- lib/floe/cli.rb
|
188
191
|
- lib/floe/container_runner.rb
|
189
192
|
- lib/floe/container_runner/docker.rb
|
190
193
|
- lib/floe/container_runner/docker_mixin.rb
|
@@ -193,6 +196,7 @@ files:
|
|
193
196
|
- lib/floe/logging.rb
|
194
197
|
- lib/floe/null_logger.rb
|
195
198
|
- lib/floe/runner.rb
|
199
|
+
- lib/floe/validation_mixin.rb
|
196
200
|
- lib/floe/version.rb
|
197
201
|
- lib/floe/workflow.rb
|
198
202
|
- lib/floe/workflow/catcher.rb
|
@@ -202,6 +206,10 @@ files:
|
|
202
206
|
- lib/floe/workflow/choice_rule/not.rb
|
203
207
|
- lib/floe/workflow/choice_rule/or.rb
|
204
208
|
- lib/floe/workflow/context.rb
|
209
|
+
- lib/floe/workflow/error_matcher_mixin.rb
|
210
|
+
- lib/floe/workflow/intrinsic_function.rb
|
211
|
+
- lib/floe/workflow/intrinsic_function/parser.rb
|
212
|
+
- lib/floe/workflow/intrinsic_function/transformer.rb
|
205
213
|
- lib/floe/workflow/path.rb
|
206
214
|
- lib/floe/workflow/payload_template.rb
|
207
215
|
- lib/floe/workflow/reference_path.rb
|
@@ -220,7 +228,8 @@ files:
|
|
220
228
|
- renovate.json
|
221
229
|
- sig/floe.rbs/floe.rbs
|
222
230
|
homepage: https://github.com/ManageIQ/floe
|
223
|
-
licenses:
|
231
|
+
licenses:
|
232
|
+
- Apache-2.0
|
224
233
|
metadata:
|
225
234
|
allowed_push_host: https://rubygems.org
|
226
235
|
rubygems_mfa_required: 'true'
|
@@ -242,8 +251,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
242
251
|
- !ruby/object:Gem::Version
|
243
252
|
version: '0'
|
244
253
|
requirements: []
|
245
|
-
rubygems_version: 3.
|
254
|
+
rubygems_version: 3.4.20
|
246
255
|
signing_key:
|
247
256
|
specification_version: 4
|
248
|
-
summary:
|
257
|
+
summary: Floe is a runner for Amazon States Language workflows.
|
249
258
|
test_files: []
|