floe 0.7.1 → 0.8.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 -3
- data/README.md +13 -0
- data/exe/floe +52 -10
- data/lib/floe/version.rb +1 -1
- data/lib/floe/workflow/context.rb +4 -0
- data/lib/floe/workflow/runner/docker.rb +4 -2
- data/lib/floe/workflow/runner/kubernetes.rb +2 -0
- data/lib/floe/workflow/runner/podman.rb +3 -1
- data/lib/floe/workflow/state.rb +0 -4
- data/lib/floe/workflow/states/task.rb +1 -1
- data/lib/floe/workflow.rb +9 -15
- 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: c121968c8c3f9c70edaa20c0ab3e7937206623b7c06c0cf0f300aceddcde7814
|
4
|
+
data.tar.gz: 943d9e77a9e8309a771e57fee5d6f70e5026a4d254b0da5072b768a99f427d1a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4fa820c364e8dc3266c2fc1eb29d682e72a9d2ab4d8f154aac3a88ff6d4c35e45e124fe714b80110b52e78a0ab654e25f956bc8a39f45f05f3990c0ab26d01ed
|
7
|
+
data.tar.gz: b62ac48c4105497c1d3dd2f2ef9d8aec60c25bcadf71046e8488175705365d0d84facde34c11f0047660b5ab2f4390f13babbe8ba2ab6becf31027f027e0b833
|
data/CHANGELOG.md
CHANGED
@@ -4,8 +4,16 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
4
4
|
|
5
5
|
## [Unreleased]
|
6
6
|
|
7
|
-
## [0.
|
7
|
+
## [0.8.0] - 2024-01-17
|
8
|
+
### Added
|
9
|
+
- Add CLI shorthand options for docker runner ([#147](https://github.com/ManageIQ/floe/pull/147))
|
10
|
+
- Run multiple workflows in exe/floe ([#149](https://github.com/ManageIQ/floe/pull/149))
|
11
|
+
- Add secure options for passing credentials via command-line ([#151](https://github.com/ManageIQ/floe/pull/151))
|
12
|
+
- Add a Docker Runner pull-policy option ([#155](https://github.com/ManageIQ/floe/pull/155))
|
13
|
+
|
8
14
|
### Fixed
|
15
|
+
- Fix podman with empty output ([#150](https://github.com/ManageIQ/floe/pull/150))
|
16
|
+
- Fix run_container logger saying docker when using podman ([#154](https://github.com/ManageIQ/floe/pull/154))
|
9
17
|
- Ensure that workflow credentials is not-nil ([#156](https://github.com/ManageIQ/floe/pull/156))
|
10
18
|
|
11
19
|
## [0.7.0] - 2023-12-18
|
@@ -118,8 +126,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
118
126
|
### Added
|
119
127
|
- Initial release
|
120
128
|
|
121
|
-
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.
|
122
|
-
[0.
|
129
|
+
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.8.0...HEAD
|
130
|
+
[0.8.0]: https://github.com/ManageIQ/floe/compare/v0.7.0...v0.8.0
|
123
131
|
[0.7.0]: https://github.com/ManageIQ/floe/compare/v0.6.1...v0.7.0
|
124
132
|
[0.6.1]: https://github.com/ManageIQ/floe/compare/v0.6.0...v0.6.1
|
125
133
|
[0.6.0]: https://github.com/ManageIQ/floe/compare/v0.5.0...v0.6.0
|
data/README.md
CHANGED
@@ -51,6 +51,16 @@ You can provide that at runtime via the `--credentials` parameter:
|
|
51
51
|
bundle exec ruby exe/floe --workflow my-workflow.asl --credentials='{"roleArn": "arn:aws:iam::111122223333:role/LambdaRole"}'
|
52
52
|
```
|
53
53
|
|
54
|
+
Or if you are running the floe command programmatically you can securely provide the credentials via a stdin pipe via `--credentials=-`:
|
55
|
+
```
|
56
|
+
echo '{"roleArn": "arn:aws:iam::111122223333:role/LambdaRole"}' | bundle exec ruby exe/floe --workflow my-workflow.asl --credentials -
|
57
|
+
```
|
58
|
+
|
59
|
+
Or you can pass a file path with the `--credentials-file` parameter:
|
60
|
+
```
|
61
|
+
bundle exec ruby exe/floe --workflow my-workflow.asl --credentials-file /tmp/20231218-80537-kj494t
|
62
|
+
```
|
63
|
+
|
54
64
|
If you need to set a credential at runtime you can do that by using the `"ResultPath": "$.Credentials"` directive, for example to user a username/password to login and get a Bearer token:
|
55
65
|
|
56
66
|
```
|
@@ -152,6 +162,7 @@ end
|
|
152
162
|
Options supported by the Docker docker runner are:
|
153
163
|
|
154
164
|
* `network` - What docker to connect the container to, defaults to `"bridge"`. If you need access to host resources for development you can pass `network=host`.
|
165
|
+
* `pull-policy` - Pull image policy. The default is missing. Allowed values: always, missing, never
|
155
166
|
|
156
167
|
#### Podman
|
157
168
|
|
@@ -161,6 +172,7 @@ Options supported by the podman docker runner are:
|
|
161
172
|
* `log-level=string` - Log messages above specified level (trace, debug, info, warn, warning, error, fatal, panic)
|
162
173
|
* `network=string` - What docker to connect the container to, defaults to `"bridge"`. If you need access to host resources for development you can pass `network=host`.
|
163
174
|
* `noout=boolean` - do not output to stdout
|
175
|
+
* `pull-policy=string` - Pull image policy. The default is missing. Allowed values: always, missing, never, newer
|
164
176
|
* `root=string` - Path to the root directory in which data, including images, is stored
|
165
177
|
* `runroot=string` - Path to the 'run directory' where all state information is stored
|
166
178
|
* `runtime=string` - Path to the OCI-compatible binary used to run containers
|
@@ -179,6 +191,7 @@ Options supported by the kubernetes docker runner are:
|
|
179
191
|
* `kubeconfig` - Path to a kubeconfig file, defaults to `KUBECONFIG` environment variable or `~/.kube/config`
|
180
192
|
* `kubeconfig_context` - Context to use in the kubeconfig file, defaults to `"default"`
|
181
193
|
* `namespace` - Namespace to use when creating kubernetes resources, defaults to `"default"`
|
194
|
+
* `pull-policy` - Pull image policy. The default is Always. Allowed values: IfNotPresent, Always, Never
|
182
195
|
* `server` - A kubernetes API Server URL, overrides anything in your kubeconfig file. If set `KUBERNETES_SERVICE_HOST` and `KUBERNETES_SERVICE_PORT` will be used
|
183
196
|
* `token` - A bearer_token to use to authenticate to the kubernetes API, overrides anything in your kubeconfig file. If present, `/run/secrets/kubernetes.io/serviceaccount/token` will be used
|
184
197
|
* `ca_file` - Path to a certificate-authority file for the kubernetes API, only valid if server and token are passed. If present `/run/secrets/kubernetes.io/serviceaccount/ca.crt` will be used
|
data/exe/floe
CHANGED
@@ -6,15 +6,31 @@ require "optimist"
|
|
6
6
|
|
7
7
|
opts = Optimist.options do
|
8
8
|
version("v#{Floe::VERSION}\n")
|
9
|
-
|
10
|
-
|
11
|
-
opt :
|
12
|
-
opt :
|
13
|
-
opt :
|
9
|
+
usage("[options] workflow input [workflow2 input2]")
|
10
|
+
|
11
|
+
opt :workflow, "Path to your workflow json (legacy)", :type => :string
|
12
|
+
opt :input, "JSON payload to input to the workflow (legacy)", :type => :string
|
13
|
+
opt :credentials, "JSON payload with credentials", :type => :string
|
14
|
+
opt :credentials_file, "Path to a file with credentials", :type => :string
|
15
|
+
opt :docker_runner, "Type of runner for docker images", :type => :string, :short => 'r'
|
16
|
+
opt :docker_runner_options, "Options to pass to the runner", :type => :strings, :short => 'o'
|
17
|
+
|
18
|
+
opt :docker, "Use docker to run images (short for --docker_runner=docker)", :type => :boolean
|
19
|
+
opt :podman, "Use podman to run images (short for --docker_runner=podman)", :type => :boolean
|
20
|
+
opt :kubernetes, "Use kubernetes to run images (short for --docker_runner=kubernetes)", :type => :boolean
|
14
21
|
end
|
15
22
|
|
16
23
|
Optimist.die(:docker_runner, "must be one of #{Floe::Workflow::Runner::TYPES.join(", ")}") unless Floe::Workflow::Runner::TYPES.include?(opts[:docker_runner])
|
17
24
|
|
25
|
+
# legacy support for --workflow
|
26
|
+
args = ARGV.empty? ? [opts[:workflow], opts[:input]] : ARGV
|
27
|
+
Optimist.die(:workflow, "must be specified") if args.empty?
|
28
|
+
|
29
|
+
# shortcut support
|
30
|
+
opts[:docker_runner] ||= "docker" if opts[:docker]
|
31
|
+
opts[:docker_runner] ||= "podman" if opts[:podman]
|
32
|
+
opts[:docker_runner] ||= "kubernetes" if opts[:kubernetes]
|
33
|
+
|
18
34
|
require "logger"
|
19
35
|
Floe.logger = Logger.new($stdout)
|
20
36
|
|
@@ -31,10 +47,36 @@ runner_options = opts[:docker_runner_options].to_h { |opt| opt.split("=", 2) }
|
|
31
47
|
|
32
48
|
Floe::Workflow::Runner.docker_runner = runner_klass.new(runner_options)
|
33
49
|
|
34
|
-
|
35
|
-
|
50
|
+
credentials =
|
51
|
+
if opts[:credentials_given]
|
52
|
+
opts[:credentials] == "-" ? $stdin.read : opts[:credentials]
|
53
|
+
elsif opts[:credentials_file_given]
|
54
|
+
File.read(opts[:credentials_file])
|
55
|
+
end
|
56
|
+
|
57
|
+
workflows =
|
58
|
+
args.each_slice(2).map do |workflow, input|
|
59
|
+
context = Floe::Workflow::Context.new(:input => input || opts[:input] || "{}")
|
60
|
+
Floe::Workflow.load(workflow, context, credentials)
|
61
|
+
end
|
62
|
+
|
63
|
+
# run
|
64
|
+
|
65
|
+
outstanding = workflows.dup
|
66
|
+
until outstanding.empty?
|
67
|
+
ready = outstanding.select(&:step_nonblock_ready?)
|
68
|
+
ready.map(&:run_nonblock)
|
69
|
+
outstanding -= ready.select(&:end?)
|
70
|
+
sleep(1) if !outstanding.empty?
|
71
|
+
end
|
72
|
+
|
73
|
+
# display status
|
74
|
+
|
75
|
+
workflows.each do |workflow|
|
76
|
+
puts "", "#{workflow.name}#{" (#{workflow.status})" unless workflow.context.success?}", "===" if workflows.size > 1
|
77
|
+
puts workflow.output.inspect
|
78
|
+
end
|
36
79
|
|
37
|
-
|
80
|
+
# exit status
|
38
81
|
|
39
|
-
|
40
|
-
exit workflow.status == "success" ? 0 : 1
|
82
|
+
exit workflows.all? { |workflow| workflow.context.success? } ? 0 : 1
|
data/lib/floe/version.rb
CHANGED
@@ -14,7 +14,8 @@ module Floe
|
|
14
14
|
|
15
15
|
super
|
16
16
|
|
17
|
-
@network
|
17
|
+
@network = options.fetch("network", "bridge")
|
18
|
+
@pull_policy = options["pull-policy"]
|
18
19
|
end
|
19
20
|
|
20
21
|
def run_async!(resource, env = {}, secrets = {})
|
@@ -72,7 +73,7 @@ module Floe
|
|
72
73
|
def run_container(image, env, secrets_file)
|
73
74
|
params = run_container_params(image, env, secrets_file)
|
74
75
|
|
75
|
-
logger.debug("Running #{AwesomeSpawn.build_command_line(
|
76
|
+
logger.debug("Running #{AwesomeSpawn.build_command_line(self.class::DOCKER_COMMAND, params)}")
|
76
77
|
|
77
78
|
result = docker!(*params)
|
78
79
|
result.output
|
@@ -83,6 +84,7 @@ module Floe
|
|
83
84
|
params << :detach
|
84
85
|
params += env.map { |k, v| [:e, "#{k}=#{v}"] }
|
85
86
|
params << [:e, "_CREDENTIALS=/run/secrets"] if secrets_file
|
87
|
+
params << [:pull, @pull_policy] if @pull_policy
|
86
88
|
params << [:net, "host"] if @network == "host"
|
87
89
|
params << [:v, "#{secrets_file}:/run/secrets:z"] if secrets_file
|
88
90
|
params << [:name, container_name(image)]
|
@@ -40,6 +40,7 @@ module Floe
|
|
40
40
|
|
41
41
|
@namespace = options.fetch("namespace", "default")
|
42
42
|
|
43
|
+
@pull_policy = options["pull-policy"]
|
43
44
|
@task_service_account = options["task_service_account"]
|
44
45
|
|
45
46
|
super
|
@@ -143,6 +144,7 @@ module Floe
|
|
143
144
|
}
|
144
145
|
}
|
145
146
|
|
147
|
+
spec[:spec][:imagePullPolicy] = @pull_policy if @pull_policy
|
146
148
|
spec[:spec][:serviceAccountName] = @task_service_account if @task_service_account
|
147
149
|
|
148
150
|
if secret
|
@@ -16,6 +16,7 @@ module Floe
|
|
16
16
|
@log_level = options["log-level"]
|
17
17
|
@network = options["network"]
|
18
18
|
@noout = options["noout"].to_s == "true" if options.key?("noout")
|
19
|
+
@pull_policy = options["pull-policy"]
|
19
20
|
@root = options["root"]
|
20
21
|
@runroot = options["runroot"]
|
21
22
|
@runtime = options["runtime"]
|
@@ -35,7 +36,8 @@ module Floe
|
|
35
36
|
params << :detach
|
36
37
|
params += env.map { |k, v| [:e, "#{k}=#{v}"] }
|
37
38
|
params << [:e, "_CREDENTIALS=/run/secrets/#{secret}"] if secret
|
38
|
-
params << [:
|
39
|
+
params << [:pull, @pull_policy] if @pull_policy
|
40
|
+
params << [:net, "host"] if @network == "host"
|
39
41
|
params << [:secret, secret] if secret
|
40
42
|
params << [:name, container_name(image)]
|
41
43
|
params << image
|
data/lib/floe/workflow/state.rb
CHANGED
@@ -33,10 +33,6 @@ module Floe
|
|
33
33
|
raise Floe::InvalidWorkflowError, "State name [#{name}] must be less than or equal to 80 characters" if name.length > 80
|
34
34
|
end
|
35
35
|
|
36
|
-
def run!(_input = nil)
|
37
|
-
wait until run_nonblock! == 0
|
38
|
-
end
|
39
|
-
|
40
36
|
def wait(timeout: 5)
|
41
37
|
start = Time.now.utc
|
42
38
|
|
data/lib/floe/workflow.rb
CHANGED
@@ -8,9 +8,12 @@ module Floe
|
|
8
8
|
include Logging
|
9
9
|
|
10
10
|
class << self
|
11
|
-
def load(path_or_io, context = nil, credentials = {})
|
11
|
+
def load(path_or_io, context = nil, credentials = {}, name = nil)
|
12
12
|
payload = path_or_io.respond_to?(:read) ? path_or_io.read : File.read(path_or_io)
|
13
|
-
|
13
|
+
# default the name if it is a filename and none was passed in
|
14
|
+
name ||= path_or_io.respond_to?(:read) ? "stream" : path_or_io.split("/").last.split(".").first
|
15
|
+
|
16
|
+
new(payload, context, credentials, name)
|
14
17
|
end
|
15
18
|
|
16
19
|
def wait(workflows, timeout: 5)
|
@@ -31,9 +34,9 @@ module Floe
|
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
34
|
-
attr_reader :context, :credentials, :payload, :states, :states_by_name, :start_at
|
37
|
+
attr_reader :context, :credentials, :payload, :states, :states_by_name, :start_at, :name
|
35
38
|
|
36
|
-
def initialize(payload, context = nil, credentials = {})
|
39
|
+
def initialize(payload, context = nil, credentials = {}, name = nil)
|
37
40
|
payload = JSON.parse(payload) if payload.kind_of?(String)
|
38
41
|
credentials = JSON.parse(credentials) if credentials.kind_of?(String)
|
39
42
|
context = Context.new(context) unless context.kind_of?(Context)
|
@@ -42,12 +45,13 @@ module Floe
|
|
42
45
|
raise Floe::InvalidWorkflowError, "Missing field \"StartAt\"" if payload["StartAt"].nil?
|
43
46
|
raise Floe::InvalidWorkflowError, "\"StartAt\" not in the \"States\" field" unless payload["States"].key?(payload["StartAt"])
|
44
47
|
|
48
|
+
@name = name
|
45
49
|
@payload = payload
|
46
50
|
@context = context
|
47
51
|
@credentials = credentials || {}
|
48
52
|
@start_at = payload["StartAt"]
|
49
53
|
|
50
|
-
@states = payload["States"].to_a.map { |
|
54
|
+
@states = payload["States"].to_a.map { |state_name, state| State.build!(self, state_name, state) }
|
51
55
|
@states_by_name = @states.each_with_object({}) { |state, result| result[state.name] = state }
|
52
56
|
|
53
57
|
unless context.state.key?("Name")
|
@@ -58,16 +62,6 @@ module Floe
|
|
58
62
|
raise Floe::InvalidWorkflowError, err.message
|
59
63
|
end
|
60
64
|
|
61
|
-
def run!
|
62
|
-
step until end?
|
63
|
-
self
|
64
|
-
end
|
65
|
-
|
66
|
-
def step
|
67
|
-
step_nonblock_wait until step_nonblock == 0
|
68
|
-
self
|
69
|
-
end
|
70
|
-
|
71
65
|
def run_nonblock
|
72
66
|
loop while step_nonblock == 0 && !end?
|
73
67
|
self
|