floe 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 88cfa362fcb949aeaa6e9b0b799bff4bbb0e6b7158c95920545300ba063d6a0d
4
- data.tar.gz: 14df3cddf7a1811e945f533b88fcacd6611566e212cacb88bebf2da0bae9fe67
3
+ metadata.gz: c121968c8c3f9c70edaa20c0ab3e7937206623b7c06c0cf0f300aceddcde7814
4
+ data.tar.gz: 943d9e77a9e8309a771e57fee5d6f70e5026a4d254b0da5072b768a99f427d1a
5
5
  SHA512:
6
- metadata.gz: 90a3cdfdb241242d5b52ad403e7bbbf409476efec6d01d16f8f6f223b6f5fa3e227647da98ab2fb9466993982793be32684521ca5a268e73ab191ff7b6a025fb
7
- data.tar.gz: c1194b741493a3db2f3abdf083ef2858b25af61442584c6a2001d222579c78e5057f0a7efd3c6c904e02d898c296f543c5af1889c2e4bb91c95f9197d327fa35
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.1] - 2024-01-17
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.7.1...HEAD
122
- [0.7.1]: https://github.com/ManageIQ/floe/compare/v0.7.0...v0.7.1
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
- opt :workflow, "Path to your workflow json", :type => :string, :required => true
10
- opt :input, "JSON payload to input to the workflow", :default => '{}'
11
- opt :credentials, "JSON payload with credentials", :default => "{}"
12
- opt :docker_runner, "Type of runner for docker images", :default => "docker"
13
- opt :docker_runner_options, "Options to pass to the runner", :type => :strings
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
- context = Floe::Workflow::Context.new(:input => opts[:input])
35
- workflow = Floe::Workflow.load(opts[:workflow], context, opts[:credentials])
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
- workflow.run!
80
+ # exit status
38
81
 
39
- puts workflow.output.inspect
40
- exit workflow.status == "success" ? 0 : 1
82
+ exit workflows.all? { |workflow| workflow.context.success? } ? 0 : 1
data/lib/floe/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Floe
4
- VERSION = "0.7.1".freeze
4
+ VERSION = "0.8.0".freeze
5
5
  end
@@ -74,6 +74,10 @@ module Floe
74
74
  end
75
75
  end
76
76
 
77
+ def success?
78
+ status == "success"
79
+ end
80
+
77
81
  def state=(val)
78
82
  @context["State"] = val
79
83
  end
@@ -14,7 +14,8 @@ module Floe
14
14
 
15
15
  super
16
16
 
17
- @network = options.fetch("network", "bridge")
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("docker", params)}")
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 << [:net, "host"] if @network == "host"
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
@@ -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
 
@@ -137,8 +137,8 @@ module Floe
137
137
  end
138
138
 
139
139
  def parse_output(output)
140
- return if output.nil?
141
140
  return output if output.kind_of?(Hash)
141
+ return if output.nil? || output.empty?
142
142
 
143
143
  JSON.parse(output.split("\n").last)
144
144
  rescue JSON::ParserError
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
- new(payload, context, credentials)
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 { |name, state| State.build!(self, name, state) }
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
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.7.1
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ManageIQ Developers