floe 0.19.2 → 0.20.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 +12 -1
- data/README.md +21 -0
- data/lib/floe/builtin_runner/methods.rb +7 -1
- data/lib/floe/builtin_runner.rb +4 -2
- data/lib/floe/cli/logger.rb +21 -0
- data/lib/floe/cli.rb +20 -32
- data/lib/floe/container_runner/docker.rb +10 -7
- data/lib/floe/version.rb +1 -1
- data/lib/floe/workflow/context.rb +8 -0
- metadata +12 -6
- data/floe.gemspec +0 -50
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7276764c838fbe619e9dfbd03ef63ee48d654cb51e1c90338bd221312c35bac3
|
|
4
|
+
data.tar.gz: 61541bf22ba9193628978471a418d351aac5576cbdf3dc186374da04319dbec1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4f295717a57a70e71683ec8e82b0755452cb5e740ffad204c40aa8d6970e78f159559c784541694982326a09e7437fea579202e77a34caa2f075293b4c11c421
|
|
7
|
+
data.tar.gz: 6ec1d9c222569c4bfa9d5decab841aa87fd8206bf69f753aa77dad6b182e680f9e0252f8be29cd8a93965ef91e1a92a290bc13bcb941c802dcb983ab289582a9
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,16 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.20.0] - 2026-07-08
|
|
8
|
+
### Fixed
|
|
9
|
+
- Update to support io-wait v0.4 which drops the `IO#ready?` method ([#340](https://github.com/ManageIQ/floe/pull/340))
|
|
10
|
+
- Return States.TaskFailed for failed http request ([#343](https://github.com/ManageIQ/floe/pull/343))
|
|
11
|
+
- `kill -TERM` the docker wait process before exiting ([#344](https://github.com/ManageIQ/floe/pull/344))
|
|
12
|
+
- Use Kernel.spawn directly to ensure no `/bin/sh -c` shell is created ([#345](https://github.com/ManageIQ/floe/pull/345))
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- Add logging of the execution_id to exe/floe ([#341](https://github.com/ManageIQ/floe/pull/341))
|
|
16
|
+
|
|
7
17
|
## [0.19.2] - 2026-01-06
|
|
8
18
|
### Fixed
|
|
9
19
|
- Lockdown io-wait to v0.3.* for `IO#ready?` ([#339](https://github.com/ManageIQ/floe/pull/339))
|
|
@@ -337,7 +347,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
337
347
|
### Added
|
|
338
348
|
- Initial release
|
|
339
349
|
|
|
340
|
-
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.
|
|
350
|
+
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.20.0...HEAD
|
|
351
|
+
[0.20.0]: https://github.com/ManageIQ/floe/compare/v0.19.2...v0.20.0
|
|
341
352
|
[0.19.2]: https://github.com/ManageIQ/floe/compare/v0.19.1...v0.19.2
|
|
342
353
|
[0.19.1]: https://github.com/ManageIQ/floe/compare/v0.19.0...v0.19.1
|
|
343
354
|
[0.19.0]: https://github.com/ManageIQ/floe/compare/v0.18.0...v0.19.0
|
data/README.md
CHANGED
|
@@ -246,6 +246,27 @@ HTTP Parameters:
|
|
|
246
246
|
* `User` - String - User for the proxy.
|
|
247
247
|
* `Password` - String - Pasword for the proxy
|
|
248
248
|
|
|
249
|
+
Output:
|
|
250
|
+
A successful HTTP request will return the response Status, Body, and Headers. For example,
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"Status": 200,
|
|
254
|
+
"Body": {"result": {"foo": "bar"}},
|
|
255
|
+
"Headers": {"content-type": "application/json"}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Errors:
|
|
260
|
+
If an HTTP request fails, the `floe://http` builtin method will return an error payload that includes
|
|
261
|
+
the error code, the reason, and the response Status, Body, and Headers. For example,
|
|
262
|
+
```json
|
|
263
|
+
{
|
|
264
|
+
"Error": "States.Http.StatusCode.404",
|
|
265
|
+
"Cause": "Not Found",
|
|
266
|
+
"Details": {"Status": 404, "Body": {"code": 404, "reason": "Page not found"}, "Headers": {"content-type": "application/json"}}
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
249
270
|
#### Docker resource
|
|
250
271
|
|
|
251
272
|
The docker resource runner takes a docker image URI, including the registry, name, and tag.
|
|
@@ -94,7 +94,13 @@ module Floe
|
|
|
94
94
|
|
|
95
95
|
output = {"Status" => response.status, "Body" => response.body, "Headers" => response.headers}
|
|
96
96
|
|
|
97
|
-
|
|
97
|
+
if response.success?
|
|
98
|
+
BuiltinRunner.success!({}, :output => output)
|
|
99
|
+
else
|
|
100
|
+
BuiltinRunner.error!({}, :error => "States.Http.StatusCode.#{response.status}", :cause => response.reason_phrase, :details => output)
|
|
101
|
+
end
|
|
102
|
+
rescue ::Faraday::ConnectionFailed => err
|
|
103
|
+
BuiltinRunner.error!({}, :cause => err.to_s)
|
|
98
104
|
end
|
|
99
105
|
|
|
100
106
|
private_class_method def self.http_verify_params(params)
|
data/lib/floe/builtin_runner.rb
CHANGED
|
@@ -7,9 +7,11 @@ module Floe
|
|
|
7
7
|
SCHEME_PREFIX = "#{SCHEME}://".freeze
|
|
8
8
|
|
|
9
9
|
class << self
|
|
10
|
-
def error!(runner_context = {}, cause
|
|
10
|
+
def error!(runner_context = {}, cause: nil, error: "States.TaskFailed", details: nil)
|
|
11
|
+
output = {"Error" => error, "Cause" => cause, "Details" => details}.compact
|
|
12
|
+
|
|
11
13
|
runner_context.merge!(
|
|
12
|
-
"running" => false, "success" => false, "output" =>
|
|
14
|
+
"running" => false, "success" => false, "output" => output
|
|
13
15
|
)
|
|
14
16
|
end
|
|
15
17
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'logger'
|
|
4
|
+
|
|
5
|
+
module Floe
|
|
6
|
+
class CLI
|
|
7
|
+
class Logger < ::Logger
|
|
8
|
+
attr_accessor :execution_id
|
|
9
|
+
|
|
10
|
+
def initialize(...)
|
|
11
|
+
super
|
|
12
|
+
|
|
13
|
+
original_formatter = formatter || ::Logger::Formatter.new
|
|
14
|
+
self.formatter = proc do |severity, time, progname, msg|
|
|
15
|
+
prefix = execution_id ? "[#{execution_id}] " : ""
|
|
16
|
+
original_formatter.call(severity, time, progname, "#{prefix}#{msg}")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/floe/cli.rb
CHANGED
|
@@ -1,44 +1,38 @@
|
|
|
1
1
|
require "floe"
|
|
2
|
+
require "floe/cli/logger"
|
|
2
3
|
require "floe/container_runner"
|
|
4
|
+
require "optimist"
|
|
5
|
+
require "stringio"
|
|
3
6
|
|
|
4
7
|
module Floe
|
|
5
8
|
class CLI
|
|
6
9
|
include Logging
|
|
7
10
|
|
|
8
11
|
def initialize
|
|
9
|
-
|
|
10
|
-
require "logger"
|
|
11
|
-
|
|
12
|
-
Floe.logger = Logger.new($stdout)
|
|
12
|
+
Floe.logger = ::Logger.new($stdout)
|
|
13
13
|
Floe.logger.level = 0 if ENV["DEBUG"]
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def run(args = ARGV)
|
|
17
17
|
workflows_inputs, opts = parse_options!(args)
|
|
18
18
|
|
|
19
|
+
show_execution_id = workflows_inputs.size > 1
|
|
20
|
+
|
|
19
21
|
credentials = create_credentials(opts)
|
|
20
22
|
|
|
21
23
|
workflows =
|
|
22
|
-
workflows_inputs.
|
|
23
|
-
|
|
24
|
+
workflows_inputs.map do |workflow, input|
|
|
25
|
+
wf_logger = create_logger(show_execution_id)
|
|
26
|
+
create_workflow(workflow, opts[:context], input, credentials, wf_logger)
|
|
24
27
|
end
|
|
25
28
|
|
|
26
|
-
output_streams = create_loggers(workflows, opts[:segment_output])
|
|
27
|
-
|
|
28
29
|
logger.info("Checking #{workflows.count} workflows...")
|
|
29
30
|
ready = Floe::Workflow.wait(workflows, &:run_nonblock)
|
|
30
31
|
logger.info("Checking #{workflows.count} workflows...Complete - #{ready.count} ready")
|
|
31
32
|
|
|
32
33
|
# Display status
|
|
33
34
|
workflows.each do |workflow|
|
|
34
|
-
|
|
35
|
-
logger.info("")
|
|
36
|
-
logger.info("#{workflow.name}#{" (#{workflow.status})" unless workflow.context.success?}")
|
|
37
|
-
logger.info("===")
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
logger.info(output_streams[workflow].string) if output_streams[workflow]
|
|
41
|
-
logger.info(workflow.output)
|
|
35
|
+
logger.info("[#{workflow.context.execution_id}] #{workflow.name} (#{workflow.status}) - output: #{workflow.output}")
|
|
42
36
|
end
|
|
43
37
|
|
|
44
38
|
workflows.all? { |workflow| workflow.context.success? }
|
|
@@ -62,7 +56,6 @@ module Floe
|
|
|
62
56
|
opt :context, "JSON payload of the Context", :type => :string
|
|
63
57
|
opt :credentials, "JSON payload with Credentials", :type => :string
|
|
64
58
|
opt :credentials_file, "Path to a file with Credentials", :type => :string
|
|
65
|
-
opt :segment_output, "Segment output by each worker", :default => false
|
|
66
59
|
|
|
67
60
|
Floe::ContainerRunner.cli_options(self)
|
|
68
61
|
|
|
@@ -70,20 +63,20 @@ module Floe
|
|
|
70
63
|
banner("General options:")
|
|
71
64
|
end
|
|
72
65
|
|
|
73
|
-
# Create workflow/input pairs from the various combinations of
|
|
66
|
+
# Create workflow/input pairs from the various combinations of parameters
|
|
74
67
|
workflows_inputs =
|
|
75
68
|
if opts[:workflow_given]
|
|
76
69
|
Optimist.die("cannot specify both --workflow and bare workflows") if args.any?
|
|
77
70
|
|
|
78
|
-
[opts[:workflow], opts.fetch(:input, "{}")]
|
|
71
|
+
[[opts[:workflow], opts.fetch(:input, "{}")]]
|
|
79
72
|
elsif opts[:input_given]
|
|
80
73
|
Optimist.die("workflow(s) must be specified") if args.empty?
|
|
81
74
|
|
|
82
|
-
args.
|
|
75
|
+
args.map { |w| [w, opts[:input].dup] }
|
|
83
76
|
else
|
|
84
77
|
Optimist.die("workflow/input pairs must be specified") if args.empty? || (args.size > 1 && args.size.odd?)
|
|
85
78
|
|
|
86
|
-
args
|
|
79
|
+
args.each_slice(2).to_a
|
|
87
80
|
end
|
|
88
81
|
|
|
89
82
|
Floe::ContainerRunner.resolve_cli_options!(opts)
|
|
@@ -102,20 +95,15 @@ module Floe
|
|
|
102
95
|
JSON.parse(credentials_str)
|
|
103
96
|
end
|
|
104
97
|
|
|
105
|
-
def create_workflow(workflow, context_payload, input, credentials)
|
|
106
|
-
context = Floe::Workflow::Context.new(context_payload, :input => input, :credentials => credentials)
|
|
98
|
+
def create_workflow(workflow, context_payload, input, credentials, logger)
|
|
99
|
+
context = Floe::Workflow::Context.new(context_payload, :input => input, :credentials => credentials, :logger => logger)
|
|
107
100
|
Floe::Workflow.load(workflow, context)
|
|
108
101
|
end
|
|
109
102
|
|
|
110
|
-
def
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
else
|
|
115
|
-
workflows.each_with_object({}) do |workflow, h|
|
|
116
|
-
workflow.context.logger = Logger.new(output = StringIO.new)
|
|
117
|
-
h[workflow] = output
|
|
118
|
-
end
|
|
103
|
+
def create_logger(show_execution_id)
|
|
104
|
+
logger_class = show_execution_id ? Floe::CLI::Logger : ::Logger
|
|
105
|
+
logger_class.new($stdout).tap do |logger|
|
|
106
|
+
logger.level = 0 if ENV["DEBUG"]
|
|
119
107
|
end
|
|
120
108
|
end
|
|
121
109
|
end
|
|
@@ -46,13 +46,12 @@ module Floe
|
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def wait(timeout: nil, events: %i[create update delete], &block)
|
|
49
|
-
until_timestamp = Time.now.utc + timeout if timeout
|
|
49
|
+
until_timestamp = (Time.now.utc + timeout).to_i if timeout
|
|
50
50
|
|
|
51
51
|
r, w = IO.pipe
|
|
52
52
|
|
|
53
|
-
pid =
|
|
54
|
-
|
|
55
|
-
)
|
|
53
|
+
pid = Kernel.spawn({}, self.class::DOCKER_COMMAND, *wait_params(until_timestamp), :err => :out, :out => w)
|
|
54
|
+
Process.detach(pid)
|
|
56
55
|
|
|
57
56
|
w.close
|
|
58
57
|
|
|
@@ -65,7 +64,7 @@ module Floe
|
|
|
65
64
|
|
|
66
65
|
# Get all events while the pipe is readable
|
|
67
66
|
notices = []
|
|
68
|
-
while r.
|
|
67
|
+
while r.wait_readable(0)
|
|
69
68
|
notice = r.gets
|
|
70
69
|
|
|
71
70
|
# If the process has exited `r.gets` returns `nil` and the pipe is
|
|
@@ -92,10 +91,12 @@ module Floe
|
|
|
92
91
|
Process.kill(0, pid)
|
|
93
92
|
rescue Errno::ESRCH
|
|
94
93
|
# Break out of the loop if the `docker events` process has exited
|
|
94
|
+
pid = nil
|
|
95
95
|
break
|
|
96
96
|
end
|
|
97
97
|
ensure
|
|
98
98
|
r.close
|
|
99
|
+
sigterm(pid) if pid
|
|
99
100
|
end
|
|
100
101
|
|
|
101
102
|
def status!(runner_context)
|
|
@@ -150,8 +151,10 @@ module Floe
|
|
|
150
151
|
end
|
|
151
152
|
|
|
152
153
|
def wait_params(until_timestamp)
|
|
153
|
-
|
|
154
|
-
|
|
154
|
+
since_timestamp = Time.now.utc.to_i
|
|
155
|
+
|
|
156
|
+
params = ["events", "--format", "{{json .}}", "--filter", "type=container", "--since", since_timestamp.to_s]
|
|
157
|
+
params += ["--until", until_timestamp.to_s] if until_timestamp
|
|
155
158
|
params
|
|
156
159
|
end
|
|
157
160
|
|
data/lib/floe/version.rb
CHANGED
|
@@ -36,12 +36,20 @@ module Floe
|
|
|
36
36
|
|
|
37
37
|
execution["Id"] ||= SecureRandom.uuid
|
|
38
38
|
execution["StartTime"] = Time.now.utc.iso8601
|
|
39
|
+
|
|
40
|
+
if logger.respond_to?(:execution_id=)
|
|
41
|
+
logger.execution_id = execution["Id"]
|
|
42
|
+
end
|
|
39
43
|
end
|
|
40
44
|
|
|
41
45
|
def execution
|
|
42
46
|
@context["Execution"]
|
|
43
47
|
end
|
|
44
48
|
|
|
49
|
+
def execution_id
|
|
50
|
+
execution["Id"]
|
|
51
|
+
end
|
|
52
|
+
|
|
45
53
|
def credentials
|
|
46
54
|
@context["Credentials"]
|
|
47
55
|
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.20.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- ManageIQ Developers
|
|
@@ -69,16 +69,22 @@ dependencies:
|
|
|
69
69
|
name: io-wait
|
|
70
70
|
requirement: !ruby/object:Gem::Requirement
|
|
71
71
|
requirements:
|
|
72
|
-
- - "
|
|
72
|
+
- - ">="
|
|
73
73
|
- !ruby/object:Gem::Version
|
|
74
74
|
version: 0.3.6
|
|
75
|
+
- - "<"
|
|
76
|
+
- !ruby/object:Gem::Version
|
|
77
|
+
version: 0.5.0
|
|
75
78
|
type: :runtime
|
|
76
79
|
prerelease: false
|
|
77
80
|
version_requirements: !ruby/object:Gem::Requirement
|
|
78
81
|
requirements:
|
|
79
|
-
- - "
|
|
82
|
+
- - ">="
|
|
80
83
|
- !ruby/object:Gem::Version
|
|
81
84
|
version: 0.3.6
|
|
85
|
+
- - "<"
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: 0.5.0
|
|
82
88
|
- !ruby/object:Gem::Dependency
|
|
83
89
|
name: json
|
|
84
90
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -155,14 +161,14 @@ dependencies:
|
|
|
155
161
|
requirements:
|
|
156
162
|
- - ">="
|
|
157
163
|
- !ruby/object:Gem::Version
|
|
158
|
-
version:
|
|
164
|
+
version: '0'
|
|
159
165
|
type: :development
|
|
160
166
|
prerelease: false
|
|
161
167
|
version_requirements: !ruby/object:Gem::Requirement
|
|
162
168
|
requirements:
|
|
163
169
|
- - ">="
|
|
164
170
|
- !ruby/object:Gem::Version
|
|
165
|
-
version:
|
|
171
|
+
version: '0'
|
|
166
172
|
- !ruby/object:Gem::Dependency
|
|
167
173
|
name: rake
|
|
168
174
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -242,12 +248,12 @@ files:
|
|
|
242
248
|
- examples/set-credential.asl
|
|
243
249
|
- examples/workflow.asl
|
|
244
250
|
- exe/floe
|
|
245
|
-
- floe.gemspec
|
|
246
251
|
- lib/floe.rb
|
|
247
252
|
- lib/floe/builtin_runner.rb
|
|
248
253
|
- lib/floe/builtin_runner/methods.rb
|
|
249
254
|
- lib/floe/builtin_runner/runner.rb
|
|
250
255
|
- lib/floe/cli.rb
|
|
256
|
+
- lib/floe/cli/logger.rb
|
|
251
257
|
- lib/floe/container_runner.rb
|
|
252
258
|
- lib/floe/container_runner/docker.rb
|
|
253
259
|
- lib/floe/container_runner/docker_mixin.rb
|
data/floe.gemspec
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "lib/floe/version"
|
|
4
|
-
|
|
5
|
-
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name = "floe"
|
|
7
|
-
spec.version = Floe::VERSION
|
|
8
|
-
spec.authors = ["ManageIQ Developers"]
|
|
9
|
-
|
|
10
|
-
spec.summary = "Floe is a runner for Amazon States Language workflows."
|
|
11
|
-
spec.description = spec.summary
|
|
12
|
-
spec.homepage = "https://github.com/ManageIQ/floe"
|
|
13
|
-
spec.licenses = ["Apache-2.0"]
|
|
14
|
-
spec.required_ruby_version = ">= 3.0.0"
|
|
15
|
-
|
|
16
|
-
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
|
17
|
-
spec.metadata['rubygems_mfa_required'] = "true"
|
|
18
|
-
|
|
19
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
|
20
|
-
spec.metadata["source_code_uri"] = spec.homepage
|
|
21
|
-
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
|
|
22
|
-
|
|
23
|
-
# Specify which files should be added to the gem when it is released.
|
|
24
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
25
|
-
spec.files = Dir.chdir(__dir__) do
|
|
26
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
|
27
|
-
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
spec.bindir = "exe"
|
|
31
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
32
|
-
spec.require_paths = ["lib"]
|
|
33
|
-
|
|
34
|
-
spec.add_dependency "activesupport", ">5.2"
|
|
35
|
-
spec.add_dependency "awesome_spawn", "~>1.6"
|
|
36
|
-
spec.add_dependency "faraday"
|
|
37
|
-
spec.add_dependency "faraday-follow_redirects"
|
|
38
|
-
spec.add_dependency "io-wait", "~> 0.3.6"
|
|
39
|
-
spec.add_dependency "json", "~>2.10"
|
|
40
|
-
spec.add_dependency "jsonpath", "~>1.1"
|
|
41
|
-
spec.add_dependency "kubeclient", "~>4.7"
|
|
42
|
-
spec.add_dependency "optimist", "~>3.0"
|
|
43
|
-
spec.add_dependency "parslet", "~>2.0"
|
|
44
|
-
|
|
45
|
-
spec.add_development_dependency "manageiq-style", ">= 1.5.2"
|
|
46
|
-
spec.add_development_dependency "rake", "~> 13.0"
|
|
47
|
-
spec.add_development_dependency "rspec"
|
|
48
|
-
spec.add_development_dependency "simplecov", ">= 0.21.2"
|
|
49
|
-
spec.add_development_dependency "timecop"
|
|
50
|
-
end
|