foreman-tasks-core 0.3.0 → 0.3.5

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: 9eeba72428b030c0bcec9f51c7b58cbfb7e046eb7279b68b6030b46aaa72a434
4
+ data.tar.gz: 9a40808da08504914c856d9407a7d454a5c5045125a768ee5d6b54424444bd93
5
5
  SHA512:
6
- metadata.gz: 519b03fc48675250a25bb0c75d49fa6f335c1e8c904acc7fe57d7f5b6b143d25cf3b6effd7ae4554fd69796f9648eb13ef1e9e853366a22c17bbb8dae75e4978
7
- data.tar.gz: f7009108c1a9c5e515e37213e435e91cb35a704d72c650576a1331d6395cacf2c7fc2d8cedda1363de33384c59b3dbe55f38d871143e8c7b405bed00d1a76ec4
6
+ metadata.gz: c9cd9a1e5c8f247bd5b3f335998d2cba76d2a17ba6edfae529713f6eaaa60de6133d61125e76175544a8248f259f48bfa715de46cd4f1ff5132844d1c5c0250d
7
+ data.tar.gz: 2ddb3a83487315cdbc6ecf32886d67f599badb47f61f02019122fed0103602af72f0c8e5f999016dfec36a3caa56c17202fb3ce0426f5814084d5703bd702991
@@ -4,6 +4,12 @@
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'
12
+ require 'foreman_tasks_core/task_launcher'
7
13
 
8
14
  module ForemanTasksCore
9
15
  def self.dynflow_world
@@ -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
@@ -17,10 +17,15 @@ module ForemanTasksCore
17
17
  @password ||= {}
18
18
  end
19
19
 
20
- def authenticate(hash)
20
+ def authenticate(hash, expected_user: nil, clear: true)
21
21
  plain = Base64.decode64(hash)
22
22
  username, otp = plain.split(':', 2)
23
- drop_otp(username, otp)
23
+ if expected_user
24
+ return false unless expected_user == username
25
+ end
26
+ password_matches = passwords[username] == otp
27
+ passwords.delete(username) if clear && password_matches
28
+ password_matches
24
29
  end
25
30
 
26
31
  def tokenize(username, password)
@@ -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
@@ -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,14 @@ 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
23
+ end
24
+
25
+ # by default, external event just causes the refresh to be triggered: this allows the descendants
26
+ # of the Base to add custom logic to process the external events.
27
+ # Similarly as `run_refresh`, it's expected to return updates to be dispatched.
28
+ def external_event(_event)
29
+ run_refresh
26
30
  end
27
31
 
28
32
  def start
@@ -59,13 +63,36 @@ module ForemanTasksCore
59
63
  def publish_exception(context, exception, fatal = true)
60
64
  logger.error("#{context} - #{exception.class} #{exception.message}:\n" + \
61
65
  exception.backtrace.join("\n"))
62
- @continuous_output.add_exception(context, exception)
66
+ dispatch_exception context, exception
63
67
  publish_exit_status('EXCEPTION') if fatal
64
68
  end
65
69
 
66
70
  def publish_exit_status(status)
67
71
  @exit_status = status
68
72
  end
73
+
74
+ def dispatch_exception(context, exception)
75
+ @continuous_output.add_exception(context, exception)
76
+ end
77
+
78
+ def generate_updates
79
+ return no_update if @continuous_output.empty? && @exit_status.nil?
80
+ new_data = @continuous_output
81
+ @continuous_output = ForemanTasksCore::ContinuousOutput.new
82
+ new_update(new_data, @exit_status)
83
+ end
84
+
85
+ def no_update
86
+ {}
87
+ end
88
+
89
+ def new_update(data, exit_status)
90
+ { @suspended_action => Runner::Update.new(data, exit_status) }
91
+ end
92
+
93
+ def initialize_continuous_outputs
94
+ @continuous_output = ::ForemanTasksCore::ContinuousOutput.new
95
+ end
69
96
  end
70
97
  end
71
98
  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
@@ -37,15 +37,21 @@ 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
+ dispatch_updates(@runner.run_refresh)
44
41
  ensure
45
42
  @refresh_planned = false
46
43
  plan_next_refresh
47
44
  end
48
45
 
46
+ def dispatch_updates(updates)
47
+ updates.each { |receiver, update| (receiver || @suspended_action) << update }
48
+
49
+ # This is a workaround when the runner does not accept the suspended action
50
+ main_key = updates.keys.any?(&:nil?) ? nil : @suspended_action
51
+ main_process = updates[main_key]
52
+ finish if main_process && main_process.exit_status
53
+ end
54
+
49
55
  def timeout_runner
50
56
  @logger.debug("timeout runner #{@runner.id}")
51
57
  @runner.timeout
