floe 0.9.0 → 0.10.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 +15 -1
- data/Gemfile +0 -6
- data/exe/floe +2 -1
- data/floe.gemspec +7 -0
- data/lib/floe/version.rb +1 -1
- data/lib/floe/workflow/context.rb +14 -10
- data/lib/floe/workflow/reference_path.rb +2 -2
- data/lib/floe/workflow/runner/docker_mixin.rb +4 -3
- data/lib/floe/workflow/runner/kubernetes.rb +1 -1
- data/lib/floe/workflow/state.rb +7 -2
- data/lib/floe/workflow/states/choice.rb +3 -3
- data/lib/floe/workflow/states/fail.rb +4 -6
- data/lib/floe/workflow/states/pass.rb +3 -5
- data/lib/floe/workflow/states/succeed.rb +3 -3
- data/lib/floe/workflow/states/task.rb +8 -6
- data/lib/floe/workflow/states/wait.rb +7 -2
- data/lib/floe/workflow.rb +2 -1
- metadata +86 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b158e514e08902a1138c7b2878bb8633ca7c1abb59d4b77b6d9ce568c65710b
|
4
|
+
data.tar.gz: 32d053bba54e8c35645a636771964692e435ad72ee38e03c8c22b4da5bce25ec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e39301eed1de9189b66f07a7ecdda807974bf47495b4906bd57f6f8ed862fab38448668155f79e12c87da8935f4054d14dbcd08e292429b194768d2234fa7c46
|
7
|
+
data.tar.gz: 5cf0476521a1cd4fc0dcc4edeccdd00c6c72ee88bf7428103b86f5f30097608f934f19e75499a61b5d7e7380f2e1451dccfd82f796eee6bcef041dd6f3db8664
|
data/CHANGELOG.md
CHANGED
@@ -4,6 +4,19 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
5
5
|
## [Unreleased]
|
6
6
|
|
7
|
+
## [0.10.0] - 2024-04-05
|
8
|
+
### Fixed
|
9
|
+
- Fix rubocops ([#164](https://github.com/ManageIQ/floe/pull/164))
|
10
|
+
- Output should contain errors ([#165](https://github.com/ManageIQ/floe/pull/165))
|
11
|
+
|
12
|
+
### Added
|
13
|
+
- Add simplecov ([#162](https://github.com/ManageIQ/floe/pull/162))
|
14
|
+
- Add ability to pass context on the command line ([#161](https://github.com/ManageIQ/floe/pull/161))
|
15
|
+
- Add specs for `Workflow#wait_until`, `#waiting?` ([#166](https://github.com/ManageIQ/floe/pull/166))
|
16
|
+
|
17
|
+
### Changed
|
18
|
+
- Drop non-standard Error/Cause fields ([#167](https://github.com/ManageIQ/floe/pull/167))
|
19
|
+
|
7
20
|
## [0.9.0] - 2024-02-19
|
8
21
|
### Changed
|
9
22
|
- Default to wait indefinitely ([#157](https://github.com/ManageIQ/floe/pull/157))
|
@@ -136,7 +149,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
136
149
|
### Added
|
137
150
|
- Initial release
|
138
151
|
|
139
|
-
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.
|
152
|
+
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.10.0...HEAD
|
153
|
+
[0.10.0]: https://github.com/ManageIQ/floe/compare/v0.9.0...v0.10.0
|
140
154
|
[0.9.0]: https://github.com/ManageIQ/floe/compare/v0.8.0...v0.9.0
|
141
155
|
[0.8.0]: https://github.com/ManageIQ/floe/compare/v0.7.0...v0.8.0
|
142
156
|
[0.7.0]: https://github.com/ManageIQ/floe/compare/v0.6.1...v0.7.0
|
data/Gemfile
CHANGED
data/exe/floe
CHANGED
@@ -10,6 +10,7 @@ opts = Optimist.options do
|
|
10
10
|
|
11
11
|
opt :workflow, "Path to your workflow json (legacy)", :type => :string
|
12
12
|
opt :input, "JSON payload to input to the workflow (legacy)", :type => :string
|
13
|
+
opt :context, "JSON payload of the Context", :type => :string
|
13
14
|
opt :credentials, "JSON payload with credentials", :type => :string
|
14
15
|
opt :credentials_file, "Path to a file with credentials", :type => :string
|
15
16
|
opt :docker_runner, "Type of runner for docker images", :type => :string, :short => 'r'
|
@@ -49,7 +50,7 @@ credentials =
|
|
49
50
|
|
50
51
|
workflows =
|
51
52
|
args.each_slice(2).map do |workflow, input|
|
52
|
-
context = Floe::Workflow::Context.new(:input => input || opts[:input] || "{}")
|
53
|
+
context = Floe::Workflow::Context.new(opts[:context], :input => input || opts[:input] || "{}")
|
53
54
|
Floe::Workflow.load(workflow, context, credentials)
|
54
55
|
end
|
55
56
|
|
data/floe.gemspec
CHANGED
@@ -34,4 +34,11 @@ Gem::Specification.new do |spec|
|
|
34
34
|
spec.add_dependency "jsonpath", "~>1.1"
|
35
35
|
spec.add_dependency "kubeclient", "~>4.7"
|
36
36
|
spec.add_dependency "optimist", "~>3.0"
|
37
|
+
|
38
|
+
spec.add_development_dependency "manageiq-style"
|
39
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
40
|
+
spec.add_development_dependency "rspec"
|
41
|
+
spec.add_development_dependency "rubocop"
|
42
|
+
spec.add_development_dependency "simplecov", ">= 0.21.2"
|
43
|
+
spec.add_development_dependency "timecop"
|
37
44
|
end
|
data/lib/floe/version.rb
CHANGED
@@ -3,19 +3,19 @@
|
|
3
3
|
module Floe
|
4
4
|
class Workflow
|
5
5
|
class Context
|
6
|
+
# @param context [Json|Hash] (default, create another with input and execution params)
|
7
|
+
# @param input [Hash] (default: {})
|
6
8
|
def initialize(context = nil, input: {})
|
7
9
|
context = JSON.parse(context) if context.kind_of?(String)
|
8
10
|
input = JSON.parse(input) if input.kind_of?(String)
|
9
11
|
|
10
|
-
@context = context || {
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
"Task" => {}
|
18
|
-
}
|
12
|
+
@context = context || {}
|
13
|
+
self["Execution"] ||= {}
|
14
|
+
self["Execution"]["Input"] ||= input
|
15
|
+
self["State"] ||= {}
|
16
|
+
self["StateHistory"] ||= []
|
17
|
+
self["StateMachine"] ||= {}
|
18
|
+
self["Task"] ||= {}
|
19
19
|
end
|
20
20
|
|
21
21
|
def execution
|
@@ -30,6 +30,10 @@ module Floe
|
|
30
30
|
started? && !ended?
|
31
31
|
end
|
32
32
|
|
33
|
+
def failed?
|
34
|
+
output&.key?("Error") || false
|
35
|
+
end
|
36
|
+
|
33
37
|
def ended?
|
34
38
|
execution.key?("EndTime")
|
35
39
|
end
|
@@ -67,7 +71,7 @@ module Floe
|
|
67
71
|
"pending"
|
68
72
|
elsif running?
|
69
73
|
"running"
|
70
|
-
elsif
|
74
|
+
elsif failed?
|
71
75
|
"failure"
|
72
76
|
else
|
73
77
|
"success"
|
@@ -19,11 +19,11 @@ module Floe
|
|
19
19
|
super
|
20
20
|
|
21
21
|
raise Floe::InvalidWorkflowError, "Invalid Reference Path" if payload.match?(/@|,|:|\?/)
|
22
|
+
|
22
23
|
@path = JsonPath.new(payload)
|
23
24
|
.path[1..]
|
24
25
|
.map { |v| v.match(/\[(?<name>.+)\]/)["name"] }
|
25
|
-
.
|
26
|
-
.compact
|
26
|
+
.filter_map { |v| v[0] == "'" ? v.delete("'") : v.to_i }
|
27
27
|
end
|
28
28
|
|
29
29
|
def get(context)
|
@@ -6,9 +6,10 @@ module Floe
|
|
6
6
|
image.match(%r{^(?<repository>.+/)?(?<image>.+):(?<tag>.+)$})&.named_captures&.dig("image")
|
7
7
|
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
# 63 is the max kubernetes pod name length
|
10
|
+
# -5 for the "floe-" prefix
|
11
|
+
# -9 for the random hex suffix and leading hyphen
|
12
|
+
MAX_CONTAINER_NAME_SIZE = 63 - 5 - 9
|
12
13
|
|
13
14
|
def container_name(image)
|
14
15
|
name = image_name(image)
|
@@ -164,7 +164,7 @@ module Floe
|
|
164
164
|
|
165
165
|
def failed_container_states(context)
|
166
166
|
container_statuses = context.dig("container_state", "containerStatuses") || []
|
167
|
-
container_statuses.
|
167
|
+
container_statuses.filter_map { |status| status["state"]&.values&.first }
|
168
168
|
.select { |state| FAILURE_REASONS.include?(state["reason"]) }
|
169
169
|
end
|
170
170
|
|
data/lib/floe/workflow/state.rb
CHANGED
@@ -58,7 +58,7 @@ module Floe
|
|
58
58
|
context.state["Guid"] = SecureRandom.uuid
|
59
59
|
context.state["EnteredTime"] = start_time
|
60
60
|
|
61
|
-
logger.info("Running state: [#{
|
61
|
+
logger.info("Running state: [#{long_name}] with input [#{context.input}]...")
|
62
62
|
end
|
63
63
|
|
64
64
|
def finish
|
@@ -70,7 +70,8 @@ module Floe
|
|
70
70
|
context.state["Duration"] = finished_time - entered_time
|
71
71
|
context.execution["EndTime"] = finished_time_iso if context.next_state.nil?
|
72
72
|
|
73
|
-
|
73
|
+
level = context.output&.[]("Error") ? :error : :info
|
74
|
+
logger.public_send(level, "Running state: [#{long_name}] with input [#{context.input}]...Complete #{context.next_state ? "- next state [#{context.next_state}]" : "workflow -"} output: [#{context.output}]")
|
74
75
|
|
75
76
|
context.state_history << context.state
|
76
77
|
|
@@ -101,6 +102,10 @@ module Floe
|
|
101
102
|
context.state["WaitUntil"] && Time.parse(context.state["WaitUntil"])
|
102
103
|
end
|
103
104
|
|
105
|
+
def long_name
|
106
|
+
"#{self.class.name.split("::").last}:#{name}"
|
107
|
+
end
|
108
|
+
|
104
109
|
private
|
105
110
|
|
106
111
|
def wait_until!(seconds: nil, time: nil)
|
@@ -18,14 +18,14 @@ module Floe
|
|
18
18
|
@output_path = Path.new(payload.fetch("OutputPath", "$"))
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
|
23
|
-
input = input_path.value(context, input)
|
21
|
+
def finish
|
22
|
+
input = input_path.value(context, context.input)
|
24
23
|
next_state = choices.detect { |choice| choice.true?(context, input) }&.next || default
|
25
24
|
output = output_path.value(context, input)
|
26
25
|
|
27
26
|
context.next_state = next_state
|
28
27
|
context.output = output
|
28
|
+
super
|
29
29
|
end
|
30
30
|
|
31
31
|
def running?
|
@@ -15,18 +15,16 @@ module Floe
|
|
15
15
|
@error_path = Path.new(payload["ErrorPath"]) if payload["ErrorPath"]
|
16
16
|
end
|
17
17
|
|
18
|
-
def
|
19
|
-
super
|
18
|
+
def finish
|
20
19
|
context.next_state = nil
|
21
20
|
# TODO: support intrinsic functions here
|
22
21
|
# see https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-fail-state.html
|
23
22
|
# https://docs.aws.amazon.com/step-functions/latest/dg/amazon-states-language-intrinsic-functions.html#asl-intrsc-func-generic
|
24
23
|
context.output = {
|
25
|
-
"Error" => @error_path ? @error_path.value(context, input) : error,
|
26
|
-
"Cause" => @cause_path ? @cause_path.value(context, input) : cause
|
24
|
+
"Error" => @error_path ? @error_path.value(context, context.input) : error,
|
25
|
+
"Cause" => @cause_path ? @cause_path.value(context, context.input) : cause
|
27
26
|
}.compact
|
28
|
-
|
29
|
-
context.state["Cause"] = context.output["Cause"]
|
27
|
+
super
|
30
28
|
end
|
31
29
|
|
32
30
|
def running?
|
@@ -24,13 +24,11 @@ module Floe
|
|
24
24
|
validate_state!
|
25
25
|
end
|
26
26
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
30
|
-
input = process_input(input)
|
31
|
-
|
27
|
+
def finish
|
28
|
+
input = process_input(context.input)
|
32
29
|
context.output = process_output(input, result)
|
33
30
|
context.next_state = end? ? nil : @next
|
31
|
+
super
|
34
32
|
end
|
35
33
|
|
36
34
|
def running?
|
@@ -46,19 +46,18 @@ module Floe
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def finish
|
49
|
-
super
|
50
|
-
|
51
49
|
output = runner.output(context.state["RunnerContext"])
|
52
50
|
|
53
51
|
if success?
|
54
52
|
output = parse_output(output)
|
55
|
-
context.
|
53
|
+
context.output = process_output(context.input.dup, output)
|
54
|
+
super
|
56
55
|
else
|
57
56
|
context.next_state = nil
|
58
|
-
error = parse_error(output)
|
57
|
+
context.output = error = parse_error(output)
|
58
|
+
super
|
59
59
|
retry_state!(error) || catch_error!(error) || fail_workflow!(error)
|
60
60
|
end
|
61
|
-
|
62
61
|
ensure
|
63
62
|
runner.cleanup(context.state["RunnerContext"])
|
64
63
|
end
|
@@ -110,6 +109,7 @@ module Floe
|
|
110
109
|
|
111
110
|
wait_until!(:seconds => retrier.sleep_duration(context["State"]["RetryCount"]))
|
112
111
|
context.next_state = context.state_name
|
112
|
+
logger.info("Running state: [#{long_name}] with input [#{context.input}]...Retry - delay: #{wait_until}")
|
113
113
|
true
|
114
114
|
end
|
115
115
|
|
@@ -119,13 +119,15 @@ module Floe
|
|
119
119
|
|
120
120
|
context.next_state = catcher.next
|
121
121
|
context.output = catcher.result_path.set(context.input, error)
|
122
|
+
logger.info("Running state: [#{long_name}] with input [#{context.input}]...CatchError - next state: [#{context.next_state}] output: [#{context.output}]")
|
123
|
+
|
122
124
|
true
|
123
125
|
end
|
124
126
|
|
125
127
|
def fail_workflow!(error)
|
126
128
|
context.next_state = nil
|
127
129
|
context.output = {"Error" => error["Error"], "Cause" => error["Cause"]}.compact
|
128
|
-
|
130
|
+
logger.error("Running state: [#{long_name}] with input [#{context.input}]...Complete workflow - output: [#{context.output}]")
|
129
131
|
end
|
130
132
|
|
131
133
|
def parse_error(output)
|
@@ -29,8 +29,7 @@ module Floe
|
|
29
29
|
def start(input)
|
30
30
|
super
|
31
31
|
|
32
|
-
input
|
33
|
-
context.output = output_path.value(context, input)
|
32
|
+
input = input_path.value(context, context.input)
|
34
33
|
|
35
34
|
wait_until!(
|
36
35
|
:seconds => seconds_path ? seconds_path.value(context, input).to_i : seconds,
|
@@ -38,6 +37,12 @@ module Floe
|
|
38
37
|
)
|
39
38
|
end
|
40
39
|
|
40
|
+
def finish
|
41
|
+
input = input_path.value(context, context.input)
|
42
|
+
context.output = output_path.value(context, input)
|
43
|
+
super
|
44
|
+
end
|
45
|
+
|
41
46
|
def running?
|
42
47
|
waiting?
|
43
48
|
end
|
data/lib/floe/workflow.rb
CHANGED
@@ -85,7 +85,7 @@ module Floe
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
-
attr_reader :context, :credentials, :payload, :states, :states_by_name, :start_at, :name
|
88
|
+
attr_reader :context, :credentials, :payload, :states, :states_by_name, :start_at, :name, :comment
|
89
89
|
|
90
90
|
def initialize(payload, context = nil, credentials = {}, name = nil)
|
91
91
|
payload = JSON.parse(payload) if payload.kind_of?(String)
|
@@ -100,6 +100,7 @@ module Floe
|
|
100
100
|
@payload = payload
|
101
101
|
@context = context
|
102
102
|
@credentials = credentials || {}
|
103
|
+
@comment = payload["Comment"]
|
103
104
|
@start_at = payload["StartAt"]
|
104
105
|
|
105
106
|
@states = payload["States"].to_a.map { |state_name, state| State.build!(self, state_name, state) }
|
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.10.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-04-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: awesome_spawn
|
@@ -80,6 +80,90 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '3.0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: manageiq-style
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rake
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '13.0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '13.0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rspec
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
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
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: simplecov
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - ">="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.21.2
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - ">="
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: 0.21.2
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: timecop
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
83
167
|
description: Simple Workflow Runner.
|
84
168
|
email:
|
85
169
|
executables:
|