foreman-tasks-core 0.3.0 → 0.3.1

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