foreman-tasks 0.9.1 → 0.9.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -1
- data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +1 -1
- data/app/lib/actions/bulk_action.rb +0 -1
- data/app/lib/actions/middleware/keep_current_user.rb +1 -1
- data/app/models/foreman_tasks/concerns/action_triggering.rb +7 -7
- data/app/models/foreman_tasks/task.rb +7 -5
- data/app/models/foreman_tasks/task/dynflow_task.rb +40 -12
- data/app/views/foreman_tasks/tasks/_errors.html.erb +5 -1
- data/bin/dynflow-executor +53 -28
- data/deploy/foreman-tasks.sysconfig +12 -0
- data/foreman-tasks.gemspec +2 -1
- data/lib/foreman_tasks/dynflow.rb +10 -2
- data/lib/foreman_tasks/dynflow/daemon.rb +90 -19
- data/lib/foreman_tasks/version.rb +1 -1
- data/locale/en/LC_MESSAGES/foreman_tasks.mo +0 -0
- data/locale/en/foreman_tasks.po +6 -9
- data/locale/foreman_tasks.pot +102 -107
- data/test/unit/actions/action_with_sub_plans_test.rb +1 -3
- data/test/unit/config/environment.rb +1 -0
- data/test/unit/daemon_test.rb +86 -0
- data/test/unit/task_test.rb +19 -1
- metadata +24 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ffd1f0c3a1276172b47fc40f58b34d92641fe2b1
|
4
|
+
data.tar.gz: e9faaecd202c22b32c13de8de2ba8bea5ddaee49
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5f057fff909d5f4cfc7a107f91dcba2654618dca2615fa23b819e0cbe6867f6ad7cdaae9b4de8bbbde832c9783d8c0b1d729151a9a7dacefdb42d76c15f5e597
|
7
|
+
data.tar.gz: 3f306778e7796e788f5f251bd2c4be7ef31ae94094a6b29d9ab7e1972650eae9c0c9c85590ae10d173be2b46101cb2c7d77da43cdaed09de038db478246f00b0
|
data/Gemfile
CHANGED
@@ -3,7 +3,8 @@ source "https://rubygems.org"
|
|
3
3
|
# Declare your gem's dependencies in tasks.gemspec.
|
4
4
|
# Bundler will treat runtime dependencies like base dependencies, and
|
5
5
|
# development dependencies will be added by default to the :development group.
|
6
|
-
|
6
|
+
|
7
|
+
gemspec :name => 'foreman-tasks'
|
7
8
|
|
8
9
|
# Declare any dependencies that are still in development here instead of in
|
9
10
|
# your gemspec. These might include edge Rails or gems from your path or
|
@@ -116,7 +116,7 @@ module ForemanTasks
|
|
116
116
|
tags = []
|
117
117
|
tags << text_f(f, :start_at_raw, :label => _('Start at'), :placeholder => 'YYYY-mm-dd HH:MM')
|
118
118
|
tags << text_f(f, :start_before_raw, :label => _('Start before'), :placeholder => 'YYYY-mm-dd HH:MM',
|
119
|
-
|
119
|
+
:label_help => _('Indicates that the action should be cancelled if it cannot be started before this time.'))
|
120
120
|
content_tag(:fieldset, nil, :id => 'trigger_mode_future', :class => "trigger_mode_form #{'hidden' unless triggering.future?}") do
|
121
121
|
tags.join.html_safe
|
122
122
|
end
|
@@ -108,13 +108,13 @@ module ForemanTasks
|
|
108
108
|
@_dynflow_task_wrapped = true
|
109
109
|
|
110
110
|
@_dynflow_hook_action = case method
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
111
|
+
when :save
|
112
|
+
new_record? ? create_action : update_action
|
113
|
+
when :destroy
|
114
|
+
destroy_action
|
115
|
+
else
|
116
|
+
raise 'unexpected method'
|
117
|
+
end
|
118
118
|
ensure_not_in_transaction! if @_dynflow_hook_action
|
119
119
|
yield.tap do |result|
|
120
120
|
execute_planned_action if result
|
@@ -38,10 +38,10 @@ module ForemanTasks
|
|
38
38
|
scoped_search :on => :parent_task_id, :complete_value => true
|
39
39
|
scoped_search :relation => :locks, :on => :resource_type, :complete_value => true, :rename => 'resource_type', :ext_method => :search_by_generic_resource
|
40
40
|
scoped_search :relation => :locks, :on => :resource_id, :complete_value => false, :rename => 'resource_id', :ext_method => :search_by_generic_resource
|
41
|
-
scoped_search :relation => :owners,
|
42
|
-
:on => :id,
|
43
|
-
:complete_value => true,
|
44
|
-
:rename => 'owner.id',
|
41
|
+
scoped_search :relation => :owners,
|
42
|
+
:on => :id,
|
43
|
+
:complete_value => true,
|
44
|
+
:rename => 'owner.id',
|
45
45
|
:ext_method => :search_by_owner,
|
46
46
|
:validator => ->(value) { ScopedSearch::Validators::INTEGER.call(value) || value == 'current_user' }
|
47
47
|
scoped_search :relation => :owners, :on => :login, :complete_value => true, :rename => 'owner.login', :ext_method => :search_by_owner
|
@@ -172,7 +172,9 @@ module ForemanTasks
|
|
172
172
|
end
|
173
173
|
|
174
174
|
def sub_tasks_counts
|
175
|
-
result = %w(cancelled error pending success warning).
|
175
|
+
result = %w(cancelled error pending success warning).inject({}) do |hash, state|
|
176
|
+
hash.update(state => 0)
|
177
|
+
end
|
176
178
|
result.update sub_tasks.group(:result).count
|
177
179
|
sum = result.values.reduce(:+)
|
178
180
|
if respond_to?(:main_action) && main_action.respond_to?(:total_count)
|
@@ -2,7 +2,6 @@ module ForemanTasks
|
|
2
2
|
class Task::DynflowTask < ForemanTasks::Task
|
3
3
|
include Algebrick::TypeCheck
|
4
4
|
|
5
|
-
delegate :cancellable?, :progress, to: :execution_plan
|
6
5
|
scope :for_action, ->(action_class) { where(label: action_class.name) }
|
7
6
|
|
8
7
|
def update_from_dynflow(data)
|
@@ -19,26 +18,50 @@ module ForemanTasks
|
|
19
18
|
DynflowTask.where(:external_id => main_action.caller_execution_plan_id).first!.id
|
20
19
|
end
|
21
20
|
end
|
22
|
-
self.label ||= main_action.class.name
|
21
|
+
self.label ||= main_action && main_action.class.name
|
23
22
|
changes = self.changes
|
24
23
|
save!
|
25
24
|
changes
|
26
25
|
end
|
27
26
|
|
27
|
+
def cancellable?
|
28
|
+
execution_plan.try(:cancellable?)
|
29
|
+
end
|
30
|
+
|
28
31
|
def cancel
|
29
|
-
execution_plan
|
32
|
+
execution_plan!.cancel.any?
|
30
33
|
end
|
31
34
|
|
32
35
|
def resumable?
|
33
|
-
execution_plan.state == :paused
|
36
|
+
execution_plan.try(:state) == :paused
|
34
37
|
end
|
35
38
|
|
36
39
|
def cancellable_action?(action)
|
37
40
|
action.is_a?(::Dynflow::Action::Cancellable)
|
38
41
|
end
|
39
42
|
|
40
|
-
def
|
41
|
-
|
43
|
+
def progress
|
44
|
+
execution_plan.try(:progress) || 0
|
45
|
+
end
|
46
|
+
|
47
|
+
def execution_plan(silence_exception = true)
|
48
|
+
return @execution_plan if defined?(@execution_plan)
|
49
|
+
execution_plan = ForemanTasks.dynflow.world.persistence.load_execution_plan(external_id)
|
50
|
+
# don't use invalid execution plans for our purposes
|
51
|
+
if execution_plan.respond_to?(:valid?) && !execution_plan.valid?
|
52
|
+
raise execution_plan.exception
|
53
|
+
else
|
54
|
+
@execution_plan = execution_plan
|
55
|
+
end
|
56
|
+
@execution_plan
|
57
|
+
rescue => e
|
58
|
+
Foreman::Logging.exception("Could not load execution plan #{external_id} for task #{id}", e, :logger => 'foreman-tasks')
|
59
|
+
raise e unless silence_exception
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
def execution_plan!
|
64
|
+
execution_plan(false)
|
42
65
|
end
|
43
66
|
|
44
67
|
def input
|
@@ -50,11 +73,11 @@ module ForemanTasks
|
|
50
73
|
end
|
51
74
|
|
52
75
|
def failed_steps
|
53
|
-
execution_plan.
|
76
|
+
execution_plan.try(:steps_in_state, :skipped, :skipping, :error) || []
|
54
77
|
end
|
55
78
|
|
56
79
|
def running_steps
|
57
|
-
execution_plan.
|
80
|
+
execution_plan.try(:steps_in_state, :running, :suspended) || []
|
58
81
|
end
|
59
82
|
|
60
83
|
def humanized
|
@@ -69,8 +92,8 @@ module ForemanTasks
|
|
69
92
|
end
|
70
93
|
|
71
94
|
def main_action
|
72
|
-
return @main_action if @main_action
|
73
|
-
execution_plan.root_plan_step.action execution_plan
|
95
|
+
return @main_action if defined?(@main_action)
|
96
|
+
@main_action = execution_plan && execution_plan.root_plan_step.try(:action, execution_plan)
|
74
97
|
end
|
75
98
|
|
76
99
|
def get_humanized(method)
|
@@ -79,7 +102,8 @@ module ForemanTasks
|
|
79
102
|
method = "humanized_#{method}".to_sym
|
80
103
|
end
|
81
104
|
Match! method, :humanized_name, :humanized_input, :humanized_output, :humanized_errors
|
82
|
-
return N_('N/A') if method != :humanized_name && main_action.execution_plan.state == :scheduled
|
105
|
+
return N_('N/A') if method != :humanized_name && (main_action.nil? || main_action.execution_plan.state == :scheduled)
|
106
|
+
return N_(label) if method == :humanized_name && main_action.nil?
|
83
107
|
@humanized_cache[method] ||= begin
|
84
108
|
if main_action.respond_to? method
|
85
109
|
begin
|
@@ -102,6 +126,10 @@ module ForemanTasks
|
|
102
126
|
logger.warn('Task %s updated at consistency check: %s' % [task.id, changes.inspect])
|
103
127
|
end
|
104
128
|
rescue => e
|
129
|
+
# if we fail updating the data from dynflow, it usually means there is something
|
130
|
+
# odd with the data consistency and at this point it is not possible to resume, switching
|
131
|
+
# the task to stopped/error
|
132
|
+
task.update_attributes(:state => 'stopped', :result => 'error')
|
105
133
|
Foreman::Logging.exception("Failed at consistency check for task #{task.id}", e, :logger => 'foreman-tasks')
|
106
134
|
end
|
107
135
|
end
|
@@ -126,7 +154,7 @@ module ForemanTasks
|
|
126
154
|
end
|
127
155
|
|
128
156
|
def cancelled?
|
129
|
-
execution_plan.errors.
|
157
|
+
execution_plan.errors.any? { |error| error.exception_class == ::ForemanTasks::Task::TaskCancelledException }
|
130
158
|
end
|
131
159
|
end
|
132
160
|
end
|
@@ -1,7 +1,11 @@
|
|
1
1
|
<% if @task.is_a? ForemanTasks::Task::DynflowTask %>
|
2
2
|
<div>
|
3
3
|
<% failed_steps = @task.failed_steps %>
|
4
|
-
<% if
|
4
|
+
<% if @task.execution_plan.nil? %>
|
5
|
+
<div class="alert alert-danger">
|
6
|
+
<%= _("Execution plan data not available ") %>
|
7
|
+
</div>
|
8
|
+
<% elsif failed_steps.empty? %>
|
5
9
|
<div class="alert alert-success">
|
6
10
|
<%= _("No errors") %>
|
7
11
|
</div>
|
data/bin/dynflow-executor
CHANGED
@@ -2,45 +2,70 @@
|
|
2
2
|
|
3
3
|
require 'optparse'
|
4
4
|
|
5
|
-
|
5
|
+
class ArgvParser
|
6
|
+
attr_reader :options, :command
|
6
7
|
|
7
|
-
|
8
|
-
|
8
|
+
def initialize(argv, file)
|
9
|
+
@options = { foreman_root: Dir.pwd }
|
10
|
+
|
11
|
+
opts = OptionParser.new do |opts|
|
12
|
+
opts.banner = banner(file)
|
13
|
+
|
14
|
+
opts.on('-h', '--help', 'Show this message') do
|
15
|
+
puts opts
|
16
|
+
exit 1
|
17
|
+
end
|
18
|
+
opts.on('-f', '--foreman-root=PATH', "Path to Foreman Rails root path. By default '#{@options[:foreman_root]}'") do |path|
|
19
|
+
@options[:foreman_root] = path
|
20
|
+
end
|
21
|
+
opts.on('-c', '--executors-count=COUNT', 'Number of parallel executors to spawn. Overrides EXECUTORS_COUNT environment varaible.') do |count|
|
22
|
+
@options[:executors_count] = count.to_i
|
23
|
+
end
|
24
|
+
opts.on('-m', '--memory-limit=SIZE', 'Limits the amount of memory an executor can consume. Overrides EXECUTOR_MEMORY_LIMIT environment varaible. You can use kb, mb, gb') do |size|
|
25
|
+
@options[:memory_limit] = size
|
26
|
+
end
|
27
|
+
opts.on('--executor-memory-init-delay=SECONDS', 'Start memory polling after SECONDS. Overrides EXECUTOR_MEMORY_MONITOR_DELAY environment varaible.') do |seconds|
|
28
|
+
@options[:memory_init_delay] = seconds.to_i
|
29
|
+
end
|
30
|
+
opts.on('--executor-memory-polling-interval=SECONDS', 'Check for memory useage every SECONDS sec. Overrides EXECUTOR_MEMORY_MONITOR_INTERVAL environment varaible.') do |seconds|
|
31
|
+
@options[:memory_polling_interval] = seconds.to_i
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
args = opts.parse!(argv)
|
36
|
+
@command = args.first || 'run'
|
37
|
+
end
|
38
|
+
|
39
|
+
def banner(file)
|
40
|
+
banner = <<BANNER
|
9
41
|
Run Dynflow executor for Foreman tasks.
|
10
42
|
|
11
|
-
Usage: #{File.basename(
|
43
|
+
Usage: #{File.basename(file)} [options] ACTION"
|
12
44
|
|
13
45
|
ACTION can be one of:
|
14
46
|
|
15
|
-
|
16
|
-
|
47
|
+
* start - start the executor on background. It creates these files
|
48
|
+
in tmp/pid directory:
|
17
49
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
50
|
+
* dynflow_executor_monitor.pid - pid of monitor ensuring
|
51
|
+
the executor keeps running
|
52
|
+
* dynflow_executor.pid - pid of the executor itself
|
53
|
+
* dynflow_executor.output - stdout of the executor
|
54
|
+
* stop - stops the running executor
|
55
|
+
* restart - restarts the running executor
|
56
|
+
* run - run the executor in foreground
|
25
57
|
|
26
58
|
BANNER
|
27
|
-
|
28
|
-
opts.on('-h', '--help', 'Show this message') do
|
29
|
-
puts opts
|
30
|
-
exit 1
|
31
|
-
end
|
32
|
-
opts.on('-f', '--foreman-root=PATH', "Path to Foreman Rails root path. By default '#{options[:foreman_root]}'") do |path|
|
33
|
-
options[:foreman_root] = path
|
34
|
-
end
|
35
|
-
opts.on('-c', '--executors-count=COUNT', 'Number of parallel executors to spawn. Overrides EXECUTORS_COUNT environment varaible.') do |count|
|
36
|
-
options[:executors_count] = count.to_i
|
59
|
+
banner
|
37
60
|
end
|
38
61
|
end
|
39
62
|
|
40
|
-
|
41
|
-
|
63
|
+
# run the script if it's executed explicitly
|
64
|
+
if $PROGRAM_NAME == __FILE__
|
65
|
+
parser = ArgvParser.new(ARGV, $PROGRAM_NAME)
|
42
66
|
|
43
|
-
app_file = File.expand_path('./config/application', options[:foreman_root])
|
44
|
-
require app_file
|
67
|
+
app_file = File.expand_path('./config/application', parser.options[:foreman_root])
|
68
|
+
require app_file
|
45
69
|
|
46
|
-
ForemanTasks::Dynflow::Daemon.new.run_background(command, options)
|
70
|
+
ForemanTasks::Dynflow::Daemon.new.run_background(parser.command, parser.options)
|
71
|
+
end
|
@@ -12,3 +12,15 @@ RUBY_GC_MALLOC_LIMIT_MAX=16000100
|
|
12
12
|
RUBY_GC_MALLOC_LIMIT_GROWTH_FACTOR=1.1
|
13
13
|
RUBY_GC_OLDMALLOC_LIMIT=16000100
|
14
14
|
RUBY_GC_OLDMALLOC_LIMIT_MAX=16000100
|
15
|
+
|
16
|
+
#Set the number of executors you want to run
|
17
|
+
#EXECUTORS_COUNT=1
|
18
|
+
|
19
|
+
#Set memory limit for executor process, before it's restarted automatically
|
20
|
+
#EXECUTOR_MEMORY_LIMIT=2gb
|
21
|
+
|
22
|
+
#Set delay before first memory polling to let executor initialize (in sec)
|
23
|
+
#EXECUTOR_MEMORY_MONITOR_DELAY=7200 #default: 2 hours
|
24
|
+
|
25
|
+
#Set memory polling interval, process memory will be checked every N seconds.
|
26
|
+
#EXECUTOR_MEMORY_MONITOR_INTERVAL=60
|
data/foreman-tasks.gemspec
CHANGED
@@ -29,9 +29,10 @@ DESC
|
|
29
29
|
s.extra_rdoc_files = Dir['README*', 'LICENSE']
|
30
30
|
|
31
31
|
s.add_dependency "foreman-tasks-core"
|
32
|
-
s.add_dependency "dynflow", '~> 0.8.
|
32
|
+
s.add_dependency "dynflow", '~> 0.8.24'
|
33
33
|
s.add_dependency "sequel" # for Dynflow process persistence
|
34
34
|
s.add_dependency "sinatra" # for Dynflow web console
|
35
35
|
s.add_dependency "daemons" # for running remote executor
|
36
36
|
s.add_dependency "parse-cron", '~> 0.1.4'
|
37
|
+
s.add_dependency "get_process_mem" # for memory polling
|
37
38
|
end
|
@@ -9,8 +9,9 @@ module ForemanTasks
|
|
9
9
|
require 'foreman_tasks/dynflow/daemon'
|
10
10
|
require 'foreman_tasks/dynflow/console_authorizer'
|
11
11
|
|
12
|
-
def initialize
|
12
|
+
def initialize(world_class = nil)
|
13
13
|
@required = false
|
14
|
+
@world_class = world_class
|
14
15
|
end
|
15
16
|
|
16
17
|
def config
|
@@ -37,7 +38,7 @@ module ForemanTasks
|
|
37
38
|
if config.lazy_initialization && defined?(PhusionPassenger)
|
38
39
|
config.dynflow_logger.warn('ForemanTasks: lazy loading with PhusionPassenger might lead to unexpected results')
|
39
40
|
end
|
40
|
-
|
41
|
+
init_world.tap do |world|
|
41
42
|
@world = world
|
42
43
|
|
43
44
|
unless config.remote?
|
@@ -119,5 +120,12 @@ module ForemanTasks
|
|
119
120
|
def loaded_paths
|
120
121
|
@loaded_paths ||= Set.new
|
121
122
|
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
def init_world
|
127
|
+
return config.initialize_world(@world_class) if @world_class
|
128
|
+
config.initialize_world
|
129
|
+
end
|
122
130
|
end
|
123
131
|
end
|
@@ -1,18 +1,47 @@
|
|
1
1
|
require 'fileutils'
|
2
|
+
require 'daemons'
|
3
|
+
require 'get_process_mem'
|
4
|
+
require 'dynflow/watchers/memory_consumption_watcher'
|
2
5
|
|
3
6
|
module ForemanTasks
|
4
7
|
class Dynflow::Daemon
|
8
|
+
attr_reader :dynflow_memory_watcher_class,
|
9
|
+
:daemons_class
|
10
|
+
|
11
|
+
# make Daemon dependency injection ready for testing purposes
|
12
|
+
def initialize(
|
13
|
+
dynflow_memory_watcher_class = ::Dynflow::Watchers::MemoryConsumptionWatcher,
|
14
|
+
daemons_class = ::Daemons
|
15
|
+
)
|
16
|
+
@dynflow_memory_watcher_class = dynflow_memory_watcher_class
|
17
|
+
@daemons_class = daemons_class
|
18
|
+
end
|
19
|
+
|
5
20
|
# load the Rails environment and initialize the executor
|
6
21
|
# in this thread.
|
7
|
-
def run(foreman_root = Dir.pwd)
|
22
|
+
def run(foreman_root = Dir.pwd, options = {})
|
8
23
|
STDERR.puts('Starting Rails environment')
|
9
24
|
foreman_env_file = File.expand_path('./config/environment.rb', foreman_root)
|
10
25
|
unless File.exist?(foreman_env_file)
|
11
26
|
raise "#{foreman_root} doesn't seem to be a foreman root directory"
|
12
27
|
end
|
28
|
+
|
29
|
+
STDERR.puts("Starting dynflow with the following options: #{options}")
|
30
|
+
|
13
31
|
ForemanTasks.dynflow.executor!
|
32
|
+
|
33
|
+
if options[:memory_limit] > 0
|
34
|
+
ForemanTasks.dynflow.config.on_init do |world|
|
35
|
+
memory_watcher = initialize_memory_watcher(world, options[:memory_limit], options)
|
36
|
+
world.terminated.on_completion do
|
37
|
+
STDERR.puts("World has been terminated")
|
38
|
+
memory_watcher = nil # the object can be disposed
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
14
43
|
require foreman_env_file
|
15
|
-
STDERR.puts(
|
44
|
+
STDERR.puts("Everything ready for world: #{(ForemanTasks.dynflow.initialized? ? ForemanTasks.dynflow.world.id : nil)}")
|
16
45
|
sleep
|
17
46
|
ensure
|
18
47
|
STDERR.puts('Exiting')
|
@@ -20,13 +49,6 @@ module ForemanTasks
|
|
20
49
|
|
21
50
|
# run the executor as a daemon
|
22
51
|
def run_background(command = 'start', options = {})
|
23
|
-
default_options = { foreman_root: Dir.pwd,
|
24
|
-
process_name: 'dynflow_executor',
|
25
|
-
pid_dir: "#{Rails.root}/tmp/pids",
|
26
|
-
log_dir: File.join(Rails.root, 'log'),
|
27
|
-
wait_attempts: 300,
|
28
|
-
wait_sleep: 1,
|
29
|
-
executors_count: (ENV['EXECUTORS_COUNT'] || 1).to_i }
|
30
52
|
options = default_options.merge(options)
|
31
53
|
FileUtils.mkdir_p(options[:pid_dir])
|
32
54
|
begin
|
@@ -42,18 +64,13 @@ module ForemanTasks
|
|
42
64
|
STDERR.puts("Dynflow Executor: #{command} in progress")
|
43
65
|
|
44
66
|
options[:executors_count].times do
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
:dir_mode => :normal,
|
50
|
-
:monitor => true,
|
51
|
-
:log_output => true,
|
52
|
-
:log_output_syslog => true,
|
53
|
-
:ARGV => [command]) do |*_args|
|
67
|
+
daemons_class.run_proc(
|
68
|
+
options[:process_name],
|
69
|
+
daemons_options(command, options)
|
70
|
+
) do |*_args|
|
54
71
|
begin
|
55
72
|
::Logging.reopen
|
56
|
-
run(options[:foreman_root])
|
73
|
+
run(options[:foreman_root], options)
|
57
74
|
rescue => e
|
58
75
|
STDERR.puts e.message
|
59
76
|
Foreman::Logging.exception('Failed running foreman-tasks daemon', e)
|
@@ -68,5 +85,59 @@ module ForemanTasks
|
|
68
85
|
def world
|
69
86
|
ForemanTasks.dynflow.world
|
70
87
|
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def daemons_options(command, options)
|
92
|
+
{
|
93
|
+
:multiple => true,
|
94
|
+
:dir => options[:pid_dir],
|
95
|
+
:log_dir => options[:log_dir],
|
96
|
+
:dir_mode => :normal,
|
97
|
+
:monitor => true,
|
98
|
+
:log_output => true,
|
99
|
+
:log_output_syslog => true,
|
100
|
+
:monitor_interval => [options[:memory_polling_interval] / 2, 30].min,
|
101
|
+
:ARGV => [command]
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
def default_options
|
106
|
+
{
|
107
|
+
foreman_root: Dir.pwd,
|
108
|
+
process_name: 'dynflow_executor',
|
109
|
+
pid_dir: "#{Rails.root}/tmp/pids",
|
110
|
+
log_dir: File.join(Rails.root, 'log'),
|
111
|
+
wait_attempts: 300,
|
112
|
+
wait_sleep: 1,
|
113
|
+
executors_count: (ENV['EXECUTORS_COUNT'] || 1).to_i,
|
114
|
+
memory_limit: begin
|
115
|
+
(ENV['EXECUTOR_MEMORY_LIMIT'] || '').to_gb.gigabytes
|
116
|
+
rescue RuntimeError
|
117
|
+
ENV['EXECUTOR_MEMORY_LIMIT'].to_i
|
118
|
+
end,
|
119
|
+
memory_init_delay: (ENV['EXECUTOR_MEMORY_MONITOR_DELAY'] || 7200).to_i, # 2 hours
|
120
|
+
memory_polling_interval: (ENV['EXECUTOR_MEMORY_MONITOR_INTERVAL'] || 60).to_i
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
def initialize_memory_watcher(world, memory_limit, options)
|
125
|
+
watcher_options = {}
|
126
|
+
watcher_options[:polling_interval] = options[:memory_polling_interval]
|
127
|
+
watcher_options[:initial_wait] = options[:memory_init_delay]
|
128
|
+
watcher_options[:memory_checked_callback] = ->(current_memory, memory_limit) { log_memory_within_limit(current_memory, memory_limit) }
|
129
|
+
watcher_options[:memory_limit_exceeded_callback] = ->(current_memory, memory_limit) { log_memory_limit_exceeded(current_memory, memory_limit) }
|
130
|
+
dynflow_memory_watcher_class.new(world, memory_limit, watcher_options)
|
131
|
+
end
|
132
|
+
|
133
|
+
def log_memory_limit_exceeded(current_memory, memory_limit)
|
134
|
+
message = "Memory level exceeded, registered #{current_memory} bytes, which is greater than #{memory_limit} limit."
|
135
|
+
Foreman::Logging.logger('foreman-tasks').error(message)
|
136
|
+
end
|
137
|
+
|
138
|
+
def log_memory_within_limit(current_memory, memory_limit)
|
139
|
+
message = "Memory level OK, registered #{current_memory} bytes, which is less than #{memory_limit} limit."
|
140
|
+
Foreman::Logging.logger('foreman-tasks').debug(message)
|
141
|
+
end
|
71
142
|
end
|
72
143
|
end
|