floe 0.11.2 → 0.12.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: 302eabcce76893426ed95920bff11ed2bba9956a7d006249d86212e30c5cc67c
4
- data.tar.gz: 4148b20da8ef81e6e45a4ec73489cdcf956115d1f99535019aef3b561ed45e00
3
+ metadata.gz: 2eb195983f47d7cac44f18e962bf314c3ff4fefbdf3ac5cae32e960bfc2c0fb0
4
+ data.tar.gz: 6a0f8d91337297786c03e99fc96cb118ed8cb76244e52cf8253702d1517a6f5d
5
5
  SHA512:
6
- metadata.gz: 73a1a714b1d729af89dd9839655955f76c3ed69fa8814e230fc03746175d1ff243b8b58fd02dce272d683b39fe50cd88c1c3a7f05cd2e36e9aa070747f85ae6f
7
- data.tar.gz: bf13a6c8f07a5918e78d99bb5a03c29b3a76e059c92cd6f3fec85dd64b1d701138a16a9c0e6df10ed820f4f97ef932152f1d0a61b38a60d3d344728da9ec33c7
6
+ metadata.gz: 99f5a3c8e67a9f5c97b43795739c201a49a8f1195d40a1da38f2aa0e16d200266783336e4be9ada5d41b566c545144a4533db258546ebbf04a23c454a489e6a1
7
+ data.tar.gz: a77bfe436b37adf9dbdc2a279b9b1f70f48980062aaaec191a8e1fd57238e8b8b50b02828025e561c7ccb8429cb67fefb3f7ed044ed309d82eaae8dd8d9d407b
data/.codeclimate.yml ADDED
@@ -0,0 +1,16 @@
1
+ prepare:
2
+ fetch:
3
+ - url: https://raw.githubusercontent.com/ManageIQ/manageiq-style/master/.rubocop_base.yml
4
+ path: ".rubocop_base.yml"
5
+ - url: https://raw.githubusercontent.com/ManageIQ/manageiq-style/master/.rubocop_cc_base.yml
6
+ path: ".rubocop_cc_base.yml"
7
+ - url: https://raw.githubusercontent.com/ManageIQ/manageiq-style/master/styles/base.yml
8
+ path: styles/base.yml
9
+ - url: https://raw.githubusercontent.com/ManageIQ/manageiq-style/master/styles/cc_base.yml
10
+ path: styles/cc_base.yml
11
+ plugins:
12
+ rubocop:
13
+ enabled: true
14
+ config: ".rubocop_cc.yml"
15
+ channel: rubocop-1-56-3
16
+ version: '2'
data/.yamllint CHANGED
@@ -1,8 +1,6 @@
1
- ---
2
1
  extends: relaxed
3
-
4
2
  rules:
5
3
  indentation:
6
4
  indent-sequences: false
7
5
  line-length:
