foreman-tasks-core 0.3.1 → 0.3.2

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: c46d9f3def1bf8405c45eb6a32331437dede8c68071d0c0c8a0159a2e489f007
4
- data.tar.gz: 24e606e5743d4694c607514b25bbff6492153290d9d3c99546a91b6ce75967d0
3
+ metadata.gz: 7c036a816a81a10ce427600ce920c1aa98c59bb2c317dcba1662f906941d00e0
4
+ data.tar.gz: 3e74db375442e42f0ad7e7f5887d1cd5ab79b311d86c1389f24fb5d168146a70
5
5
  SHA512:
6
- metadata.gz: 5a3c8a38e39d80a3f5f9bc1deebdcf7e4b082838e687ae78846a17448302dddff4c00c24e81235ad455133c12bfd7112d657b7ca630210ce79bddd1f4d04e13f
7
- data.tar.gz: c4777a0ca2d8d7562e3b2f0ab02293e7af5039753adcd9c7d0fabe1955726b60cf15e2c9664376410aff9e08de45054459ce9e2ac7a0d4a94ce49e02b3a61936
6
+ metadata.gz: 7c68ff530abb93733509feb1201d74c7588b1edc3e90c6caf2fd54fce11aedeb760595c2ce3fcf8f208fe5eafbc7bca00aecec08ee4b28d0b1e438c6b81938d1
7
+ data.tar.gz: f1e5baa029323687099e055874464d1710ddbcc8488207fcb64bdc5f5682ff90311c8db6fbf5ce6eb372a4d9eb821df51ac26a5e151bbc6d4551983a896e6f82
@@ -4,6 +4,11 @@
4
4
  require 'foreman_tasks_core/settings_loader'
5
5
  require 'foreman_tasks_core/otp_manager'
6
6
  require 'foreman_tasks_core/ticker'
7
+ require 'foreman_tasks_core/batch_action'
8
+ require 'foreman_tasks_core/batch_callback_action'
9
+ require 'foreman_tasks_core/batch_runner_action'
10
+ require 'foreman_tasks_core/output_collector_action'
11
+ require 'foreman_tasks_core/single_runner_batch_action'
7
12
  require 'foreman_tasks_core/task_launcher'
8
13
 
9
14
  module ForemanTasksCore
@@ -0,0 +1,21 @@
1
+ module ForemanTasksCore
2
+ class BatchAction < ::Dynflow::Action
3
+ include Dynflow::Action::WithSubPlans
4
+ include Dynflow::Action::WithPollingSubPlans
5
+
6
+ # { task_id => { :action_class => Klass, :input => input } }
7
+ def plan(launcher, input_hash)
8
+ launcher.launch_children(self, input_hash)
9
+ plan_self
10
+ end
11
+
12
+ def initiate
13
+ ping suspended_action
14
+ wait_for_sub_plans sub_plans
15
+ end
16
+
17
+ def rescue_strategy
18
+ Dynflow::Action::Rescue::Fail
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ module ForemanTasksCore
2
+ class BatchCallback < ::Dynflow::Action
3
+ def plan(input_hash, results)
4
+ plan_self :targets => input_hash, :results => results
5
+ end
6
+
7
+ def run
8
+ payload = format_payload(input['targets'], input['results'])
9
+ SmartProxyDynflowCore::Callback::Request.new.callback({ :callbacks => payload }.to_json)
10
+ end
11
+
12
+ private
13
+
14
+ def format_payload(input_hash, results)
15
+ input_hash.map do |task_id, callback|
16
+ { :callback => callback, :data => results[task_id] }
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ require 'foreman_tasks_core/runner/action'
2
+
3
+ module ForemanTasksCore
4
+ class BatchRunnerAction < ::ForemanTasksCore::Runner::Action
5
+ def plan(launcher, input)
6
+ plan_self :targets => launcher.runner_input(input), :operation => launcher.operation
7
+ end
8
+
9
+ def initiate_runner
10
+ launcher = SmartProxyDynflowCore::TaskLauncherRegistry.fetch(input[:operation])
11
+ launcher.runner_class.new(input[:targets], suspended_action: suspended_action)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,8 @@
1
+ module ForemanTasksCore
2
+ class OutputCollectorAction < ::ForemanTasksCore::Runner::Action
3
+ def init_run
4
+ output[:result] = []
5
+ suspend
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,40 @@
1
+ module ForemanTasksCore
2
+ module Runner
3
+ module Command
4
+ def initialize_command(*command)
5
+ @command_out, @command_in, @command_pid = PTY.spawn(*command)
6
+ rescue Errno::ENOENT => e
7
+ publish_exception("Error running command '#{command.join(' ')}'", e)
8
+ end
9
+
10
+ def refresh
11
+ return if @command_out.nil?
12
+ ready_outputs, * = IO.select([@command_out], nil, nil, 0.1)
13
+ if ready_outputs
14
+ if @command_out.nread > 0
15
+ lines = @command_out.read_nonblock(@command_out.nread)
16
+ else
17
+ close_io
18
+ Process.wait(@command_pid)
19
+ publish_exit_status($CHILD_STATUS.exitstatus)
20
+ end
21
+ publish_data(lines, 'stdout') if lines && !lines.empty?
22
+ end
23
+ end
24
+
25
+ def close
26
+ close_io
27
+ end
28
+
29
+ private
30
+
31
+ def close_io
32
+ @command_out.close if @command_out && !@command_out.closed?
33
+ @command_out = nil
34
+
35
+ @command_in.close if @command_in && !@command_in.closed?
36
+ @command_in = nil
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,41 +1,11 @@
1
1
  require 'io/wait'
