floe 0.9.0 → 0.10.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|