floe 0.21.0 → 0.22.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 +11 -1
- data/lib/floe/container_runner/docker.rb +2 -2
- data/lib/floe/version.rb +1 -1
- data/lib/floe/workflow/context.rb +2 -1
- data/lib/floe/workflow/state.rb +12 -2
- data/lib/floe/workflow/states/task.rb +18 -19
- data/lib/floe/workflow.rb +20 -6
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bf92b02c87a1fa72f7a6424a6f01413fed9830a6e35194762095e7af6e76c366
|
|
4
|
+
data.tar.gz: 6d9b6209e73739fc7d6f7572b7e630366127de06eb2dca550542dc7558790ca6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d03f3cde0036103469210a5a28eb20f2198c4cea7bb5306960a2fada722b109a2030f83858b51d9796a97cb30b6ce7a2f4b3be12209085e561009b9918d8a102
|
|
7
|
+
data.tar.gz: eca7b964bc9dbd5af65d122e01852650d4892731dc26fc30b71896bf256e98837d5d8c426ea84c59466aee6c4a8c383d67a455e8256c9f5b2fc5be97954065d6
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,15 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.22.0] - 2026-10-23
|
|
8
|
+
### Fixed
|
|
9
|
+
- Fix TypeError in Docker#wait when timeout is given ([#352](https://github.com/ManageIQ/floe/pull/352))
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
- Implement StateMachine TimeoutSeconds ([#350](https://github.com/ManageIQ/floe/pull/350))
|
|
13
|
+
- Add StateMachine Version ([#350](https://github.com/ManageIQ/floe/pull/350))
|
|
14
|
+
- Add timeout_at to Workflow and State ([#351](https://github.com/ManageIQ/floe/pull/351))
|
|
15
|
+
|
|
7
16
|
## [0.21.0] - 2026-09-16
|
|
8
17
|
### Added
|
|
9
18
|
- Add volumes: option to run_async! for all container runners ([#347](https://github.com/ManageIQ/floe/pull/347))
|
|
@@ -356,7 +365,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
356
365
|
### Added
|
|
357
366
|
- Initial release
|
|
358
367
|
|
|
359
|
-
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.
|
|
368
|
+
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.22.0...HEAD
|
|
369
|
+
[0.22.0]: https://github.com/ManageIQ/floe/compare/v0.21.0...v0.22.0
|
|
360
370
|
[0.21.0]: https://github.com/ManageIQ/floe/compare/v0.20.1...v0.21.0
|
|
361
371
|
[0.20.1]: https://github.com/ManageIQ/floe/compare/v0.20.0...v0.20.1
|
|
362
372
|
[0.20.0]: https://github.com/ManageIQ/floe/compare/v0.19.2...v0.20.0
|
|
@@ -48,7 +48,7 @@ module Floe
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
def wait(timeout: nil, events: %i[create update delete], &block)
|
|
51
|
-
until_timestamp =
|
|
51
|
+
until_timestamp = Time.now.utc + timeout if timeout
|
|
52
52
|
|
|
53
53
|
r, w = IO.pipe
|
|
54
54
|
|
|
@@ -168,7 +168,7 @@ module Floe
|
|
|
168
168
|
since_timestamp = Time.now.utc.to_i
|
|
169
169
|
|
|
170
170
|
params = ["events", "--format", "{{json .}}", "--filter", "type=container", "--since", since_timestamp.to_s]
|
|
171
|
-
params += ["--until", until_timestamp.to_s] if until_timestamp
|
|
171
|
+
params += ["--until", until_timestamp.to_i.to_s] if until_timestamp
|
|
172
172
|
params
|
|
173
173
|
end
|
|
174
174
|
|
data/lib/floe/version.rb
CHANGED
|
@@ -26,7 +26,7 @@ module Floe
|
|
|
26
26
|
raise Floe::InvalidExecutionInput, "Invalid State Machine Execution Input: #{err}: was expecting (JSON String, Number, Array, Object or token 'null', 'true' or 'false')"
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
-
def prepare_start(start_at)
|
|
29
|
+
def prepare_start(start_at, timeout_seconds: nil)
|
|
30
30
|
return if started?
|
|
31
31
|
|
|
32
32
|
state["Name"] = start_at
|
|
@@ -35,6 +35,7 @@ module Floe
|
|
|
35
35
|
|
|
36
36
|
execution["Id"] ||= SecureRandom.uuid
|
|
37
37
|
execution["StartTime"] = Time.now.utc.iso8601
|
|
38
|
+
execution["TimeoutAt"] = (Time.now.utc + timeout_seconds).iso8601 if timeout_seconds
|
|
38
39
|
|
|
39
40
|
if logger.respond_to?(:execution_id=)
|
|
40
41
|
logger.execution_id = execution["Id"]
|
data/lib/floe/workflow/state.rb
CHANGED
|
@@ -45,8 +45,9 @@ module Floe
|
|
|
45
45
|
def run_nonblock!(context)
|
|
46
46
|
# Only start the state if it isn't already started and it isn't waiting
|
|
47
47
|
# from a prior Retry.
|
|
48
|
-
start(context)
|
|
49
|
-
|
|
48
|
+
start(context) unless started?(context) || waiting?(context)
|
|
49
|
+
raise Floe::TimeoutError if timed_out?(context)
|
|
50
|
+
return Errno::EAGAIN unless ready?(context)
|
|
50
51
|
|
|
51
52
|
finish(context)
|
|
52
53
|
rescue Floe::ExecutionError => e
|
|
@@ -115,6 +116,15 @@ module Floe
|
|
|
115
116
|
context.state["WaitUntil"] && Time.parse(context.state["WaitUntil"])
|
|
116
117
|
end
|
|
117
118
|
|
|
119
|
+
def timeout_at(context)
|
|
120
|
+
context.execution["TimeoutAt"] && Time.parse(context.execution["TimeoutAt"])
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def timed_out?(context)
|
|
124
|
+
t = timeout_at(context)
|
|
125
|
+
t && Time.now.utc > t
|
|
126
|
+
end
|
|
127
|
+
|
|
118
128
|
def short_name
|
|
119
129
|
name.last
|
|
120
130
|
end
|
|
@@ -39,8 +39,7 @@ module Floe
|
|
|
39
39
|
def start(context)
|
|
40
40
|
super
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
wait_until!(context, :seconds => timeout_seconds) if timeout_seconds
|
|
42
|
+
set_timeout_at!(context)
|
|
44
43
|
|
|
45
44
|
input = process_input(context)
|
|
46
45
|
secrets = credentials&.value(context, context.input)
|
|
@@ -62,7 +61,6 @@ module Floe
|
|
|
62
61
|
end
|
|
63
62
|
|
|
64
63
|
def running?(context)
|
|
65
|
-
raise Floe::TimeoutError if timed_out?(context)
|
|
66
64
|
return false if finished?(context)
|
|
67
65
|
|
|
68
66
|
runner.status!(context.state["RunnerContext"])
|
|
@@ -80,8 +78,25 @@ module Floe
|
|
|
80
78
|
@end
|
|
81
79
|
end
|
|
82
80
|
|
|
81
|
+
def timeout_at(context)
|
|
82
|
+
workflow_timeout = super
|
|
83
|
+
task_timeout = context.state["TimeoutAt"] && Time.parse(context.state["TimeoutAt"])
|
|
84
|
+
|
|
85
|
+
[workflow_timeout, task_timeout].compact.min
|
|
86
|
+
end
|
|
87
|
+
|
|
83
88
|
private
|
|
84
89
|
|
|
90
|
+
def set_timeout_at!(context)
|
|
91
|
+
return if timeout_seconds.nil? && timeout_seconds_path.nil?
|
|
92
|
+
|
|
93
|
+
entered_time = Time.parse(context.state["EnteredTime"])
|
|
94
|
+
seconds = timeout_seconds || timeout_seconds_path.value(context, context.input)
|
|
95
|
+
raise Floe::PathError, "TimeoutSecondsPath references an invalid value [#{seconds}]" unless seconds.kind_of?(Integer) && seconds > 0
|
|
96
|
+
|
|
97
|
+
context.state["TimeoutAt"] = (entered_time + seconds).iso8601
|
|
98
|
+
end
|
|
99
|
+
|
|
85
100
|
attr_reader :runner
|
|
86
101
|
|
|
87
102
|
def validate_state!(workflow)
|
|
@@ -107,22 +122,6 @@ module Floe
|
|
|
107
122
|
runner.success?(context.state["RunnerContext"])
|
|
108
123
|
end
|
|
109
124
|
|
|
110
|
-
def timed_out?(context)
|
|
111
|
-
return false if timeout_seconds.nil? && timeout_seconds_path.nil?
|
|
112
|
-
|
|
113
|
-
timeout = timeout_seconds || timeout_seconds_path.value(context, context.input)
|
|
114
|
-
entered_time = Time.parse(context.state["EnteredTime"])
|
|
115
|
-
|
|
116
|
-
Time.now.utc > entered_time + timeout
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def task_timed_out!(context)
|
|
120
|
-
context.state["RunnerContext"]["Error"] = "States.Timeout"
|
|
121
|
-
context.state["RunnerContext"]["Cause"] = "Task timed out"
|
|
122
|
-
|
|
123
|
-
false
|
|
124
|
-
end
|
|
125
|
-
|
|
126
125
|
def parse_error(output)
|
|
127
126
|
return if output.nil?
|
|
128
127
|
return output if output.kind_of?(Hash)
|
data/lib/floe/workflow.rb
CHANGED
|
@@ -42,9 +42,10 @@ module Floe
|
|
|
42
42
|
break if timeout && (timeout.zero? || Time.now.utc > run_until)
|
|
43
43
|
|
|
44
44
|
# Find the earliest time that we should wakeup if no container events
|
|
45
|
-
# are caught, either a workflow in a Wait or
|
|
46
|
-
# exceeded the requested timeout
|
|
45
|
+
# are caught, either a workflow in a Wait, Retry, or Timeout state or
|
|
46
|
+
# we've exceeded the requested timeout
|
|
47
47
|
wait_until = workflows.map(&:wait_until)
|
|
48
|
+
.concat(workflows.map(&:timeout_at))
|
|
48
49
|
.unshift(run_until)
|
|
49
50
|
.compact
|
|
50
51
|
.min
|
|
@@ -77,7 +78,7 @@ module Floe
|
|
|
77
78
|
end
|
|
78
79
|
end
|
|
79
80
|
|
|
80
|
-
attr_reader :comment, :context
|
|
81
|
+
attr_reader :comment, :context, :timeout_seconds, :version
|
|
81
82
|
|
|
82
83
|
def initialize(payload, context = nil, credentials = nil, name = nil)
|
|
83
84
|
payload = JSON.parse(payload) if payload.kind_of?(String)
|
|
@@ -88,8 +89,10 @@ module Floe
|
|
|
88
89
|
# caller should really put credentials into context and not pass that variable
|
|
89
90
|
context.credentials = credentials if credentials
|
|
90
91
|
|
|
91
|
-
@context
|
|
92
|
-
@comment
|
|
92
|
+
@context = context
|
|
93
|
+
@comment = payload["Comment"]
|
|
94
|
+
@timeout_seconds = payload["TimeoutSeconds"]
|
|
95
|
+
@version = payload["Version"] || "1.0"
|
|
93
96
|
|
|
94
97
|
super(payload, name)
|
|
95
98
|
rescue Floe::Error
|
|
@@ -136,6 +139,10 @@ module Floe
|
|
|
136
139
|
current_state.wait_until(context)
|
|
137
140
|
end
|
|
138
141
|
|
|
142
|
+
def timeout_at
|
|
143
|
+
current_state.timeout_at(context)
|
|
144
|
+
end
|
|
145
|
+
|
|
139
146
|
def status
|
|
140
147
|
context.status
|
|
141
148
|
end
|
|
@@ -150,7 +157,7 @@ module Floe
|
|
|
150
157
|
|
|
151
158
|
# setup a workflow
|
|
152
159
|
def start_workflow
|
|
153
|
-
context.prepare_start(start_at)
|
|
160
|
+
context.prepare_start(start_at, :timeout_seconds => timeout_seconds)
|
|
154
161
|
self
|
|
155
162
|
end
|
|
156
163
|
|
|
@@ -170,6 +177,13 @@ module Floe
|
|
|
170
177
|
|
|
171
178
|
private
|
|
172
179
|
|
|
180
|
+
def validate_workflow!
|
|
181
|
+
super
|
|
182
|
+
return unless timeout_seconds
|
|
183
|
+
|
|
184
|
+
invalid_field_error!("TimeoutSeconds", timeout_seconds, "must be a positive, non-zero integer") unless timeout_seconds.kind_of?(Integer) && timeout_seconds.positive?
|
|
185
|
+
end
|
|
186
|
+
|
|
173
187
|
def step!
|
|
174
188
|
next_state = {"Name" => context.next_state, "Guid" => SecureRandom.uuid, "PreviousStateGuid" => context.state["Guid"]}
|
|
175
189
|
|