2
2
  require 'pty'
3
+ require 'foreman_tasks_core/runner/command'
3
4
 
4
5
  module ForemanTasksCore
5
6
  module Runner
6
- class CommandRunner < Runner::Base
7
- def initialize_command(*command)
8
- @command_out, @command_in, @command_pid = PTY.spawn(*command)
9
- end
10
-
11
- def refresh
12
- return if @command_out.nil?
13
- ready_outputs, * = IO.select([@command_out], nil, nil, 0.1)
14
- if ready_outputs
15
- if @command_out.nread > 0
16
- lines = @command_out.read_nonblock(@command_out.nread)
17
- else
18
- close_io
19
- Process.wait(@command_pid)
20
- publish_exit_status($CHILD_STATUS.exitstatus)
21
- end
22
- publish_data(lines, 'stdout') if lines && !lines.empty?
23
- end
24
- end
25
-
26
- def close
27
- close_io
28
- end
29
-
30
- private
31
-
32
- def close_io
33
- @command_out.close if @command_out && !@command_out.closed?
34
- @command_out = nil
35
-
36
- @command_in.close if @command_in && !@command_in.closed?
37
- @command_in = nil
38
- end
7
+ class CommandRunner < Base
8
+ include Command
39
9
  end
40
10
  end
41
11
  end
@@ -1,10 +1,11 @@
1
1
  module ForemanTasksCore
2
2
  module Runner
3
3
  class Parent < Base
4
- # targets = { hostname => { :execution_plan_id => "...", :run_step_id => id,
4
+ # targets = { identifier => { :execution_plan_id => "...", :run_step_id => id,
5
5
  # :input => { ... } }
6
6
  def initialize(targets = {}, suspended_action: nil)
7
7
  @targets = targets
8
+ @exit_statuses = {}
8
9
  super suspended_action: suspended_action
9
10
  end
10
11
 
@@ -13,9 +14,11 @@ module ForemanTasksCore
13
14
  if value.empty? && @exit_status.nil?
14
15
  acc
15
16
  else
16
- @outputs[key] = ForemanTasksCore::ContinuousOutput.new
17
- key = host_action(key) unless key == @suspended_action
18
- acc.merge(key => Runner::Update.new(value, @exit_status))
17
+ identifier = key
18
+ @outputs[identifier] = ForemanTasksCore::ContinuousOutput.new
19
+ key = host_action(identifier) unless identifier == @suspended_action
20
+ exit_status = @exit_statuses[identifier] || @exit_status if @exit_status
21
+ acc.merge(key => Runner::Update.new(value, exit_status))
19
22
  end
20
23
  end
21
24
  end
@@ -26,9 +29,9 @@ module ForemanTasksCore
26
29
  end
27
30
  end
28
31
 
29
- def host_action(hostname)
30
- options = @targets[hostname].slice('execution_plan_id', 'run_step_id')
31
- .merge(:world => ForemanTasksCore.dynflow_world)
32
+ def host_action(identifier)
33
+ options = @targets[identifier].slice('execution_plan_id', 'run_step_id')
34
+ .merge(:world => ForemanTasksCore.dynflow_world)
32
35
  Dynflow::Action::Suspended.new OpenStruct.new(options)
33
36
  end
