floe 0.6.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 +23 -1
- data/README.md +45 -0
- data/examples/set-credential.asl +26 -0
- data/exe/floe +52 -10
- data/floe.gemspec +0 -1
- data/lib/floe/version.rb +1 -1
- data/lib/floe/workflow/context.rb +4 -0
- data/lib/floe/workflow/path.rb +2 -4
- data/lib/floe/workflow/reference_path.rb +28 -11
- 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/input_output_mixin.rb +31 -0
- data/lib/floe/workflow/states/pass.rb +4 -4
- data/lib/floe/workflow/states/task.rb +3 -18
- data/lib/floe/workflow.rb +10 -16
- data/lib/floe.rb +1 -0
- metadata +4 -16
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,6 +4,26 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
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
|
+
|
|
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))
|
|
17
|
+
- Ensure that workflow credentials is not-nil ([#156](https://github.com/ManageIQ/floe/pull/156))
|
|
18
|
+
|
|
19
|
+
## [0.7.0] - 2023-12-18
|
|
20
|
+
### Changed
|
|
21
|
+
- Remove the dependency on more_core_extensions in ReferencePath ([#144](https://github.com/ManageIQ/floe/pull/144))
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
- Implement `ReferencePath#get` ([#144](https://github.com/ManageIQ/floe/pull/144))
|
|
25
|
+
- Allow a State to set a value in Credentials for subsequent states ([#145](https://github.com/ManageIQ/floe/pull/145))
|
|
26
|
+
|
|
7
27
|
## [0.6.1] - 2023-11-21
|
|
8
28
|
### Fixed
|
|
9
29
|
- Return an error payload if run_async! fails ([#143](https://github.com/ManageIQ/floe/pull/143))
|
|
@@ -106,7 +126,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|
|
106
126
|
### Added
|
|
107
127
|
- Initial release
|
|
108
128
|
|
|
109
|
-
[Unreleased]: https://github.com/ManageIQ/floe/compare/v0.
|
|
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
|
|
131
|
+
[0.7.0]: https://github.com/ManageIQ/floe/compare/v0.6.1...v0.7.0
|
|
110
132
|
[0.6.1]: https://github.com/ManageIQ/floe/compare/v0.6.0...v0.6.1
|
|
111
133
|
[0.6.0]: https://github.com/ManageIQ/floe/compare/v0.5.0...v0.6.0
|
|
112
134
|
[0.5.0]: https://github.com/ManageIQ/floe/compare/v0.4.1...v0.5.0
|
data/README.md
CHANGED
|
@@ -51,6 +51,48 @@ 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
|
+
|
|
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:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
bundle exec ruby exe/floe --workflow my-workflow.asl --credentials='{"username": "user", "password": "pass"}'
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
```json
|
|
71
|
+
{
|
|
72
|
+
"StartAt": "Login",
|
|
73
|
+
"States": {
|
|
74
|
+
"Login": {
|
|
75
|
+
"Type": "Task",
|
|
76
|
+
"Resource": "docker://login:latest",
|
|
77
|
+
"Credentials": {
|
|
78
|
+
"username.$": "$.username",
|
|
79
|
+
"password.$": "$.password"
|
|
80
|
+
},
|
|
81
|
+
"ResultPath": "$.Credentials",
|
|
82
|
+
"Next": "DoSomething"
|
|
83
|
+
},
|
|
84
|
+
"DoSomething": {
|
|
85
|
+
"Type": "Task",
|
|
86
|
+
"Resource": "docker://do-something:latest",
|
|
87
|
+
"Credentials": {
|
|
88
|
+
"token.$": "$.bearer_token"
|
|
89
|
+
},
|
|
90
|
+
"End": true
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
54
96
|
### Ruby Library
|
|
55
97
|
|
|
56
98
|
```ruby
|
|
@@ -120,6 +162,7 @@ end
|
|
|
120
162
|
Options supported by the Docker docker runner are:
|
|
121
163
|
|
|
122
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
|
|
123
166
|
|
|
124
167
|
#### Podman
|
|
125
168
|
|
|
@@ -129,6 +172,7 @@ Options supported by the podman docker runner are:
|
|
|
129
172
|
* `log-level=string` - Log messages above specified level (trace, debug, info, warn, warning, error, fatal, panic)
|
|
130
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`.
|
|
131
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
|
|
132
176
|
* `root=string` - Path to the root directory in which data, including images, is stored
|
|
133
177
|
* `runroot=string` - Path to the 'run directory' where all state information is stored
|
|
134
178
|
* `runtime=string` - Path to the OCI-compatible binary used to run containers
|
|
@@ -147,6 +191,7 @@ Options supported by the kubernetes docker runner are:
|
|
|
147
191
|
* `kubeconfig` - Path to a kubeconfig file, defaults to `KUBECONFIG` environment variable or `~/.kube/config`
|
|
148
192
|
* `kubeconfig_context` - Context to use in the kubeconfig file, defaults to `"default"`
|
|
149
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
|
|
150
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
|
|
151
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
|
|
152
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
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"Comment": "An example showing how to set a credential.",
|
|
3
|
+
"StartAt": "Login",
|
|
4
|
+
"States": {
|
|
5
|
+
"Login": {
|
|
6
|
+
"Type": "Task",
|
|
7
|
+
"Resource": "docker://docker.io/agrare/echo:latest",
|
|
8
|
+
"Parameters": {
|
|
9
|
+
"ECHO": "TOKEN"
|
|
10
|
+
},
|
|
11
|
+
"ResultPath": "$.Credentials",
|
|
12
|
+
"ResultSelector": {
|
|
13
|
+
"bearer_token.$": "$.echo"
|
|
14
|
+
},
|
|
15
|
+
"Next": "DoSomething"
|
|
16
|
+
},
|
|
17
|
+
"DoSomething": {
|
|
18
|
+
"Type": "Task",
|
|
19
|
+
"Resource": "docker://docker.io/agrare/hello-world:latest",
|
|
20
|
+
"Credentials": {
|
|
21
|
+
"bearer_token.$": "$.bearer_token"
|
|
22
|
+
},
|
|
23
|
+
"End": true
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
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/floe.gemspec
CHANGED
data/lib/floe/version.rb
CHANGED
data/lib/floe/workflow/path.rb
CHANGED
|
@@ -9,6 +9,8 @@ module Floe
|
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
+
attr_reader :payload
|
|
13
|
+
|
|
12
14
|
def initialize(payload)
|
|
13
15
|
@payload = payload
|
|
14
16
|
|
|
@@ -28,10 +30,6 @@ module Floe
|
|
|
28
30
|
|
|
29
31
|
results.count < 2 ? results.first : results
|
|
30
32
|
end
|
|
31
|
-
|
|
32
|
-
private
|
|
33
|
-
|
|
34
|
-
attr_reader :payload
|
|
35
33
|
end
|
|
36
34
|
end
|
|
37
35
|
end
|
|
@@ -4,37 +4,54 @@ module Floe
|
|
|
4
4
|
class Workflow
|
|
5
5
|
class ReferencePath < Path
|
|
6
6
|
class << self
|
|
7
|
+
def get(payload, context)
|
|
8
|
+
new(payload).get(context)
|
|
9
|
+
end
|
|
10
|
+
|
|
7
11
|
def set(payload, context, value)
|
|
8
12
|
new(payload).set(context, value)
|
|
9
13
|
end
|
|
10
14
|
end
|
|
11
15
|
|
|
12
|
-
|
|
13
|
-
require "more_core_extensions/core_ext/hash/nested"
|
|
14
|
-
require "more_core_extensions/core_ext/array/nested"
|
|
16
|
+
attr_reader :path
|
|
15
17
|
|
|
18
|
+
def initialize(*)
|
|
16
19
|
super
|
|
17
20
|
|
|
18
21
|
raise Floe::InvalidWorkflowError, "Invalid Reference Path" if payload.match?(/@|,|:|\?/)
|
|
22
|
+
@path = JsonPath.new(payload)
|
|
23
|
+
.path[1..]
|
|
24
|
+
.map { |v| v.match(/\[(?<name>.+)\]/)["name"] }
|
|
25
|
+
.map { |v| v[0] == "'" ? v.delete("'") : v.to_i }
|
|
26
|
+
.compact
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def get(context)
|
|
30
|
+
return context if path.empty?
|
|
31
|
+
|
|
32
|
+
context.dig(*path)
|
|
19
33
|
end
|
|
20
34
|
|
|
21
35
|
def set(context, value)
|
|
22
36
|
result = context.dup
|
|
23
37
|
|
|
24
|
-
path = JsonPath.new(payload)
|
|
25
|
-
.path[1..]
|
|
26
|
-
.map { |v| v.match(/\[(?<name>.+)\]/)["name"] }
|
|
27
|
-
.map { |v| v[0] == "'" ? v.delete("'") : v.to_i }
|
|
28
|
-
.compact
|
|
29
|
-
|
|
30
38
|
# If the payload is '$' then merge the value into the context
|
|
31
|
-
# otherwise
|
|
39
|
+
# otherwise store the value under the path
|
|
32
40
|
#
|
|
33
41
|
# TODO: how to handle non-hash values, raise error if path=$ and value not a hash?
|
|
34
42
|
if path.empty?
|
|
35
43
|
result.merge!(value)
|
|
36
44
|
else
|
|
37
|
-
result
|
|
45
|
+
child = result
|
|
46
|
+
keys = path.dup
|
|
47
|
+
last_key = keys.pop
|
|
48
|
+
|
|
49
|
+
keys.each do |key|
|
|
50
|
+
child[key] = {} if child[key].nil?
|
|
51
|
+
child = child[key]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
child[last_key] = value
|
|
38
55
|
end
|
|
39
56
|
|
|
40
57
|
result
|
|
@@ -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
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Floe
|
|
4
|
+
class Workflow
|
|
5
|
+
module States
|
|
6
|
+
module InputOutputMixin
|
|
7
|
+
def process_input(input)
|
|
8
|
+
input = input_path.value(context, input)
|
|
9
|
+
input = parameters.value(context, input) if parameters
|
|
10
|
+
input
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def process_output(input, results)
|
|
14
|
+
return input if results.nil?
|
|
15
|
+
return if output_path.nil?
|
|
16
|
+
|
|
17
|
+
results = result_selector.value(context, results) if @result_selector
|
|
18
|
+
if result_path.payload.start_with?("$.Credentials")
|
|
19
|
+
credentials = result_path.set(workflow.credentials, results)["Credentials"]
|
|
20
|
+
workflow.credentials.merge!(credentials)
|
|
21
|
+
output = input
|
|
22
|
+
else
|
|
23
|
+
output = result_path.set(input, results)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
output_path.value(context, output)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -4,6 +4,7 @@ module Floe
|
|
|
4
4
|
class Workflow
|
|
5
5
|
module States
|
|
6
6
|
class Pass < Floe::Workflow::State
|
|
7
|
+
include InputOutputMixin
|
|
7
8
|
include NonTerminalMixin
|
|
8
9
|
|
|
9
10
|
attr_reader :end, :next, :result, :parameters, :input_path, :output_path, :result_path
|
|
@@ -25,12 +26,11 @@ module Floe
|
|
|
25
26
|
|
|
26
27
|
def start(input)
|
|
27
28
|
super
|
|
28
|
-
output = input_path.value(context, input)
|
|
29
|
-
output = result_path.set(output, result) if result && result_path
|
|
30
|
-
output = output_path.value(context, output)
|
|
31
29
|
|
|
30
|
+
input = process_input(input)
|
|
31
|
+
|
|
32
|
+
context.output = process_output(input, result)
|
|
32
33
|
context.next_state = end? ? nil : @next
|
|
33
|
-
context.output = output
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def running?
|
|
@@ -4,6 +4,7 @@ module Floe
|
|
|
4
4
|
class Workflow
|
|
5
5
|
module States
|
|
6
6
|
class Task < Floe::Workflow::State
|
|
7
|
+
include InputOutputMixin
|
|
7
8
|
include NonTerminalMixin
|
|
8
9
|
|
|
9
10
|
attr_reader :credentials, :end, :heartbeat_seconds, :next, :parameters,
|
|
@@ -49,7 +50,7 @@ module Floe
|
|
|
49
50
|
|
|
50
51
|
if success?
|
|
51
52
|
output = parse_output(output)
|
|
52
|
-
context.state["Output"] = process_output
|
|
53
|
+
context.state["Output"] = process_output(context.input.dup, output)
|
|
53
54
|
context.next_state = next_state
|
|
54
55
|
else
|
|
55
56
|
error = parse_error(output)
|
|
@@ -126,12 +127,6 @@ module Floe
|
|
|
126
127
|
context.state["Error"] = context.output["Error"]
|
|
127
128
|
end
|
|
128
129
|
|
|
129
|
-
def process_input(input)
|
|
130
|
-
input = input_path.value(context, input)
|
|
131
|
-
input = parameters.value(context, input) if parameters
|
|
132
|
-
input
|
|
133
|
-
end
|
|
134
|
-
|
|
135
130
|
def parse_error(output)
|
|
136
131
|
return if output.nil?
|
|
137
132
|
return output if output.kind_of?(Hash)
|
|
@@ -142,24 +137,14 @@ module Floe
|
|
|
142
137
|
end
|
|
143
138
|
|
|
144
139
|
def parse_output(output)
|
|
145
|
-
return if output.nil?
|
|
146
140
|
return output if output.kind_of?(Hash)
|
|
141
|
+
return if output.nil? || output.empty?
|
|
147
142
|
|
|
148
143
|
JSON.parse(output.split("\n").last)
|
|
149
144
|
rescue JSON::ParserError
|
|
150
145
|
nil
|
|
151
146
|
end
|
|
152
147
|
|
|
153
|
-
def process_output!(results)
|
|
154
|
-
output = context.input.dup
|
|
155
|
-
return output if results.nil?
|
|
156
|
-
return if output_path.nil?
|
|
157
|
-
|
|
158
|
-
results = result_selector.value(context, results) if result_selector
|
|
159
|
-
output = result_path.set(output, results)
|
|
160
|
-
output_path.value(context, output)
|
|
161
|
-
end
|
|
162
|
-
|
|
163
148
|
def next_state
|
|
164
149
|
end? ? nil : @next
|
|
165
150
|
end
|
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
|
-
@credentials = credentials
|
|
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
|
data/lib/floe.rb
CHANGED
|
@@ -25,6 +25,7 @@ require_relative "floe/workflow/runner/podman"
|
|
|
25
25
|
require_relative "floe/workflow/state"
|
|
26
26
|
require_relative "floe/workflow/states/choice"
|
|
27
27
|
require_relative "floe/workflow/states/fail"
|
|
28
|
+
require_relative "floe/workflow/states/input_output_mixin"
|
|
28
29
|
require_relative "floe/workflow/states/map"
|
|
29
30
|
require_relative "floe/workflow/states/non_terminal_mixin"
|
|
30
31
|
require_relative "floe/workflow/states/parallel"
|
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.8.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:
|
|
11
|
+
date: 2024-01-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: awesome_spawn
|
|
@@ -52,20 +52,6 @@ dependencies:
|
|
|
52
52
|
- - "~>"
|
|
53
53
|
- !ruby/object:Gem::Version
|
|
54
54
|
version: '4.7'
|
|
55
|
-
- !ruby/object:Gem::Dependency
|
|
56
|
-
name: more_core_extensions
|
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
|
58
|
-
requirements:
|
|
59
|
-
- - ">="
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0'
|
|
62
|
-
type: :runtime
|
|
63
|
-
prerelease: false
|
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
-
requirements:
|
|
66
|
-
- - ">="
|
|
67
|
-
- !ruby/object:Gem::Version
|
|
68
|
-
version: '0'
|
|
69
55
|
- !ruby/object:Gem::Dependency
|
|
70
56
|
name: optimist
|
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -96,6 +82,7 @@ files:
|
|
|
96
82
|
- Gemfile
|
|
97
83
|
- README.md
|
|
98
84
|
- Rakefile
|
|
85
|
+
- examples/set-credential.asl
|
|
99
86
|
- examples/workflow.asl
|
|
100
87
|
- exe/floe
|
|
101
88
|
- floe.gemspec
|
|
@@ -123,6 +110,7 @@ files:
|
|
|
123
110
|
- lib/floe/workflow/state.rb
|
|
124
111
|
- lib/floe/workflow/states/choice.rb
|
|
125
112
|
- lib/floe/workflow/states/fail.rb
|
|
113
|
+
- lib/floe/workflow/states/input_output_mixin.rb
|
|
126
114
|
- lib/floe/workflow/states/map.rb
|
|
127
115
|
- lib/floe/workflow/states/non_terminal_mixin.rb
|
|
128
116
|
- lib/floe/workflow/states/parallel.rb
|