floe 0.2.3 → 0.3.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 68fc7de6c5112f8717f2941c4f89f22d688092a2ab15a6fe5b859c0f6684c177
4
- data.tar.gz: 23172e12b50792acbc0891dd9b0fa0a9e4d0583aa6e52e85fae408faa33ffdd4
3
+ metadata.gz: ae01d21cee834a23c32b89a8a1418d5c02e1a7ec825305cce2ea783c9be6fc40
4
+ data.tar.gz: df5f35ef07d199e929e7cf3bb0a73620a6ee5def9b555abf1409a9f74c6f7b9a
5
5
  SHA512:
6
- metadata.gz: 157482ec4fd0914d18cb87c06d1ee61f91668baf9c6592451f6c8c1e5de206f088256ef8c45ff29e6d26fb175e3750e1b0ed1008a76910f6520bd424977a0763
7
- data.tar.gz: 7c5c586d0087914944e577a6af4f8ca99ebc8ef15798fcb6ed5f50b46a99e368c8315b087c1ad823e2809b24e8539f479f26699b56618324b4850e98bed9b63e
6
+ metadata.gz: e4dcf6ccfcf37b0cd187f4dfd876d60deec4f3a1037c20f76e4b759ae61cd394f68afcb23064c3a6c30f76f56e59ff038931e1630a5e7b01393702caf339d3fa
7
+ data.tar.gz: 0c588d1eb15f71e4ccd9870df09d72e0e1914f6d31d51b0275f39fb9a7fdd68930eb54f254b4dfa13cc184d0cf9b7babae671f7b2bfa5df1f87d77661b3e5036
data/CHANGELOG.md CHANGED
@@ -4,6 +4,18 @@ This project adheres to [Semantic Versioning](http://semver.org/).
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [0.3.1] - 2023-08-29
8
+ ### Added
9
+ - Add more global podman runner options ([#90])(https://github.com/ManageIQ/floe/pull/90)
10
+
11
+ ## [0.3.0] - 2023-08-07
12
+ ### Added
13
+ - Add --network=host option to Docker/Podman runners ([#81])(https://github.com/ManageIQ/floe/pull/81)
14
+
15
+ ### Fixed
16
+ - Fix PayloadTemplate value transformation rules ([#78])(https://github.com/ManageIQ/floe/pull/78)
17
+ - Move end out of the root state node ([#80])(https://github.com/ManageIQ/floe/pull/80)
18
+
7
19
  ## [0.2.3] - 2023-07-28
8
20
  ### Fixed
9
21
  - Fix storing next_state in Context ([#76])(https://github.com/ManageIQ/floe/pull/76)
@@ -46,7 +58,10 @@ This project adheres to [Semantic Versioning](http://semver.org/).
46
58
  ### Added
47
59
  - Initial release
48
60
 
49
- [Unreleased]: https://github.com/ManageIQ/floe/compare/v0.2.2...HEAD
61
+ [Unreleased]: https://github.com/ManageIQ/floe/compare/v0.3.1...HEAD
62
+ [0.3.1]: https://github.com/ManageIQ/floe/compare/v0.3.0...v0.3.1
63
+ [0.3.0]: https://github.com/ManageIQ/floe/compare/v0.2.3...v0.3.0
64
+ [0.2.3]: https://github.com/ManageIQ/floe/compare/v0.2.2...v0.2.3
50
65
  [0.2.2]: https://github.com/ManageIQ/floe/compare/v0.2.1...v0.2.2
51
66
  [0.2.1]: https://github.com/ManageIQ/floe/compare/v0.2.0...v0.2.1
52
67
  [0.2.0]: https://github.com/ManageIQ/floe/compare/v0.1.1...v0.2.0
data/README.md CHANGED
@@ -36,6 +36,21 @@ bundle exec ruby exe/floe --workflow examples/workflow.asl --inputs='{"foo": 1}'
36
36
  bundle exec ruby exe/floe --workflow examples/workflow.asl --inputs='{"foo": 1}' --docker-runner kubernetes --docker-runner-options namespace=default server=https://k8s.example.com:6443 token=my-token
37
37
  ```
38
38
 
39
+ If your workflow has `Credentials` you can provide a payload that will help resolve those credentials references at runtime.
40
+
41
+ For example if your workflow had the following Credentials field with a JSON Path property:
42
+ ```json
43
+ "Credentials": {
44
+ "RoleArn.$": "$.roleArn"
45
+ }
46
+ ```
47
+
48
+ You can provide that at runtime via the `--credentials` parameter:
49
+
50
+ ```
51
+ bundle exec ruby exe/floe --workflow my-workflow.asl --credentials='{"roleArn": "arn:aws:iam::111122223333:role/LambdaRole"}'
52
+ ```
53
+
39
54
  ### Ruby Library
40
55
 
41
56
  ```ruby
@@ -59,6 +74,31 @@ workflow.run!
59
74
 
60
75
  ### Docker Runner Options
61
76
 
77
+ #### Docker
78
+
79
+ Options supported by the Docker docker runner are:
80
+
81
+ * `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`.
82
+
83
+ #### Podman
84
+
85
+ Options supported by the podman docker runner are:
86
+
87
+ * `identity=string` - path to SSH identity file, (CONTAINER_SSHKEY)
88
+ * `log-level=string` - Log messages above specified level (trace, debug, info, warn, warning, error, fatal, panic)
89
+ * `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`.
90
+ * `noout=boolean` - do not output to stdout
91
+ * `root=string` - Path to the root directory in which data, including images, is stored
92
+ * `runroot=string` - Path to the 'run directory' where all state information is stored
93
+ * `runtime=string` - Path to the OCI-compatible binary used to run containers
94
+ * `runtime-flag=stringArray` - add global flags for the container runtime
95
+ * `storage-driver=string` - Select which storage driver is used to manage storage of images and containers
96
+ * `storage-opt=stringArray` - Used to pass an option to the storage driver
97
+ * `syslog=boolean` - Output logging information to syslog as well as the console
98
+ * `tmpdir=string` - Path to the tmp directory for libpod state content
99
+ * `transient-store=boolean` - Enable transient container storage
100
+ * `volumepath=string` - Path to the volume directory in which volume data is stored
101
+
62
102
  #### Kubernetes
63
103
 
64
104
  Options supported by the kubernetes docker runner are:
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.2.3"
4
+ VERSION = "0.3.1"
5
5
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Floe
4
+ class Workflow
5
+ class ChoiceRule
6
+ class And < Floe::Workflow::ChoiceRule
7
+ def true?(context, input)
8
+ children.all? { |choice| choice.true?(context, input) }
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Floe
4
+ class Workflow
5
+ class ChoiceRule
6
+ class Not < Floe::Workflow::ChoiceRule
7
+ def true?(context, input)
8
+ choice = children.first
9
+ !choice.true?(context, input)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Floe
4
+ class Workflow
5
+ class ChoiceRule
6
+ class Or < Floe::Workflow::ChoiceRule
7
+ def true?(context, input)
8
+ children.any? { |choice| choice.true?(context, input) }
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -4,24 +4,28 @@ module Floe
4
4
  class Workflow
5
5
  class ChoiceRule
6
6
  class << self
7
- def true?(payload, context, input)
8
- build(payload).true?(context, input)
9
- end
10
-
11
7
  def build(payload)
12
- data_expression = (payload.keys & %w[And Not Or]).empty?
13
- if data_expression
14
- Floe::Workflow::ChoiceRule::Data.new(payload)
8
+ if (sub_payloads = payload["Not"])
9
+ Floe::Workflow::ChoiceRule::Not.new(payload, build_children([sub_payloads]))
10
+ elsif (sub_payloads = payload["And"])
11
+ Floe::Workflow::ChoiceRule::And.new(payload, build_children(sub_payloads))
12
+ elsif (sub_payloads = payload["Or"])
13
+ Floe::Workflow::ChoiceRule::Or.new(payload, build_children(sub_payloads))
15
14
  else
16
- Floe::Workflow::ChoiceRule::Boolean.new(payload)
15
+ Floe::Workflow::ChoiceRule::Data.new(payload)
17
16
  end
18
17
  end
18
+
19
+ def build_children(sub_payloads)
20
+ sub_payloads.map { |payload| build(payload) }
21
+ end
19
22
  end
20
23
 
21
- attr_reader :next, :payload, :variable
24
+ attr_reader :next, :payload, :variable, :children
22
25
 
23
- def initialize(payload)
24
- @payload = payload
26
+ def initialize(payload, children = nil)
27
+ @payload = payload
28
+ @children = children
25
29
 
26
30
  @next = payload["Next"]
27
31
  @variable = payload["Variable"]
@@ -34,7 +38,7 @@ module Floe
34
38
  private
35
39
 
36
40
  def variable_value(context, input)
37
- @variable_value ||= Path.value(variable, context, input)
41
+ Path.value(variable, context, input)
38
42
  end
39
43
  end
40
44
  end
@@ -11,7 +11,7 @@ module Floe
11
11
  "Input" => input
12
12
  },
13
13
  "State" => {},
14
- "States" => [],
14
+ "StateHistory" => [],
15
15
  "StateMachine" => {},
16
16
  "Task" => {}
17
17
  }
@@ -29,8 +29,8 @@ module Floe
29
29
  @context["State"] = val
30
30
  end
31
31
 
32
- def states
33
- @context["States"]
32
+ def state_history
33
+ @context["StateHistory"]
34
34
  end
35
35
 
36
36
  def state_machine
@@ -4,34 +4,76 @@ module Floe
4
4
  class Workflow
5
5
  class PayloadTemplate
6
6
  def initialize(payload)
7
- @payload = payload
7
+ @payload_template = parse_payload(payload)
8
8
  end
9
9
 
10
10
  def value(context, inputs = {})
11
- interpolate_value_nested(payload, context, inputs)
11
+ interpolate_value(payload_template, context, inputs)
12
12
  end
13
13
 
14
14
  private
15
15
 
16
- attr_reader :payload
16
+ attr_reader :payload_template
17
17
 
18
- def interpolate_value_nested(value, context, inputs)
18
+ def parse_payload(value)
19
19
  case value
20
- when Array
21
- value.map { |val| interpolate_value_nested(val, context, inputs) }
22
- when Hash
23
- value.to_h do |key, val|
24
- val = interpolate_value_nested(val, context, inputs)
25
- key = key.gsub(/\.\$$/, "") if key.end_with?(".$")
20
+ when Array then parse_payload_array(value)
21
+ when Hash then parse_payload_hash(value)
22
+ when String then parse_payload_string(value)
23
+ else
24
+ value
25
+ end
26
+ end
27
+
28
+ def parse_payload_array(value)
29
+ value.map { |val| parse_payload(val) }
30
+ end
26
31
 
32
+ def parse_payload_hash(value)
33
+ value.to_h do |key, val|
34
+ if key.end_with?(".$")
35
+ check_key_conflicts(key, value)
36
+
37
+ [key, parse_payload(val)]
38
+ else
27
39
  [key, val]
28
40
  end
29
- when String
30
- value.start_with?("$") ? Path.value(value, context, inputs) : value
41
+ end
42
+ end
43
+
44
+ def parse_payload_string(value)
45
+ value.start_with?("$") ? Path.new(value) : value
46
+ end
47
+
48
+ def interpolate_value(value, context, inputs)
49
+ case value
50
+ when Array then interpolate_value_array(value, context, inputs)
51
+ when Hash then interpolate_value_hash(value, context, inputs)
52
+ when Path then value.value(context, inputs)
31
53
  else
32
54
  value
33
55
  end
34
56
  end
57
+
58
+ def interpolate_value_array(value, context, inputs)
59
+ value.map { |val| interpolate_value(val, context, inputs) }
60
+ end
61
+
62
+ def interpolate_value_hash(value, context, inputs)
63
+ value.to_h do |key, val|
64
+ if key.end_with?(".$")
65
+ [key.chomp(".$"), interpolate_value(val, context, inputs)]
66
+ else
67
+ [key, val]
68
+ end
69
+ end
70
+ end
71
+
72
+ def check_key_conflicts(key, value)
73
+ if value.key?(key.chomp(".$"))
74
+ raise Floe::InvalidWorkflowError, "both #{key} and #{key.chomp(".$")} present"
75
+ end
76
+ end
35
77
  end
36
78
  end
37
79
  end
@@ -4,11 +4,13 @@ module Floe
4
4
  class Workflow
5
5
  class Runner
6
6
  class Docker < Floe::Workflow::Runner
7
- def initialize(*)
7
+ def initialize(options = {})
8
8
  require "awesome_spawn"
9
9
  require "tempfile"
10
10
 
11
11
  super
12
+
13
+ @network = options.fetch("network", "bridge")
12
14
  end
13
15
 
14
16
  def run!(resource, env = {}, secrets = {})
@@ -16,7 +18,8 @@ module Floe
16
18
 
17
19
  image = resource.sub("docker://", "")
18
20
 
19
- params = ["run", :rm, [:net, "host"]]
21
+ params = ["run", :rm]
22
+ params += [[:net, "host"]] if network == "host"
20
23
  params += env.map { |k, v| [:e, "#{k}=#{v}"] } if env
21
24
 
22
25
  secrets_file = nil
@@ -26,7 +29,7 @@ module Floe
26
29
  secrets_file.write(secrets.to_json)
27
30
  secrets_file.flush
28
31
 
29
- params << [:e, "SECRETS=/run/secrets"]
32
+ params << [:e, "_CREDENTIALS=/run/secrets"]
30
33
  params << [:v, "#{secrets_file.path}:/run/secrets:z"]
31
34
  end
32
35
 
@@ -39,6 +42,10 @@ module Floe
39
42
  ensure
40
43
  secrets_file&.close!
41
44
  end
45
+
46
+ private
47
+
48
+ attr_reader :network
42
49
  end
43
50
  end
44
51
  end
@@ -47,27 +47,21 @@ module Floe
47
47
 
48
48
  begin
49
49
  create_pod!(name, image, env, secret)
50
- while running?(name)
51
- sleep(1)
50
+ loop do
51
+ case pod_info(name).dig("status", "phase")
52
+ when "Pending", "Running"
53
+ sleep(1)
54
+ when "Succeeded"
55
+ return [0, output(name)]
56
+ else
57
+ return [1, output(name)]
58
+ end
52
59
  end
53
-
54
- exit_status = success?(name) ? 0 : 1
55
- results = output(name)
56
-
57
- [exit_status, results]
58
60
  ensure
59
61
  cleanup(name, secret)
60
62
  end
61
63
  end
62
64
 
63
- def running?(pod_name)
64
- %w[Pending Running].include?(pod_info(pod_name).dig("status", "phase"))
65
- end
66
-
67
- def success?(pod_name)
68
- pod_info(pod_name).dig("status", "phase") == "Succeeded"
69
- end
70
-
71
65
  def output(pod)
72
66
  kubeclient.get_pod_log(pod, namespace).body
73
67
  end
@@ -125,7 +119,7 @@ module Floe
125
119
  ]
126
120
 
127
121
  spec[:spec][:containers][0][:env] << {
128
- :name => "SECRETS",
122
+ :name => "_CREDENTIALS",
129
123
  :value => "/run/secrets/#{secret}/secret"
130
124
  }
131
125
 
@@ -4,11 +4,26 @@ module Floe
4
4
  class Workflow
5
5
  class Runner
6
6
  class Podman < Floe::Workflow::Runner
7
- def initialize(*)
7
+ def initialize(options = {})
8
8
  require "awesome_spawn"
9
9
  require "securerandom"
10
10
 
11
11
  super
12
+
13
+ @identity = options["identity"]
14
+ @log_level = options["log-level"]
15
+ @network = options["network"]
16
+ @noout = options["noout"].to_s == "true" if options.key?("noout")
17
+ @root = options["root"]
18
+ @runroot = options["runroot"]
19
+ @runtime = options["runtime"]
20
+ @runtime_flag = options["runtime-flag"]
21
+ @storage_driver = options["storage-driver"]
22
+ @storage_opt = options["storage-opt"]
23
+ @syslog = options["syslog"].to_s == "true" if options.key?("syslog")
24
+ @tmpdir = options["tmpdir"]
25
+ @transient_store = !!options["transient-store"] if options.key?("transient-store")
26
+ @volumepath = options["volumepath"]
12
27
  end
13
28
 
14
29
  def run!(resource, env = {}, secrets = {})
@@ -16,26 +31,53 @@ module Floe
16
31
 
17
32
  image = resource.sub("docker://", "")
18
33
 
19
- params = ["run", :rm]
34
+ params = ["run", :rm]
35
+ params += [[:net, "host"]] if @network == "host"
20
36
  params += env.map { |k, v| [:e, "#{k}=#{v}"] } if env
21
37
 
22
38
  if secrets && !secrets.empty?
23
39
  secret_guid = SecureRandom.uuid
24
- AwesomeSpawn.run!("podman", :params => ["secret", "create", secret_guid, "-"], :in_data => secrets.to_json)
40
+ podman!("secret", "create", secret_guid, "-", :in_data => secrets.to_json)
25
41
 
26
- params << [:e, "SECRETS=/run/secrets/#{secret_guid}"]
42
+ params << [:e, "_CREDENTIALS=/run/secrets/#{secret_guid}"]
27
43
  params << [:secret, secret_guid]
28
44
  end
29
45
 
30
46
  params << image
31
47
 
32
48
  logger.debug("Running podman: #{AwesomeSpawn.build_command_line("podman", params)}")
33
- result = AwesomeSpawn.run!("podman", :params => params)
49
+ result = podman!(*params)
34
50
 
35
51
  [result.exit_status, result.output]
36
52
  ensure
37
53
  AwesomeSpawn.run("podman", :params => ["secret", "rm", secret_guid]) if secret_guid
38
54
  end
55
+
56
+ private
57
+
58
+ def podman!(*args, **kwargs)
59
+ params = podman_global_options + args
60
+
61
+ AwesomeSpawn.run!("podman", :params => params, **kwargs)
62
+ end
63
+
64
+ def podman_global_options
65
+ options = []
66
+ options << [:identity, @identity] if @identity
67
+ options << [:"log-level", @log_level] if @log_level
68
+ options << :noout if @noout
69
+ options << [:root, @root] if @root
70
+ options << [:runroot, @runroot] if @runroot
71
+ options << [:runtime, @runtime] if @runtime
72
+ options << [:"runtime-flag", @runtime_flag] if @runtime_flag
73
+ options << [:"storage-driver", @storage_driver] if @storage_driver
74
+ options << [:"storage-opt", @storage_opt] if @storage_opt
75
+ options << :syslog if @syslog
76
+ options << [:tmpdir, @tmpdir] if @tmpdir
77
+ options << [:"transient-store", @transient_store] if @transient_store
78
+ options << [:volumepath, @volumepath] if @volumepath
79
+ options
80
+ end
39
81
  end
40
82
  end
41
83
  end
@@ -25,22 +25,13 @@ module Floe
25
25
  @workflow = workflow
26
26
  @name = name
27
27
  @payload = payload
28
- @end = !!payload["End"]
29
28
  @type = payload["Type"]
30
29
  @comment = payload["Comment"]
31
30
  end
32
31
 
33
- def end?
34
- @end
35
- end
36
-
37
32
  def context
38
33
  workflow.context
39
34
  end
40
-
41
- def status
42
- end? ? "success" : "running"
43
- end
44
35
  end
45
36
  end
46
37
  end
@@ -23,6 +23,14 @@ module Floe
23
23
 
24
24
  [next_state, output]
25
25
  end
26
+
27
+ def status
28
+ "running"
29
+ end
30
+
31
+ def end?
32
+ false
33
+ end
26
34
  end
27
35
  end
28
36
  end
@@ -11,7 +11,6 @@ module Floe
11
11
 
12
12
  @cause = payload["Cause"]
13
13
  @error = payload["Error"]
14
- @end = true
15
14
  end
16
15
 
17
16
  def run!(input)
@@ -21,6 +20,10 @@ module Floe
21
20
  def status
22
21
  "errored"
23
22
  end
23
+
24
+ def end?
25
+ true
26
+ end
24
27
  end
25
28
  end
26
29
  end
@@ -10,6 +10,7 @@ module Floe
10
10
  super
11
11
 
12
12
  @next = payload["Next"]
13
+ @end = !!payload["End"]
13
14
  @result = payload["Result"]
14
15
 
15
16
  @parameters = PayloadTemplate.new(payload["Parameters"]) if payload["Parameters"]
@@ -23,7 +24,15 @@ module Floe
23
24
  output = result_path.set(output, result) if result && result_path
24
25
  output = output_path.value(context, output)
25
26
 
26
- [@next, output]
27
+ [@end ? nil : @next, output]
28
+ end
29
+
30
+ def status
31
+ @end ? "success" : "running"
32
+ end
33
+
34
+ def end?
35
+ @end
27
36
  end
28
37
  end
29
38
  end
@@ -8,13 +8,19 @@ module Floe
8
8
 
9
9
  def initialize(workflow, name, payload)
10
10
  super
11
-
12
- @end = true
13
11
  end
14
12
 
15
13
  def run!(input)
16
14
  [nil, input]
17
15
  end
16
+
17
+ def status
18
+ "success"
19
+ end
20
+
21
+ def end?
22
+ true
23
+ end
18
24
  end
19
25
  end
20
26
  end
@@ -13,6 +13,7 @@ module Floe
13
13
 
14
14
  @heartbeat_seconds = payload["HeartbeatSeconds"]
15
15
  @next = payload["Next"]
16
+ @end = !!payload["End"]
16
17
  @resource = payload["Resource"]
17
18
  @timeout_seconds = payload["TimeoutSeconds"]
18
19
  @retry = payload["Retry"].to_a.map { |retrier| Retrier.new(retrier) }
@@ -33,7 +34,7 @@ module Floe
33
34
  _exit_status, results = runner.run!(resource, input, credentials&.value({}, workflow.credentials))
34
35
 
35
36
  output = process_output!(input, results)
36
- [@next, output]
37
+ [@end ? nil : @next, output]
37
38
  rescue => err
38
39
  retrier = self.retry.detect { |r| (r.error_equals & [err.to_s, "States.ALL"]).any? }
39
40
  retry if retry!(retrier)
@@ -44,6 +45,14 @@ module Floe
44
45
  [catcher.next, output]
45
46
  end
46
47
 
48
+ def status
49
+ @end ? "success" : "running"
50
+ end
51
+
52
+ def end?
53
+ @end
54
+ end
55
+
47
56
  private
48
57
 
49
58
  def retry!(retrier)
@@ -10,6 +10,7 @@ module Floe
10
10
  super
11
11
 
12
12
  @next = payload["Next"]
13
+ @end = !!payload["End"]
13
14
  @seconds = payload["Seconds"].to_i
14
15
 
15
16
  @input_path = Path.new(payload.fetch("InputPath", "$"))
@@ -20,7 +21,15 @@ module Floe
20
21
  input = input_path.value(context, input)
21
22
  sleep(seconds)
22
23
  output = output_path.value(context, input)
23
- [@next, output]
24
+ [@end ? nil : @next, output]
25
+ end
26
+
27
+ def status
28
+ @end ? "success" : "running"
29
+ end
30
+
31
+ def end?
32
+ @end
24
33
  end
25
34
  end
26
35
  end
data/lib/floe/workflow.rb CHANGED
@@ -55,12 +55,12 @@ module Floe
55
55
  tock = Process.clock_gettime(Process::CLOCK_MONOTONIC)
56
56
 
57
57
  context.state["FinishedTime"] = Time.now.utc
58
- context.state["Duration"] = (tock - tick) / 1_000_000.0
58
+ context.state["Duration"] = tock - tick
59
59
  context.state["Output"] = output
60
60
 
61
61
  logger.info("Running state: [#{current_state.name}] with input [#{context["Input"]}]...Complete - next state: [#{next_state}] output: [#{output}]")
62
62
 
63
- context.states << context.state
63
+ context.state_history << context.state
64
64
 
65
65
  @status = current_state.status
66
66
  @current_state = next_state && @states_by_name[next_state]
data/lib/floe.rb CHANGED
@@ -8,7 +8,9 @@ require_relative "floe/logging"
8
8
  require_relative "floe/workflow"
9
9
  require_relative "floe/workflow/catcher"
10
10
  require_relative "floe/workflow/choice_rule"
11
- require_relative "floe/workflow/choice_rule/boolean"
11
+ require_relative "floe/workflow/choice_rule/not"
12
+ require_relative "floe/workflow/choice_rule/or"
13
+ require_relative "floe/workflow/choice_rule/and"
12
14
  require_relative "floe/workflow/choice_rule/data"
13
15
  require_relative "floe/workflow/context"
14
16
  require_relative "floe/workflow/path"
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.2.3
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ManageIQ Developers
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-28 00:00:00.000000000 Z
11
+ date: 2023-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: awesome_spawn
@@ -148,8 +148,10 @@ files:
148
148
  - lib/floe/workflow.rb
149
149
  - lib/floe/workflow/catcher.rb
150
150
  - lib/floe/workflow/choice_rule.rb
151
- - lib/floe/workflow/choice_rule/boolean.rb
151
+ - lib/floe/workflow/choice_rule/and.rb
152
152
  - lib/floe/workflow/choice_rule/data.rb
153
+ - lib/floe/workflow/choice_rule/not.rb
154
+ - lib/floe/workflow/choice_rule/or.rb
153
155
  - lib/floe/workflow/context.rb
154
156
  - lib/floe/workflow/path.rb
155
157
  - lib/floe/workflow/payload_template.rb
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Floe
4
- class Workflow
5
- class ChoiceRule
6
- class Boolean < Floe::Workflow::ChoiceRule
7
- def true?(context, input)
8
- if payload.key?("Not")
9
- !ChoiceRule.true?(payload["Not"], context, input)
10
- elsif payload.key?("And")
11
- payload["And"].all? { |choice| ChoiceRule.true?(choice, context, input) }
12
- else
13
- payload["Or"].any? { |choice| ChoiceRule.true?(choice, context, input) }
14
- end
15
- end
16
- end
17
- end
18
- end
19
- end