34
37
 
@@ -36,13 +39,21 @@ module ForemanTasksCore
36
39
  @outputs.each { |_k, output| output.add_output(data, type) }
37
40
  end
38
41
 
39
- def publish_data_for(hostname, data, type)
40
- @outputs[hostname].add_output(data, type)
42
+ def publish_data(data, type)
43
+ @outputs[@suspended_action].add_output(data, type)
44
+ end
45
+
46
+ def publish_data_for(identifier, data, type)
47
+ @outputs[identifier].add_output(data, type)
41
48
  end
42
49
 
43
50
  def dispatch_exception(context, exception)
44
51
  @outputs.values.each { |output| output.add_exception(context, exception) }
45
52
  end
53
+
54
+ def publish_exit_status_for(identifier, exit_status)
55
+ @exit_statuses[identifier] = exit_status
56
+ end
46
57
  end
47
58
  end
48
59
  end
@@ -0,0 +1,39 @@
1
+ module ForemanTasksCore
2
+ class SingleRunnerBatchAction < ForemanTasksCore::BatchAction
3
+ def plan(launcher, input_hash)
4
+ launcher.launch_children(self, input_hash)
5
+ sequence do
6
+ results = plan_self
7
+ plan_action BatchCallback, launcher.prepare_batch(input_hash), results.output[:results]
8
+ end
9
+ end
10
+
11
+ def run(event = nil)
12
+ super unless event == Dynflow::Action::Skip
13
+ end
14
+
15
+ def initiate
16
+ ping suspended_action
17
+ wait_for_sub_plans sub_plans
18
+ end
19
+
20
+ def check_for_errors!(optional = true)
21
+ super unless optional
22
+ end
23
+
24
+ def on_finish
25
+ output[:results] = sub_plans.map(&:entry_action).reduce({}) do |acc, cur|
26
+ acc.merge(cur.execution_plan_id => cur.output)
27
+ end
28
+ end
29
+
30
+ def finalize
31
+ output.delete(:results)
32
+ check_for_errors!
33
+ end
34
+
35
+ def rescue_strategy_for_self
36
+ Dynflow::Action::Rescue::Skip
37
+ end
38
+ end
39
+ end
@@ -6,3 +6,4 @@ end
6
6
  require 'foreman_tasks_core/task_launcher/abstract'
7
7
  require 'foreman_tasks_core/task_launcher/single'
8
8
  require 'foreman_tasks_core/task_launcher/batch'
9
+ require 'foreman_tasks_core/task_launcher/group'
@@ -13,6 +13,8 @@ module ForemanTasksCore
13
13
  raise NotImplementedError
14
14
  end
15
15
 
16
+ def self.input_format; end
17
+
16
18
  private
17
19
 
18
20
  def format_result(result)
@@ -25,7 +27,7 @@ module ForemanTasksCore
25
27
  end
26
28
 
27
29
  def action_class(input)
28
- ::Dynflow::Utils.constantize(input['action_class'])
30
+ options[:action_class_override] || ::Dynflow::Utils.constantize(input['action_class'])
29
31
  end
30
32
 
31
33
  def with_callback(input)
@@ -1,37 +1,37 @@
1
1
  module ForemanTasksCore
2
2
  module TaskLauncher
3
- class ParentAction < ::Dynflow::Action
4
- include Dynflow::Action::WithSubPlans
5
- include Dynflow::Action::WithPollingSubPlans
6
-
7
- # { task_id => { :action_class => Klass, :input => input } }
8
- def plan(launcher, input_hash)
9
- launcher.launch_children(self, input_hash)
10
- plan_self
11
- end
12
-
13
- def initiate
14
- ping suspended_action
15
- wait_for_sub_plans sub_plans
16
- end
17
-
18
- def rescue_strategy
19
- Dynflow::Action::Rescue::Fail
20
- end
21
- end
22
-
23
3
  class Batch < Abstract
24
4
  def launch!(input)
25
- trigger(nil, ParentAction, self, input)
5
+ trigger(nil, BatchAction, self, input)
26
6
  end
27
7
 
28
8
  def launch_children(parent, input_hash)
29
9
  input_hash.each do |task_id, input|
30
- launcher = Single.new(world, callback, :parent => parent)
31
- launcher.launch!(input)
10
+ launcher = child_launcher(parent)
11
+ launcher.launch!(transform_input(input))
32
12
  results[task_id] = launcher.results
33
13
  end
34
14
  end
