foreman-tasks-core 0.3.0 → 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: cf0691634b81dc717a0678d8dbbf78da4489fe4a4ad8e141815c0715de637192
4
- data.tar.gz: 49f3a9a18cd5cd1bee5405b9272ef9cba77476bc57973ac1dc87e65a98eebf4e
3
+ metadata.gz: c46d9f3def1bf8405c45eb6a32331437dede8c68071d0c0c8a0159a2e489f007
4
+ data.tar.gz: 24e606e5743d4694c607514b25bbff6492153290d9d3c99546a91b6ce75967d0
5
5
  SHA512:
6
- metadata.gz: 519b03fc48675250a25bb0c75d49fa6f335c1e8c904acc7fe57d7f5b6b143d25cf3b6effd7ae4554fd69796f9648eb13ef1e9e853366a22c17bbb8dae75e4978
7
- data.tar.gz: f7009108c1a9c5e515e37213e435e91cb35a704d72c650576a1331d6395cacf2c7fc2d8cedda1363de33384c59b3dbe55f38d871143e8c7b405bed00d1a76ec4
6
+ metadata.gz: 5a3c8a38e39d80a3f5f9bc1deebdcf7e4b082838e687ae78846a17448302dddff4c00c24e81235ad455133c12bfd7112d657b7ca630210ce79bddd1f4d04e13f
7
+ data.tar.gz: c4777a0ca2d8d7562e3b2f0ab02293e7af5039753adcd9c7d0fabe1955726b60cf15e2c9664376410aff9e08de45054459ce9e2ac7a0d4a94ce49e02b3a61936
@@ -4,6 +4,7 @@
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/task_launcher'
7
8
 
8
9
  module ForemanTasksCore
9
10
  def self.dynflow_world
@@ -7,3 +7,4 @@ require 'foreman_tasks_core/runner/update'
7
7
  require 'foreman_tasks_core/runner/base'
8
8
  require 'foreman_tasks_core/runner/dispatcher'
9
9
  require 'foreman_tasks_core/runner/action'
10
+ require 'foreman_tasks_core/runner/parent'
@@ -6,9 +6,10 @@ module ForemanTasksCore
6
6
  attr_reader :id
7
7
  attr_writer :logger
8
8
 
9
- def initialize(*_args)
9
+ def initialize(*_args, suspended_action: nil)
10
+ @suspended_action = suspended_action
10
11
  @id = SecureRandom.uuid
11
- @continuous_output = ::ForemanTasksCore::ContinuousOutput.new
12
+ initialize_continuous_outputs
12
13
  end
13
14
 
14
15
  def logger
@@ -18,11 +19,7 @@ module ForemanTasksCore
18
19
  def run_refresh
19
20
  logger.debug('refreshing runner')
20
21
  refresh
21
- new_data = @continuous_output
22
- @continuous_output = ForemanTasksCore::ContinuousOutput.new
23
- if !new_data.empty? || @exit_status
24
- return Runner::Update.new(new_data, @exit_status)
25
- end
22
+ generate_updates
26
23
  end
27
24
 
28
25
  def start
@@ -59,13 +56,28 @@ module ForemanTasksCore
59
56
  def publish_exception(context, exception, fatal = true)
60
57
  logger.error("#{context} - #{exception.class} #{exception.message}:\n" + \
61
58
  exception.backtrace.join("\n"))
62
- @continuous_output.add_exception(context, exception)
59
+ dispatch_exception context, exception
63
60
  publish_exit_status('EXCEPTION') if fatal
64
61
  end
65
62
 
66
63
  def publish_exit_status(status)
67
64
  @exit_status = status
68
65
  end
66
+
67
+ def dispatch_exception(context, exception)
68
+ @continuous_output.add_exception(context, exception)
69
+ end
70
+
71
+ def generate_updates
72
+ return {} if @continuous_output.empty? && @exit_status.nil?
73
+ new_data = @continuous_output
74
+ @continuous_output = ForemanTasksCore::ContinuousOutput.new
75
+ { @suspended_action => Runner::Update.new(new_data, @exit_status) }
76
+ end
77
+
78
+ def initialize_continuous_outputs
79
+ @continuous_output = ::ForemanTasksCore::ContinuousOutput.new
80
+ end
69
81
  end
70
82
  end
71
83
  end
@@ -37,10 +37,14 @@ module ForemanTasksCore
37
37
 
38
38
  def refresh_runner
39
39
  @logger.debug("refresh runner #{@runner.id}")
40
- if (update = @runner.run_refresh)
41
- @suspended_action << update
42
- finish if update.exit_status
43
- end
40
+ updates = @runner.run_refresh
41
+
42
+ updates.each { |receiver, update| (receiver || @suspended_action) << update }
43
+
44
+ # This is a workaround when the runner does not accept the suspended action
45
+ main_key = updates.keys.any?(&:nil?) ? nil : @suspended_action
46
+ main_process = updates[main_key]
47
+ finish if main_process && main_process.exit_status
44
48
  ensure
45
49
  @refresh_planned = false
46
50
  plan_next_refresh