8
- max: 120
6
+ max: 1000
data/CHANGELOG.md CHANGED
@@ -4,6 +4,54 @@ This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.12.0] - 2024-07-31
8
+ ### Added
9
+ - Set Floe.logger.level if DEBUG env var set ([#234](https://github.com/ManageIQ/floe/pull/234))
10
+ - Add InstrinsicFunction foundation + States.Array, States.UUID ([#194](https://github.com/ManageIQ/floe/pull/194))
11
+ - Evaluate IntrinsicFunctions from PayloadTemplate ([#236](https://github.com/ManageIQ/floe/pull/236))
12
+ - Add more intrinsic functions ([#242](https://github.com/ManageIQ/floe/pull/242))
13
+ - Add State#cause_path, error_path ([#249](https://github.com/ManageIQ/floe/pull/249))
14
+ - Validate Catcher Next ([#250](https://github.com/ManageIQ/floe/pull/250))
15
+ - Add spec/supports ([#248](https://github.com/ManageIQ/floe/pull/248))
16
+
17
+ ### Fixed
18
+ - Handle non-hash input/output values ([#214](https://github.com/ManageIQ/floe/pull/214))
19
+ - Fix Input/Output handling for Pass, Choice, Succeed states ([#225](https://github.com/ManageIQ/floe/pull/225))
20
+ - Wrap Parslet::ParseFailed errors as Floe::InvalidWorkflowError ([#235](https://github.com/ManageIQ/floe/pull/235))
21
+ - Fix edge cases with States.Array ([#237](https://github.com/ManageIQ/floe/pull/237))
22
+ - Fix sporadic test failure with Wait state ([#243](https://github.com/ManageIQ/floe/pull/243))
23
+ - Fix invalid container names after normalizing ([#252](https://github.com/ManageIQ/floe/pull/252))
24
+
25
+ ### Changed
26
+ - Extract ErrorMatcherMixin from Catch and Retry ([#186](https://github.com/ManageIQ/floe/pull/186))
27
+ - Output should be JSON ([#230](https://github.com/ManageIQ/floe/pull/230))
28
+ - Normalize functions to all take arguments ([#238](https://github.com/ManageIQ/floe/pull/238))
29
+ - Pass full path name to State.new for better errors ([#229](https://github.com/ManageIQ/floe/pull/229))
30
+ - Validate that state machine input is valid JSON ([#227](https://github.com/ManageIQ/floe/pull/227))
31
+ - Move the Parslet parse into initialize so that invalid function definition will fail on workflow load ([#245](https://github.com/ManageIQ/floe/pull/245))
32
+
33
+ ## [0.11.3] - 2024-06-20
34
+ ### Fixed
35
+ - ResultPath=$ replaces complete output ([#199](https://github.com/ManageIQ/floe/pull/199))
36
+ - Fix retrier backoff values ([#200](https://github.com/ManageIQ/floe/pull/200))
37
+ - Fix Retry issues ([#202](https://github.com/ManageIQ/floe/pull/202))
38
+ - Add Apache-2.0 license ([#217](https://github.com/ManageIQ/floe/pull/217))
39
+
40
+ ### Changed
41
+ - Update gemspec summary ([#205](https://github.com/ManageIQ/floe/pull/205))
42
+ - Simpler State#long_name ([#204](https://github.com/ManageIQ/floe/pull/204))
43
+ - State only modifies Context#state - prep for Map/Parallel ([#206](https://github.com/ManageIQ/floe/pull/206))
44
+ - Set StateHistory in Workflow not State ([#211](https://github.com/ManageIQ/floe/pull/211))
45
+ - Make Runner#wait optional ([#190](https://github.com/ManageIQ/floe/pull/190))
46
+ - Pass credentials around with context ([#203](https://github.com/ManageIQ/floe/pull/203))
47
+ - Pass context to State without workflow ([#216](https://github.com/ManageIQ/floe/pull/216))
48
+ - Move the guts of the CLI into a class for easy testing ([#220](https://github.com/ManageIQ/floe/pull/220))
49
+
50
+ ### Added
51
+ - Set State PreviousStateGuid in StateHistory ([#208](https://github.com/ManageIQ/floe/pull/208))
52
+ - Add a codeclimate config file ([#224](https://github.com/ManageIQ/floe/pull/224))
53
+ - Add an Execution unique ID to Context ([#226](https://github.com/ManageIQ/floe/pull/226))
54
+
7
55
  ## [0.11.2] - 2024-05-24
8
56
  ### Fixed
9
57
  - Output now based upon raw input not effective input ([#191](https://github.com/ManageIQ/floe/pull/191))
@@ -177,7 +225,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
177
225
  ### Added
178
226
  - Initial release
179
227
 
180
- [Unreleased]: https://github.com/ManageIQ/floe/compare/v0.11.2...HEAD
228
+ [Unreleased]: https://github.com/ManageIQ/floe/compare/v0.12.0...HEAD
229
+ [0.12.0]: https://github.com/ManageIQ/floe/compare/v0.11.3...v0.12.0
230
+ [0.11.3]: https://github.com/ManageIQ/floe/compare/v0.11.2...v0.11.3
181
231
  [0.11.2]: https://github.com/ManageIQ/floe/compare/v0.11.1...v0.11.2
182
232
  [0.11.1]: https://github.com/ManageIQ/floe/compare/v0.11.0...v0.11.1
183
233
  [0.11.0]: https://github.com/ManageIQ/floe/compare/v0.10.0...v0.11.0
data/Gemfile CHANGED
@@ -3,7 +3,7 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  plugin "bundler-inject", "~> 2.0"
6
- require File.join(Bundler::Plugin.index.load_paths("bundler-inject")[0], "bundler-inject") rescue nil
6
+ require File.join(Bundler::Plugin.index.load_paths("bundler-inject")[0], "bundler-inject") rescue nil # rubocop:disable Style/RescueModifier
7
7
 
8
8
  # Specify your gem's dependencies in floe.gemspec
9
9
  gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,202 @@
1
+
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
5
+
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7
+
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright [yyyy] [name of copyright owner]
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
data/README.md CHANGED
@@ -148,7 +148,7 @@ until running_workflows.empty?
148
148
  ready_workflows = Floe::Workflow.wait(running_workflows)
149
149
  # Step through the ready workflows until they would block
150
150
  ready_workflows.each do |workflow|
151
- loop while workflow.step_nonblock == 0
151
+ workflow.run_nonblock
152
152
  end
153
153
  # Remove any finished workflows from the list of running_workflows
154
154
  running_workflows.reject!(&:end?)
@@ -206,3 +206,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
206
206
  ## Contributing
207
207
 
208
208
  Bug reports and pull requests are welcome on GitHub at https://github.com/ManageIQ/floe.
209
+
210
+ ## License
211
+
212
+ The gem is available as open source under the terms of the [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
data/exe/floe CHANGED
@@ -3,75 +3,6 @@
3
3
 
4
4
  $LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
5
5
 
6
- require "optimist"
7
- require "floe"
8
- require "floe/container_runner"
9
-
10
- opts = Optimist.options do
11
- version("v#{Floe::VERSION}\n")
12
- usage("[options] workflow input [workflow2 input2]")
13
-
14
- opt :workflow, "Path to your workflow json file (alternative to passing a bare workflow)", :type => :string
15
- opt :input, <<~EOMSG, :type => :string
16
- JSON payload of the Input to the workflow
17
- If --input is passed and --workflow is not passed, will be used for all bare workflows listed.
18
- If --input is not passed and --workflow is passed, defaults to '{}'.
19
- EOMSG
20
- opt :context, "JSON payload of the Context", :type => :string
21
- opt :credentials, "JSON payload with Credentials", :type => :string
22
- opt :credentials_file, "Path to a file with Credentials", :type => :string
23
-
24
- Floe::ContainerRunner.cli_options(self)
25
-
26
- banner("")
27
- banner("General options:")
28
- end
29
-
30
- # Create workflow/input pairs from the various combinations of paramaters
31
- args =
32
- if opts[:workflow_given]
33
- Optimist.die("cannot specify both --workflow and bare workflows") if ARGV.any?
34
-
35
- [opts[:workflow], opts.fetch(:input, "{}")]
36
- elsif opts[:input_given]
37
- Optimist.die("workflow(s) must be specified") if ARGV.empty?
38
-
39
- ARGV.flat_map { |w| [w, opts[:input].dup] }
40
- else
41
- Optimist.die("workflow/input pairs must be specified") if ARGV.empty? || (ARGV.size > 1 && ARGV.size.odd?)
42
-
43
- ARGV
44
- end
45
-
46
- Floe::ContainerRunner.resolve_cli_options!(opts)
47
-
48
- require "logger"
49
- Floe.logger = Logger.new($stdout)
50
-
51
- credentials =
52
- if opts[:credentials_given]
53
- opts[:credentials] == "-" ? $stdin.read : opts[:credentials]
54
- elsif opts[:credentials_file_given]
55
- File.read(opts[:credentials_file])
56
- end
57
-
58
- workflows =
59
- args.each_slice(2).map do |workflow, input|
60
- context = Floe::Workflow::Context.new(opts[:context], :input => input)
61
- Floe::Workflow.load(workflow, context, credentials)
62
- end
63
-
64
- # run
65
-
66
- Floe::Workflow.wait(workflows, &:run_nonblock)
67
-
68
- # display status
69
-
70
- workflows.each do |workflow|
71
- puts "", "#{workflow.name}#{" (#{workflow.status})" unless workflow.context.success?}", "===" if workflows.size > 1
72
- puts workflow.output.inspect
73
- end
74
-
75
- # exit status
76
-
77
- exit workflows.all? { |workflow| workflow.context.success? } ? 0 : 1
6
+ require "floe/cli"
7
+ success = Floe::CLI.new.run(ARGV)
8
+ exit(success ? 0 : 1)
data/floe.gemspec CHANGED
@@ -7,9 +7,10 @@ Gem::Specification.new do |spec|
7
7
  spec.version = Floe::VERSION
8
8
  spec.authors = ["ManageIQ Developers"]
9
9
 
10
- spec.summary = "Simple Workflow Runner."
11
- spec.description = "Simple Workflow Runner."
10
+ spec.summary = "Floe is a runner for Amazon States Language workflows."
11
+ spec.description = spec.summary
12
12
  spec.homepage = "https://github.com/ManageIQ/floe"
13
+ spec.licenses = ["Apache-2.0"]
13
14
  spec.required_ruby_version = ">= 2.7.0"
14
15
 
15
16
  spec.metadata["allowed_push_host"] = "https://rubygems.org"
@@ -35,11 +36,11 @@ Gem::Specification.new do |spec|
35
36
  spec.add_dependency "jsonpath", "~>1.1"
36
37
  spec.add_dependency "kubeclient", "~>4.7"
37
38
  spec.add_dependency "optimist", "~>3.0"
39
+ spec.add_dependency "parslet", "~>2.0"
38
40
 
39
- spec.add_development_dependency "manageiq-style"
41
+ spec.add_development_dependency "manageiq-style", ">= 1.5.2"
40
42
  spec.add_development_dependency "rake", "~> 13.0"
41
43
  spec.add_development_dependency "rspec"
42
- spec.add_development_dependency "rubocop"
43
44
  spec.add_development_dependency "simplecov", ">= 0.21.2"
44
45
  spec.add_development_dependency "timecop"
45
46
  end
data/lib/floe/cli.rb ADDED
@@ -0,0 +1,86 @@
1
+ module Floe
2
+ class CLI
3
+ def initialize
4
+ require "optimist"
5
+ require "floe"
6
+ require "floe/container_runner"
7
+ require "logger"
8
+
9
+ Floe.logger = Logger.new($stdout)
10
+ Floe.logger.level = 0 if ENV["DEBUG"]
11
+ end
12
+
13
+ def run(args = ARGV)
14
+ workflows_inputs, opts = parse_options!(args)
15
+
16
+ credentials =
17
+ if opts[:credentials_given]
18
+ opts[:credentials] == "-" ? $stdin.read : opts[:credentials]
19
+ elsif opts[:credentials_file_given]
20
+ File.read(opts[:credentials_file])
21
+ end
22
+
23
+ workflows =
24
+ workflows_inputs.each_slice(2).map do |workflow, input|
25
+ context = Floe::Workflow::Context.new(opts[:context], :input => input, :credentials => credentials)
26
+ Floe::Workflow.load(workflow, context)
27
+ end
28
+
29
+ Floe::Workflow.wait(workflows, &:run_nonblock)
30
+
31
+ # Display status
32
+ workflows.each do |workflow|
33
+ puts "", "#{workflow.name}#{" (#{workflow.status})" unless workflow.context.success?}", "===" if workflows.size > 1
34
+ puts workflow.output
35
+ end
36
+
37
+ workflows.all? { |workflow| workflow.context.success? }
38
+ rescue Floe::Error => err
39
+ abort(err.message)
40
+ end
41
+
42
+ private
43
+
44
+ def parse_options!(args)
45
+ opts = Optimist.options(args) do
46
+ version("v#{Floe::VERSION}\n")
47
+ usage("[options] workflow input [workflow2 input2]")
48
+
49
+ opt :workflow, "Path to your workflow json file (alternative to passing a bare workflow)", :type => :string
50
+ opt :input, <<~EOMSG, :type => :string
51
+ JSON payload of the Input to the workflow
52
+ If --input is passed and --workflow is not passed, will be used for all bare workflows listed.
53
+ If --input is not passed and --workflow is passed, defaults to '{}'.
54
+ EOMSG
55
+ opt :context, "JSON payload of the Context", :type => :string
56
+ opt :credentials, "JSON payload with Credentials", :type => :string
57
+ opt :credentials_file, "Path to a file with Credentials", :type => :string
58
+
59
+ Floe::ContainerRunner.cli_options(self)
60
+
61
+ banner("")
62
+ banner("General options:")
63
+ end
64
+
65
+ # Create workflow/input pairs from the various combinations of paramaters
66
+ workflows_inputs =
67
+ if opts[:workflow_given]
68
+ Optimist.die("cannot specify both --workflow and bare workflows") if args.any?
69
+
70
+ [opts[:workflow], opts.fetch(:input, "{}")]
71
+ elsif opts[:input_given]
72
+ Optimist.die("workflow(s) must be specified") if args.empty?
73
+
74
+ args.flat_map { |w| [w, opts[:input].dup] }
75
+ else
76
+ Optimist.die("workflow/input pairs must be specified") if args.empty? || (args.size > 1 && args.size.odd?)
77
+
78
+ args
79
+ end
80
+
81
+ Floe::ContainerRunner.resolve_cli_options!(opts)
82
+
83
+ return workflows_inputs, opts
84
+ end
85
+ end
86
+ end
@@ -24,6 +24,9 @@ module Floe
24
24
  # This does not follow the leading and trailing character restriction because we will embed it
25
25
  # below with a prefix and suffix that already conform to the RFC.
26
26
  normalized_name = name.downcase.gsub(/[^a-z0-9-]/, "-")[0, MAX_CONTAINER_NAME_SIZE]
27
+ # Ensure that the normalized_name doesn't end in any invalid characters after we
28
+ # limited the length to the MAX_CONTAINER_NAME_SIZE.
29
+ normalized_name.gsub!(/[^a-z0-9]+$/, "")
27
30
 
28
31
  "floe-#{normalized_name}-#{SecureRandom.hex(4)}"
29
32
  end
data/lib/floe/runner.rb CHANGED
@@ -6,7 +6,7 @@ module Floe
6
6
 
7
7
  OUTPUT_MARKER = "__FLOE_OUTPUT__\n"
8
8
 
9
- def initialize(_options = {})
9
+ def initialize(_options = {}) # rubocop:disable Style/RedundantInitialize
10
10
  end
11
11
 
12
12
  @runners = {}
@@ -75,8 +75,13 @@ module Floe
75
75
  raise NotImplementedError, "Must be implemented in a subclass"
76
76
  end
77
77
 
78
- def wait(timeout: nil, events: %i[create update delete])
79
- raise NotImplementedError, "Must be implemented in a subclass"
80
- end
78
+ # Optional Watcher for events that is run in another thread.
79
+ #
80
+ # @yield [event, runner_context]
81
+ # @yieldparam [Symbol] event values: :create :update :delete :unknown
82
+ # @yieldparam [Hash] runner_context context provided by runner
83
+ # def wait(timeout: nil, events: %i[create update delete])
84
+ # raise NotImplementedError, "Must be implemented in a subclass"
85
+ # end
81
86
  end
82
87
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Floe
4
+ module ValidationMixin
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ def parser_error!(comment)
10
+ self.class.parser_error!(name, comment)
11
+ end
12
+
13
+ def missing_field_error!(field_name)
14
+ self.class.missing_field_error!(name, field_name)
15
+ end
16
+
17
+ def invalid_field_error!(field_name, field_value = nil, comment = nil)
18
+ self.class.invalid_field_error!(name, field_name, field_value, comment)
19
+ end
20
+
21
+ def workflow_state?(field_value, workflow)
22
+ workflow.payload["States"] ? workflow.payload["States"].include?(field_value) : true
23
+ end
24
+
25
+ def wrap_parser_error(field_name, field_value)
26
+ yield
27
+ rescue ArgumentError, InvalidWorkflowError => error
28
+ invalid_field_error!(field_name, field_value, error.message)
29
+ end
30
+
31
+ module ClassMethods
32
+ def parser_error!(name, comment)
33
+ name = name.join(".") if name.kind_of?(Array)
34
+ raise Floe::InvalidWorkflowError, "#{name} #{comment}"
35
+ end
36
+
37
+ def missing_field_error!(name, field_name)
38
+ parser_error!(name, "does not have required field \"#{field_name}\"")
39
+ end
40
+
41
+ def invalid_field_error!(name, field_name, field_value, comment)
42
+ # instead of displaying a large hash or array, just displaying the word Hash or Array
43
+ field_value = field_value.class if field_value.kind_of?(Hash) || field_value.kind_of?(Array)
44
+
45
+ parser_error!(name, "field \"#{field_name}\"#{" value \"#{field_value}\"" unless field_value.nil?} #{comment}")
46
+ end
47
+ end
48
+ end
49
+ end
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.11.2"
4
+ VERSION = "0.12.0"
5
5
  end
@@ -3,14 +3,28 @@
3
3
  module Floe
4
4
  class Workflow
5
5
  class Catcher
6
- attr_reader :error_equals, :next, :result_path
6
+ include ErrorMatcherMixin
7
+ include ValidationMixin
7
8
 
8
- def initialize(payload)
9
- @payload = payload
9
+ attr_reader :error_equals, :next, :result_path, :name
10
+
11
+ def initialize(workflow, name, payload)
12
+ @name = name
13
+ @payload = payload
10
14
 
11
15
  @error_equals = payload["ErrorEquals"]
12
16
  @next = payload["Next"]
13
17
  @result_path = ReferencePath.new(payload.fetch("ResultPath", "$"))
18
+
19
+ missing_field_error!("ErrorEquals") if !@error_equals.kind_of?(Array) || @error_equals.empty?
20
+ validate_state_next!(workflow)
21
+ end
22
+
23
+ private
24
+
25
+ def validate_state_next!(workflow)
26
+ missing_field_error!("Next") if @next.nil?
27
+ invalid_field_error!("Next", @next, "is not found in \"States\"") if @next && !workflow_state?(@next, workflow)
14
28
  end
15
29
  end
16
30
  end