foreman-tasks-core 0.3.1 → 0.3.2

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: 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