@@ -73,8 +79,8 @@ module ForemanTasksCore
73
79
  finish_termination
74
80
  end
75
81
 
76
- def external_event(_event)
77
- refresh_runner
82
+ def external_event(event)
83
+ dispatch_updates(@runner.external_event(event))
78
84
  end
79
85
 
80
86
  private
@@ -114,39 +120,33 @@ module ForemanTasksCore
114
120
 
115
121
  def start(suspended_action, runner)
116
122
  synchronize do
117
- begin
118
- raise "Actor with runner id #{runner.id} already exists" if @runner_actors[runner.id]
119
- runner.logger = @logger
120
- runner_actor = RunnerActor.spawn("runner-actor-#{runner.id}", self, suspended_action, runner, @clock, @logger)
121
- @runner_actors[runner.id] = runner_actor
122
- @runner_suspended_actions[runner.id] = suspended_action
123
- runner_actor.tell(:start_runner)
124
- return runner.id
125
- rescue => exception
126
- _handle_command_exception(runner.id, exception)
127
- return nil
128
- end
123
+ raise "Actor with runner id #{runner.id} already exists" if @runner_actors[runner.id]
124
+ runner.logger = @logger
125
+ runner_actor = RunnerActor.spawn("runner-actor-#{runner.id}", self, suspended_action, runner, @clock, @logger)
126
+ @runner_actors[runner.id] = runner_actor
127
+ @runner_suspended_actions[runner.id] = suspended_action
128
+ runner_actor.tell(:start_runner)
129
+ return runner.id
130
+ rescue => exception
131
+ _handle_command_exception(runner.id, exception)
132
+ return nil
129
133
  end
130
134
  end
131
135
 
132
136
  def kill(runner_id)
133
137
  synchronize do
134
- begin
135
- runner_actor = @runner_actors[runner_id]
136
- runner_actor.tell(:kill) if runner_actor
137
- rescue => exception
138
- _handle_command_exception(runner_id, exception, false)
139
- end
138
+ runner_actor = @runner_actors[runner_id]
139
+ runner_actor.tell(:kill) if runner_actor
140
+ rescue => exception
141
+ _handle_command_exception(runner_id, exception, false)
140
142
  end
141
143
  end
142
144
 
143
145
  def finish(runner_id)
144
146
  synchronize do
145
- begin
146
- _finish(runner_id)
147
- rescue => exception
148
- _handle_command_exception(runner_id, exception, false)
149
- end
147
+ _finish(runner_id)
148
+ rescue => exception
149
+ _handle_command_exception(runner_id, exception, false)
150
150
  end
151
151
  end
152
152
 
