smart_proxy_dynflow_core 0.3.0 → 0.4.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 +4 -4
- data/Gemfile +1 -6
- data/lib/smart_proxy_dynflow_core.rb +9 -15
- data/lib/smart_proxy_dynflow_core/task_launcher_registry.rb +1 -29
- data/lib/smart_proxy_dynflow_core/version.rb +1 -1
- data/smart_proxy_dynflow_core.gemspec +3 -19
- metadata +6 -204
- data/bin/smart_proxy_dynflow_core +0 -32
- data/config/settings.yml.example +0 -66
- data/deploy/smart_proxy_dynflow_core.init +0 -92
- data/deploy/smart_proxy_dynflow_core.service +0 -13
- data/lib/smart_proxy_dynflow_core/api.rb +0 -89
- data/lib/smart_proxy_dynflow_core/bundler_helper.rb +0 -30
- data/lib/smart_proxy_dynflow_core/callback.rb +0 -85
- data/lib/smart_proxy_dynflow_core/core.rb +0 -124
- data/lib/smart_proxy_dynflow_core/helpers.rb +0 -73
- data/lib/smart_proxy_dynflow_core/launcher.rb +0 -166
- data/lib/smart_proxy_dynflow_core/log.rb +0 -146
- data/lib/smart_proxy_dynflow_core/logger_middleware.rb +0 -31
- data/lib/smart_proxy_dynflow_core/request_id_middleware.rb +0 -18
- data/lib/smart_proxy_dynflow_core/settings.rb +0 -86
- data/lib/smart_proxy_dynflow_core/testing.rb +0 -28
@@ -1,92 +0,0 @@
|
|
1
|
-
#!/bin/bash
|
2
|
-
#
|
3
|
-
# Init script for foreman smart proxy dynflow core service
|
4
|
-
#
|
5
|
-
# chkconfig: - 85 15
|
6
|
-
# description: Init script for foreman proxy dynflow core service
|
7
|
-
|
8
|
-
# Source function library.
|
9
|
-
. /etc/rc.d/init.d/functions
|
10
|
-
|
11
|
-
prog=smart_proxy_dynflow_core
|
12
|
-
RETVAL=0
|
13
|
-
SMART_PROXY_DYNFLOW_CORE_PID=/var/run/foreman-proxy/$prog.pid
|
14
|
-
SMART_PROXY_DYNFLOW_CORE_USER=${SMART_PROXY_DYNFLOW_CORE_USER:-foreman-proxy}
|
15
|
-
|
16
|
-
start() {
|
17
|
-
echo -n $"Starting $prog: "
|
18
|
-
ulimit -n 65536
|
19
|
-
daemon --user ${SMART_PROXY_DYNFLOW_CORE_USER} /usr/bin/smart_proxy_dynflow_core -d -p $SMART_PROXY_DYNFLOW_CORE_PID > /dev/null
|
20
|
-
RETVAL=$?
|
21
|
-
if [ $RETVAL = 0 ]
|
22
|
-
then
|
23
|
-
echo_success
|
24
|
-
else
|
25
|
-
echo_failure
|
26
|
-
fi
|
27
|
-
|
28
|
-
echo
|
29
|
-
return $RETVAL
|
30
|
-
}
|
31
|
-
|
32
|
-
stop() {
|
33
|
-
echo -n $"Stopping $prog: "
|
34
|
-
if [ -f ${SMART_PROXY_DYNFLOW_CORE_PID} ]; then
|
35
|
-
killproc -p ${SMART_PROXY_DYNFLOW_CORE_PID}
|
36
|
-
RETVAL=$?
|
37
|
-
else
|
38
|
-
echo -n $"$prog was not running.";
|
39
|
-
failure $"$prog was not running.";
|
40
|
-
echo
|
41
|
-
return 1
|
42
|
-
fi
|
43
|
-
echo
|
44
|
-
[ $RETVAL -eq 0 ] && rm -f ${SMART_PROXY_DYNFLOW_CORE_PID}
|
45
|
-
return $RETVAL
|
46
|
-
}
|
47
|
-
|
48
|
-
logrotate() {
|
49
|
-
echo -n $"Rotating logs for $prog: "
|
50
|
-
if [ -f ${SMART_PROXY_DYNFLOW_CORE_PID} ]; then
|
51
|
-
killproc -p ${SMART_PROXY_DYNFLOW_CORE_PID} $prog -USR1
|
52
|
-
RETVAL=$?
|
53
|
-
echo
|
54
|
-
else
|
55
|
-
echo -n $"$prog was not running.";
|
56
|
-
failure $"$prog was not running.";
|
57
|
-
echo
|
58
|
-
return 1
|
59
|
-
fi
|
60
|
-
return $RETVAL
|
61
|
-
}
|
62
|
-
|
63
|
-
# See how we were called.
|
64
|
-
case "$1" in
|
65
|
-
start)
|
66
|
-
start
|
67
|
-
;;
|
68
|
-
stop)
|
69
|
-
stop
|
70
|
-
;;
|
71
|
-
status)
|
72
|
-
echo -n "$prog"
|
73
|
-
status -p $SMART_PROXY_DYNFLOW_CORE_PID
|
74
|
-
RETVAL=$?
|
75
|
-
;;
|
76
|
-
restart)
|
77
|
-
stop
|
78
|
-
start
|
79
|
-
;;
|
80
|
-
condrestart)
|
81
|
-
stop
|
82
|
-
[ $? -eq 0 ] && start
|
83
|
-
;;
|
84
|
-
logrotate)
|
85
|
-
logrotate
|
86
|
-
;;
|
87
|
-
*)
|
88
|
-
echo $"Usage: $prog {start|stop|restart|condrestart|logrotate}"
|
89
|
-
exit 1
|
90
|
-
esac
|
91
|
-
|
92
|
-
exit $RETVAL
|
@@ -1,13 +0,0 @@
|
|
1
|
-
[Unit]
|
2
|
-
Description=Foreman smart proxy dynflow core service
|
3
|
-
Documentation=https://github.com/theforeman/smart_proxy_dynflow
|
4
|
-
After=network.target remote-fs.target nss-lookup.target
|
5
|
-
|
6
|
-
[Service]
|
7
|
-
Type=notify
|
8
|
-
User=foreman-proxy
|
9
|
-
ExecStart=/usr/bin/smart_proxy_dynflow_core --no-daemonize
|
10
|
-
EnvironmentFile=-/etc/sysconfig/smart_proxy_dynflow_core
|
11
|
-
|
12
|
-
[Install]
|
13
|
-
WantedBy=multi-user.target
|
@@ -1,89 +0,0 @@
|
|
1
|
-
require 'sinatra/base'
|
2
|
-
require 'multi_json'
|
3
|
-
|
4
|
-
module SmartProxyDynflowCore
|
5
|
-
class Api < ::Sinatra::Base
|
6
|
-
TASK_UPDATE_REGEXP_PATH = %r{/tasks/(\S+)/(update|done)}
|
7
|
-
helpers Helpers
|
8
|
-
|
9
|
-
configure do
|
10
|
-
if Settings.instance.standalone
|
11
|
-
::Sinatra::Base.set :logging, false
|
12
|
-
::Sinatra::Base.use ::SmartProxyDynflowCore::RequestIdMiddleware
|
13
|
-
::Sinatra::Base.use ::SmartProxyDynflowCore::LoggerMiddleware
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
before do
|
18
|
-
if match = request.path_info.match(TASK_UPDATE_REGEXP_PATH)
|
19
|
-
task_id = match[1]
|
20
|
-
action = match[2]
|
21
|
-
authorize_with_token(task_id: task_id, clear: action == 'done')
|
22
|
-
else
|
23
|
-
authorize_with_ssl_client
|
24
|
-
end
|
25
|
-
content_type :json
|
26
|
-
end
|
27
|
-
|
28
|
-
post "/tasks/status" do
|
29
|
-
params = MultiJson.load(request.body.read)
|
30
|
-
ids = params.fetch('task_ids', [])
|
31
|
-
result = world.persistence
|
32
|
-
.find_execution_plans(:filters => { :uuid => ids }).reduce({}) do |acc, plan|
|
33
|
-
acc.update(plan.id => { 'state' => plan.state, 'result' => plan.result })
|
34
|
-
end
|
35
|
-
MultiJson.dump(result)
|
36
|
-
end
|
37
|
-
|
38
|
-
post "/tasks/launch/?" do
|
39
|
-
params = MultiJson.load(request.body.read)
|
40
|
-
launcher = launcher_class(params).new(world, callback_host(params, request), params.fetch('options', {}))
|
41
|
-
launcher.launch!(params['input'])
|
42
|
-
launcher.results.to_json
|
43
|
-
end
|
44
|
-
|
45
|
-
post "/tasks/?" do
|
46
|
-
params = MultiJson.load(request.body.read)
|
47
|
-
trigger_task(::Dynflow::Utils.constantize(params['action_name']),
|
48
|
-
params['action_input'].merge(:callback_host => callback_host(params, request))).to_json
|
49
|
-
end
|
50
|
-
|
51
|
-
post "/tasks/:task_id/cancel" do |task_id|
|
52
|
-
cancel_task(task_id).to_json
|
53
|
-
end
|
54
|
-
|
55
|
-
get "/tasks/:task_id/status" do |task_id|
|
56
|
-
task_status(task_id).to_json
|
57
|
-
end
|
58
|
-
|
59
|
-
get "/tasks/count" do
|
60
|
-
tasks_count(params['state']).to_json
|
61
|
-
end
|
62
|
-
|
63
|
-
# capturing post "/tasks/:task_id/(update|done)"
|
64
|
-
post TASK_UPDATE_REGEXP_PATH do |task_id, _action|
|
65
|
-
data = MultiJson.load(request.body.read)
|
66
|
-
dispatch_external_event(task_id, data)
|
67
|
-
end
|
68
|
-
|
69
|
-
get "/tasks/operations" do
|
70
|
-
TaskLauncherRegistry.operations.to_json
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def callback_host(params, request)
|
76
|
-
params.fetch('action_input', {})['proxy_url'] ||
|
77
|
-
request.env.values_at('HTTP_X_FORWARDED_FOR', 'HTTP_HOST').compact.first
|
78
|
-
end
|
79
|
-
|
80
|
-
def launcher_class(params)
|
81
|
-
operation = params.fetch('operation')
|
82
|
-
if TaskLauncherRegistry.key?(operation)
|
83
|
-
TaskLauncherRegistry.fetch(operation)
|
84
|
-
else
|
85
|
-
halt 404, MultiJson.dump(:error => "Unknown operation '#{operation}' requested.")
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
@@ -1,30 +0,0 @@
|
|
1
|
-
module SmartProxyDynflowCore
|
2
|
-
class BundlerHelper
|
3
|
-
def self.require_groups(*groups)
|
4
|
-
if File.exist?(File.expand_path('../../../Gemfile.in', __FILE__))
|
5
|
-
# If there is a Gemfile.in file, we will not use Bundler but BundlerExt
|
6
|
-
# gem which parses this file and loads all dependencies from the system
|
7
|
-
# rathern then trying to download them from rubygems.org. It always
|
8
|
-
# loads all gemfile groups.
|
9
|
-
begin
|
10
|
-
require 'bundler_ext' unless defined?(BundlerExt)
|
11
|
-
rescue LoadError
|
12
|
-
# Debian packaging guidelines state to avoid needing rubygems, so
|
13
|
-
# we only try to load it if the first require fails (for RPMs)
|
14
|
-
begin
|
15
|
-
require 'rubygems' rescue nil
|
16
|
-
require 'bundler_ext'
|
17
|
-
rescue LoadError
|
18
|
-
puts "`bundler_ext` gem is required to run smart_proxy"
|
19
|
-
exit 1
|
20
|
-
end
|
21
|
-
end
|
22
|
-
BundlerExt.system_require(File.expand_path('../../../Gemfile.in', __FILE__), *groups)
|
23
|
-
else
|
24
|
-
require 'bundler' unless defined?(Bundler)
|
25
|
-
Bundler.require(*groups)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
29
|
-
end
|
30
|
-
end
|
@@ -1,85 +0,0 @@
|
|
1
|
-
require 'rest-client'
|
2
|
-
|
3
|
-
# rubocop:disable Lint/HandleExceptions
|
4
|
-
begin
|
5
|
-
require 'smart_proxy_dynflow/callback'
|
6
|
-
rescue LoadError
|
7
|
-
end
|
8
|
-
# rubocop:enable Lint/HandleExceptions
|
9
|
-
|
10
|
-
module SmartProxyDynflowCore
|
11
|
-
module Callback
|
12
|
-
class Request
|
13
|
-
class << self
|
14
|
-
def send_to_foreman_tasks(callback_info, data)
|
15
|
-
self.new.callback(prepare_payload(callback_info, data))
|
16
|
-
end
|
17
|
-
|
18
|
-
def ssl_options
|
19
|
-
return @ssl_options if defined? @ssl_options
|
20
|
-
@ssl_options = {}
|
21
|
-
settings = Settings.instance
|
22
|
-
return @ssl_options unless URI.parse(settings.foreman_url).scheme == 'https'
|
23
|
-
|
24
|
-
@ssl_options[:verify_ssl] = OpenSSL::SSL::VERIFY_PEER
|
25
|
-
|
26
|
-
private_key_file = settings.foreman_ssl_key || settings.ssl_private_key
|
27
|
-
if private_key_file
|
28
|
-
private_key = File.read(private_key_file)
|
29
|
-
@ssl_options[:ssl_client_key] = OpenSSL::PKey::RSA.new(private_key)
|
30
|
-
end
|
31
|
-
certificate_file = settings.foreman_ssl_cert || settings.ssl_certificate
|
32
|
-
if certificate_file
|
33
|
-
certificate = File.read(certificate_file)
|
34
|
-
@ssl_options[:ssl_client_cert] = OpenSSL::X509::Certificate.new(certificate)
|
35
|
-
end
|
36
|
-
ca_file = settings.foreman_ssl_ca || settings.ssl_ca_file
|
37
|
-
@ssl_options[:ssl_ca_file] = ca_file if ca_file
|
38
|
-
@ssl_options
|
39
|
-
end
|
40
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def prepare_payload(callback, data)
|
45
|
-
{ :callback => callback, :data => data }.to_json
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def callback(payload)
|
50
|
-
response = callback_resource.post(payload, :content_type => :json)
|
51
|
-
if response.code.to_s != "200"
|
52
|
-
raise "Failed performing callback to Foreman server: #{response.code} #{response.body}"
|
53
|
-
end
|
54
|
-
response
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
def callback_resource
|
60
|
-
@resource ||= RestClient::Resource.new(Settings.instance.foreman_url + '/foreman_tasks/api/tasks/callback',
|
61
|
-
self.class.ssl_options)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
class Action < ::Dynflow::Action
|
66
|
-
def plan(callback, data)
|
67
|
-
plan_self(:callback => callback, :data => data)
|
68
|
-
end
|
69
|
-
|
70
|
-
def run
|
71
|
-
Callback::Request.send_to_foreman_tasks(input[:callback], input[:data])
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
module PlanHelper
|
76
|
-
def plan_with_callback(input)
|
77
|
-
input = input.dup
|
78
|
-
callback = input.delete('callback')
|
79
|
-
|
80
|
-
planned_action = plan_self(input)
|
81
|
-
plan_action(Callback::Action, callback, planned_action.output) if callback
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
end
|
@@ -1,124 +0,0 @@
|
|
1
|
-
module SmartProxyDynflowCore
|
2
|
-
class Core
|
3
|
-
attr_accessor :world, :accepted_cert_serial
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@world = create_world
|
7
|
-
cert_file = Settings.instance.foreman_ssl_cert || Settings.instance.ssl_certificate
|
8
|
-
if cert_file
|
9
|
-
client_cert = File.read(cert_file)
|
10
|
-
# we trust only requests using the same certificate as we are
|
11
|
-
# (in other words the local proxy only)
|
12
|
-
@accepted_cert_serial = OpenSSL::X509::Certificate.new(client_cert).serial
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def create_world(&block)
|
17
|
-
config = default_world_config(&block)
|
18
|
-
world = ::Dynflow::World.new(config)
|
19
|
-
world.middleware.use ::Actions::Middleware::KeepCurrentRequestID
|
20
|
-
world
|
21
|
-
end
|
22
|
-
|
23
|
-
def persistence_conn_string
|
24
|
-
return ENV['DYNFLOW_DB_CONN_STRING'] if ENV.key? 'DYNFLOW_DB_CONN_STRING'
|
25
|
-
db_conn_string = 'sqlite:/'
|
26
|
-
|
27
|
-
db_file = Settings.instance.database
|
28
|
-
if db_file.nil? || db_file.empty?
|
29
|
-
Log.instance.warn "Could not open DB for dynflow at '#{db_file}', " \
|
30
|
-
"will keep data in memory. Restart will drop all dynflow data."
|
31
|
-
else
|
32
|
-
db_conn_string += "/#{db_file}"
|
33
|
-
end
|
34
|
-
|
35
|
-
db_conn_string
|
36
|
-
end
|
37
|
-
|
38
|
-
def persistence_adapter
|
39
|
-
::Dynflow::PersistenceAdapters::Sequel.new persistence_conn_string
|
40
|
-
end
|
41
|
-
|
42
|
-
def default_world_config
|
43
|
-
::Dynflow::Config.new.tap do |config|
|
44
|
-
config.auto_rescue = true
|
45
|
-
config.logger_adapter = logger_adapter
|
46
|
-
config.persistence_adapter = persistence_adapter
|
47
|
-
config.execution_plan_cleaner = execution_plan_cleaner
|
48
|
-
# TODO: There has to be a better way
|
49
|
-
matchers = config.silent_dead_letter_matchers.call.concat(self.class.silencer_matchers)
|
50
|
-
config.silent_dead_letter_matchers = matchers
|
51
|
-
yield config if block_given?
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def logger_adapter
|
56
|
-
if Settings.instance.standalone
|
57
|
-
Log::ProxyAdapter.new(Log.instance, Log.instance.level)
|
58
|
-
else
|
59
|
-
Log::ProxyAdapter.new(Proxy::LogBuffer::Decorator.instance, Log.instance.level)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
def execution_plan_cleaner
|
64
|
-
proc do |world|
|
65
|
-
age = Settings.instance.execution_plan_cleaner_age
|
66
|
-
options = { :poll_interval => age, :max_age => age }
|
67
|
-
::Dynflow::Actors::ExecutionPlanCleaner.new(world, options)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
class << self
|
72
|
-
attr_reader :instance
|
73
|
-
|
74
|
-
def ensure_initialized
|
75
|
-
return @instance if @instance
|
76
|
-
@instance = Core.new
|
77
|
-
after_initialize_blocks.each { |block| block.call(@instance) }
|
78
|
-
@instance
|
79
|
-
end
|
80
|
-
|
81
|
-
def silencer_matchers
|
82
|
-
@matchers ||= []
|
83
|
-
end
|
84
|
-
|
85
|
-
def register_silencer_matchers(matchers)
|
86
|
-
silencer_matchers.concat matchers
|
87
|
-
end
|
88
|
-
|
89
|
-
def web_console
|
90
|
-
require 'dynflow/web'
|
91
|
-
dynflow_console = ::Dynflow::Web.setup do
|
92
|
-
# we can't use the proxy's after_activation hook, as
|
93
|
-
# it happens before the Daemon forks the process (including
|
94
|
-
# closing opened file descriptors)
|
95
|
-
# TODO: extend smart proxy to enable hooks that happen after
|
96
|
-
# the forking
|
97
|
-
helpers Helpers
|
98
|
-
|
99
|
-
before do
|
100
|
-
authorize_with_ssl_client if Settings.instance.console_auth
|
101
|
-
end
|
102
|
-
|
103
|
-
Core.ensure_initialized
|
104
|
-
set :world, Core.world
|
105
|
-
end
|
106
|
-
dynflow_console
|
107
|
-
end
|
108
|
-
|
109
|
-
def world
|
110
|
-
instance.world
|
111
|
-
end
|
112
|
-
|
113
|
-
def after_initialize(&block)
|
114
|
-
after_initialize_blocks << block
|
115
|
-
end
|
116
|
-
|
117
|
-
private
|
118
|
-
|
119
|
-
def after_initialize_blocks
|
120
|
-
@after_initialize_blocks ||= []
|
121
|
-
end
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
@@ -1,73 +0,0 @@
|
|
1
|
-
module SmartProxyDynflowCore
|
2
|
-
module Helpers
|
3
|
-
def world
|
4
|
-
SmartProxyDynflowCore::Core.world
|
5
|
-
end
|
6
|
-
|
7
|
-
def authorize_with_token(task_id:, clear: true)
|
8
|
-
if request.env.key? 'HTTP_AUTHORIZATION'
|
9
|
-
if defined?(::ForemanTasksCore)
|
10
|
-
auth = request.env['HTTP_AUTHORIZATION']
|
11
|
-
basic_prefix = /\ABasic /
|
12
|
-
if !auth.to_s.empty? && auth =~ basic_prefix &&
|
13
|
-
ForemanTasksCore::OtpManager.authenticate(auth.gsub(basic_prefix, ''),
|
14
|
-
expected_user: task_id, clear: clear)
|
15
|
-
Log.instance.debug('authorized with token')
|
16
|
-
return true
|
17
|
-
end
|
18
|
-
end
|
19
|
-
halt 403, MultiJson.dump(:error => 'Invalid username or password supplied')
|
20
|
-
end
|
21
|
-
false
|
22
|
-
end
|
23
|
-
|
24
|
-
def authorize_with_ssl_client
|
25
|
-
if %w[yes on 1].include? request.env['HTTPS'].to_s
|
26
|
-
if request.env['SSL_CLIENT_CERT'].to_s.empty?
|
27
|
-
Log.instance.error "No client SSL certificate supplied"
|
28
|
-
halt 403, MultiJson.dump(:error => "No client SSL certificate supplied")
|
29
|
-
else
|
30
|
-
client_cert = OpenSSL::X509::Certificate.new(request.env['SSL_CLIENT_CERT'])
|
31
|
-
unless SmartProxyDynflowCore::Core.instance.accepted_cert_serial == client_cert.serial
|
32
|
-
Log.instance.error "SSL certificate with unexpected serial supplied"
|
33
|
-
halt 403, MultiJson.dump(:error => "SSL certificate with unexpected serial supplied")
|
34
|
-
end
|
35
|
-
end
|
36
|
-
else
|
37
|
-
Log.instance.debug 'require_ssl_client_verification: skipping, non-HTTPS request'
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def trigger_task(*args)
|
42
|
-
triggered = world.trigger(*args)
|
43
|
-
{ :task_id => triggered.id }
|
44
|
-
end
|
45
|
-
|
46
|
-
def cancel_task(task_id)
|
47
|
-
execution_plan = world.persistence.load_execution_plan(task_id)
|
48
|
-
cancel_events = execution_plan.cancel
|
49
|
-
{ :task_id => task_id, :canceled_steps_count => cancel_events.size }
|
50
|
-
end
|
51
|
-
|
52
|
-
def task_status(task_id)
|
53
|
-
ep = world.persistence.load_execution_plan(task_id)
|
54
|
-
ep.to_hash.merge(:actions => ep.actions.map(&:to_hash))
|
55
|
-
rescue KeyError => _e
|
56
|
-
status 404
|
57
|
-
{}
|
58
|
-
end
|
59
|
-
|
60
|
-
def tasks_count(state)
|
61
|
-
state ||= 'all'
|
62
|
-
filter = state != 'all' ? { :filters => { :state => [state] } } : {}
|
63
|
-
tasks = world.persistence.find_execution_plans(filter)
|
64
|
-
{ :count => tasks.count, :state => state }
|
65
|
-
end
|
66
|
-
|
67
|
-
def dispatch_external_event(task_id, params)
|
68
|
-
world.event(task_id,
|
69
|
-
params['step_id'].to_i,
|
70
|
-
::ForemanTasksCore::Runner::ExternalEvent.new(params))
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|