@@ -0,0 +1,48 @@
1
+ module ForemanTasksCore
2
+ module Runner
3
+ class Parent < Base
4
+ # targets = { hostname => { :execution_plan_id => "...", :run_step_id => id,
5
+ # :input => { ... } }
6
+ def initialize(targets = {}, suspended_action: nil)
7
+ @targets = targets
8
+ super suspended_action: suspended_action
9
+ end
10
+
11
+ def generate_updates
12
+ @outputs.reduce({}) do |acc, (key, value)|
13
+ if value.empty? && @exit_status.nil?
14
+ acc
15
+ 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))
19
+ end
20
+ end
21
+ end
22
+
23
+ def initialize_continuous_outputs
24
+ @outputs = ([@suspended_action] + @targets.keys).reduce({}) do |acc, target|
25
+ acc.merge(target => ForemanTasksCore::ContinuousOutput.new)
26
+ end
27
+ end
28
+
29
+ def host_action(hostname)
30
+ options = @targets[hostname].slice('execution_plan_id', 'run_step_id')
31
+ .merge(:world => ForemanTasksCore.dynflow_world)
32
+ Dynflow::Action::Suspended.new OpenStruct.new(options)
33
+ end
34
+
35
+ def broadcast_data(data, type)
36
+ @outputs.each { |_k, output| output.add_output(data, type) }
37
+ end
38
+
39
+ def publish_data_for(hostname, data, type)
40
+ @outputs[hostname].add_output(data, type)
41
+ end
42
+
43
+ def dispatch_exception(context, exception)
44
+ @outputs.values.each { |output| output.add_exception(context, exception) }
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,8 @@
1
+ module ForemanTasksCore
2
+ module TaskLauncher
3
+ end
4
+ end
5
+
6
+ require 'foreman_tasks_core/task_launcher/abstract'
7
+ require 'foreman_tasks_core/task_launcher/single'
8
+ require 'foreman_tasks_core/task_launcher/batch'
@@ -0,0 +1,42 @@
1
+ module ForemanTasksCore
2
+ module TaskLauncher
3
+ class Abstract
4
+ attr_reader :callback, :options, :results, :world
5
+ def initialize(world, callback, options = {})
6
+ @world = world
7
+ @callback = callback
8
+ @options = options
9
+ @results = {}
10
+ end
11
+
12
+ def launch!(_input)
13
+ raise NotImplementedError
14
+ end
15
+
16
+ private
17
+
18
+ def format_result(result)
19
+ if result.triggered?
20
+ { :result => 'success', :task_id => result.execution_plan_id }
21
+ else
22
+ plan = world.persistence.load_execution_plan(result.id)
23
+ { :result => 'error', :errors => plan.errors }
24
+ end
25
+ end
26
+
27
+ def action_class(input)
28
+ ::Dynflow::Utils.constantize(input['action_class'])
29
+ end
30
+
31
+ def with_callback(input)
32
+ input.merge(:callback_host => callback)
33
+ end
34
+
35
+ def trigger(parent, klass, *input)
36
+ world.trigger do
37
+ world.plan_with_options(caller_action: parent, action_class: klass, args: input)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,37 @@
1
+ module ForemanTasksCore
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
+ class Batch < Abstract
24
+ def launch!(input)
25
+ trigger(nil, ParentAction, self, input)
26
+ end
27
+
28
+ def launch_children(parent, input_hash)
29
+ input_hash.each do |task_id, input|
30
+ launcher = Single.new(world, callback, :parent => parent)
31
+ launcher.launch!(input)
32
+ results[task_id] = launcher.results
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,14 @@
1
+ module ForemanTasksCore
2
+ module TaskLauncher
3
+ class Single < Abstract
4
+ # { :action_class => "MyActionClass", :action_input => {} }
5
+ def launch!(input)
6
+ triggered = trigger(options[:parent],
7
+ action_class(input),
8
+ with_callback(input.fetch('action_input', {})))
9
+ @results = format_result(triggered)
10
+ triggered
11
+ end
12
+ end
13
+ end
14
+ end
@@ -1,3 +1,3 @@
1
1
  module ForemanTasksCore
2
- VERSION = '0.3.0'.freeze
2
+ VERSION = '0.3.1'.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.0
4
+ version: 0.3.1
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-02-08 00:00:00.000000000 Z
11
+ date: 2019-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dynflow
@@ -42,9 +42,14 @@ files:
42
42
  - lib/foreman_tasks_core/runner/base.rb
43
43
  - lib/foreman_tasks_core/runner/command_runner.rb
44
44
  - lib/foreman_tasks_core/runner/dispatcher.rb
45
+ - lib/foreman_tasks_core/runner/parent.rb
45
46
  - lib/foreman_tasks_core/runner/update.rb
46
47
  - lib/foreman_tasks_core/settings_loader.rb
47
48
  - lib/foreman_tasks_core/shareable_action.rb
49
+ - lib/foreman_tasks_core/task_launcher.rb
50
+ - lib/foreman_tasks_core/task_launcher/abstract.rb
51
+ - lib/foreman_tasks_core/task_launcher/batch.rb
52
+ - lib/foreman_tasks_core/task_launcher/single.rb
48
53
  - lib/foreman_tasks_core/ticker.rb
49
54
  - lib/foreman_tasks_core/version.rb
50
55
  homepage: https://github.com/theforeman/foreman-tasks