@@ -0,0 +1,57 @@
1
+ module ForemanTasksCore
2
+ module Runner
3
+ class Parent < Base
4
+ # targets = { identifier => { :execution_plan_id => "...", :run_step_id => id,
5
+ # :input => { ... } }
6
+ def initialize(targets = {}, suspended_action: nil)
7
+ @targets = targets
8
+ @exit_statuses = {}
9
+ super suspended_action: suspended_action
10
+ end
11
+
12
+ def generate_updates
13
+ base = {}
14
+ base[@suspended_action] = Runner::Update.new(ForemanTasksCore::ContinuousOutput.new, @exit_status) if @exit_status
15
+ # Operate on all hosts if the main process ended or only on hosts for which we have updates
16
+ @outputs.reject { |_, output| @exit_status.nil? && output.empty? }
17
+ .reduce(base) do |acc, (identifier, output)|
18
+ @outputs[identifier] = ForemanTasksCore::ContinuousOutput.new # Create a new ContinuousOutput for next round of updates
19
+ exit_status = @exit_statuses[identifier] || @exit_status if @exit_status
20
+ acc.merge(host_action(identifier) => Runner::Update.new(output, exit_status))
21
+ end
22
+ end
23
+
24
+ def initialize_continuous_outputs
25
+ @outputs = @targets.keys.reduce({}) do |acc, target|
26
+ acc.merge(target => ForemanTasksCore::ContinuousOutput.new)
27
+ end
28
+ end
29
+
30
+ def host_action(identifier)
31
+ options = @targets[identifier].slice('execution_plan_id', 'run_step_id')
32
+ .merge(:world => ForemanTasksCore.dynflow_world)
33
+ Dynflow::Action::Suspended.new OpenStruct.new(options)
34
+ end
35
+
36
+ def broadcast_data(data, type)
37
+ @outputs.each { |_k, output| output.add_output(data, type) }
38
+ end
39
+
40
+ def publish_data(_data, _type)
41
+ true
42
+ end
43
+
44
+ def publish_data_for(identifier, data, type)
45
+ @outputs[identifier].add_output(data, type)
46
+ end
47
+
48
+ def dispatch_exception(context, exception)
49
+ @outputs.values.each { |output| output.add_exception(context, exception) }
50
+ end
51
+
52
+ def publish_exit_status_for(identifier, exit_status)
53
+ @exit_statuses[identifier] = exit_status
54
+ end
55
+ end
56
+ end
57
+ 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
@@ -0,0 +1,9 @@
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'
9
+ require 'foreman_tasks_core/task_launcher/group'
@@ -0,0 +1,44 @@
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
+ def self.input_format; end
17
+
18
+ private
19
+
20
+ def format_result(result)
21
+ if result.triggered?
22
+ { :result => 'success', :task_id => result.execution_plan_id }
23
+ else
24
+ plan = world.persistence.load_execution_plan(result.id)
25
+ { :result => 'error', :errors => plan.errors }
26
+ end
27
+ end
28
+
29
+ def action_class(input)
30
+ options[:action_class_override] || ::Dynflow::Utils.constantize(input['action_class'])
31
+ end
32
+
33
+ def with_callback(input)
34
+ input.merge(:callback_host => callback)
35
+ end
36
+
37
+ def trigger(parent, klass, *input)
38
+ world.trigger do
39
+ world.plan_with_options(caller_action: parent, action_class: klass, args: input)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,37 @@
1
+ module ForemanTasksCore
2
+ module TaskLauncher
3
+ class Batch < Abstract
4
+ def launch!(input)
5
+ trigger(nil, BatchAction, self, input)
6
+ end
7
+
8
+ def launch_children(parent, input_hash)
9
+ input_hash.each do |task_id, input|
10
+ launcher = child_launcher(parent)
11
+ launcher.launch!(transform_input(input))
12
+ results[task_id] = launcher.results
13
+ end
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
+ end
36
+ end
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
@@ -0,0 +1,17 @@
1
+ module ForemanTasksCore
2
+ module TaskLauncher
3
+ class Single < Abstract
4
+ def self.input_format
5
+ { :action_class => "MyActionClass", :action_input => {} }
6
+ end
7
+
8
+ def launch!(input)
9
+ triggered = trigger(options[:parent],
10
+ action_class(input),
11
+ with_callback(input.fetch('action_input', {})))
12
+ @results = format_result(triggered)
13
+ triggered
14
+ end
15
+ end
16
+ end
17
+ end
@@ -1,3 +1,5 @@
1
+ require 'dynflow'
2
+
1
3
  module ForemanTasksCore
2
4
  class Ticker < ::Dynflow::Actor
3
5
  attr_reader :clock
@@ -1,3 +1,3 @@
1
1
  module ForemanTasksCore
2
- VERSION = '0.3.0'.freeze
2
+ VERSION = '0.3.5'.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.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Nečas
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-08 00:00:00.000000000 Z
11
+ date: 2021-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dynflow
@@ -26,7 +26,7 @@ dependencies:
26
26
  version: 1.2.0
27
27
  description: 'Common code used both at Forman and Foreman proxy regarding tasks
28
28
 
29
- '
29
+ '
30
30
  email:
31
31
  - inecas@redhat.com
32
32
  executables: []
@@ -35,22 +35,35 @@ 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
50
+ - lib/foreman_tasks_core/runner/parent.rb
45
51
  - lib/foreman_tasks_core/runner/update.rb
46
52
  - lib/foreman_tasks_core/settings_loader.rb
47
53
  - lib/foreman_tasks_core/shareable_action.rb
54
+ - lib/foreman_tasks_core/single_runner_batch_action.rb
55
+ - lib/foreman_tasks_core/task_launcher.rb
56
+ - lib/foreman_tasks_core/task_launcher/abstract.rb
57
+ - lib/foreman_tasks_core/task_launcher/batch.rb
58
+ - lib/foreman_tasks_core/task_launcher/group.rb
59
+ - lib/foreman_tasks_core/task_launcher/single.rb
48
60
  - lib/foreman_tasks_core/ticker.rb
49
61
  - lib/foreman_tasks_core/version.rb
50
62
  homepage: https://github.com/theforeman/foreman-tasks
51
- licenses: []
63
+ licenses:
64
+ - GPL-3.0
52
65
  metadata: {}
53
- post_install_message:
66
+ post_install_message:
54
67
  rdoc_options: []
55
68
  require_paths:
56
69
  - lib
@@ -65,9 +78,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
65
78
  - !ruby/object:Gem::Version
66
79
  version: '0'
67
80
  requirements: []
68
- rubyforge_project:
69
- rubygems_version: 2.7.6
70
- signing_key:
81
+ rubygems_version: 3.1.2
82
+ signing_key:
71
83
  specification_version: 4
72
84
  summary: Common code used both at Forman and Foreman proxy regarding tasks
73
85
  test_files: []