15
+
16
+ def prepare_batch(input_hash)
17
+ success_tasks = input_hash.select do |task_id, _input|
18
+ results[task_id][:result] == 'success'
19
+ end
20
+ success_tasks.reduce({}) do |acc, (key, value)|
21
+ acc.merge(results[key][:task_id] => value['action_input']['callback'])
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def child_launcher(parent)
28
+ Single.new(world, callback, :parent => parent)
29
+ end
30
+
31
+ # Identity by default
32
+ def transform_input(input)
33
+ input
34
+ end
35
35
  end
36
36
  end
37
37
  end
@@ -0,0 +1,47 @@
1
+ require 'foreman_tasks_core/runner'
2
+
3
+ module ForemanTasksCore
4
+ module TaskLauncher
5
+ class AbstractGroup < Batch
6
+ def self.runner_class
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def launch!(input)
11
+ trigger(nil, SingleRunnerBatchAction, self, input)
12
+ end
13
+
14
+ def launch_children(parent, input_hash)
15
+ super(parent, input_hash)
16
+ trigger(parent, BatchRunnerAction, self, input_hash)
17
+ end
18
+
19
+ def operation
20
+ raise NotImplementedError
21
+ end
22
+
23
+ def runner_input(input)
24
+ input.reduce({}) do |acc, (id, input)|
25
+ input = { :execution_plan_id => results[id][:task_id],
26
+ :run_step_id => 2,
27
+ :input => input }
28
+ acc.merge(id => input)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def child_launcher(parent)
35
+ Single.new(world, callback, :parent => parent, :action_class_override => OutputCollectorAction)
36
+ end
37
+
38
+ def transform_input(input)
39
+ wipe_callback(input)
40
+ end
41
+
42
+ def wipe_callback(input)
43
+ input.merge('action_input' => input['action_input'].merge('callback' => nil))
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,7 +1,10 @@
1
1
  module ForemanTasksCore
2
2
  module TaskLauncher
3
3
  class Single < Abstract
4
- # { :action_class => "MyActionClass", :action_input => {} }
4
+ def self.input_format
5
+ { :action_class => "MyActionClass", :action_input => {} }
6
+ end
7
+
5
8
  def launch!(input)
6
9
  triggered = trigger(options[:parent],
7
10
  action_class(input),
@@ -1,3 +1,3 @@
1
1
  module ForemanTasksCore
2
- VERSION = '0.3.1'.freeze
2
+ VERSION = '0.3.2'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman-tasks-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Nečas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-03-15 00:00:00.000000000 Z
11
+ date: 2019-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dynflow
@@ -35,20 +35,27 @@ extra_rdoc_files: []
35
35
  files:
36
36
  - LICENSE
37
37
  - lib/foreman_tasks_core.rb
38
+ - lib/foreman_tasks_core/batch_action.rb
39
+ - lib/foreman_tasks_core/batch_callback_action.rb
40
+ - lib/foreman_tasks_core/batch_runner_action.rb
38
41
  - lib/foreman_tasks_core/continuous_output.rb
39
42
  - lib/foreman_tasks_core/otp_manager.rb
43
+ - lib/foreman_tasks_core/output_collector_action.rb
40
44
  - lib/foreman_tasks_core/runner.rb
41
45
  - lib/foreman_tasks_core/runner/action.rb
42
46
  - lib/foreman_tasks_core/runner/base.rb
47
+ - lib/foreman_tasks_core/runner/command.rb
43
48
  - lib/foreman_tasks_core/runner/command_runner.rb
44
49
  - lib/foreman_tasks_core/runner/dispatcher.rb
45
50
  - lib/foreman_tasks_core/runner/parent.rb
46
51
  - lib/foreman_tasks_core/runner/update.rb
47
52
  - lib/foreman_tasks_core/settings_loader.rb
48
53
  - lib/foreman_tasks_core/shareable_action.rb
54
+ - lib/foreman_tasks_core/single_runner_batch_action.rb
49
55
  - lib/foreman_tasks_core/task_launcher.rb
50
56
  - lib/foreman_tasks_core/task_launcher/abstract.rb
51
57
  - lib/foreman_tasks_core/task_launcher/batch.rb
58
+ - lib/foreman_tasks_core/task_launcher/group.rb
52
59
  - lib/foreman_tasks_core/task_launcher/single.rb
53
60
  - lib/foreman_tasks_core/ticker.rb
54
61
  - lib/foreman_tasks_core/version.rb