smart_proxy_dynflow_core 0.3.3 → 0.4.0
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/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 +2 -4
- metadata +3 -20
- 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 -102
- 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/middleware/keep_current_request_id.rb +0 -59
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3226e9b143ecf53b28e97a765c2e760d42dc030ea3809736f5e7733b2db96cfe
|
4
|
+
data.tar.gz: eb70345b228d35506d36faa0a3fe59e09c8b1787d438f73d6554699736a70d3a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf3292bd295765e51968781f4a0a3678a5825977da06b56ddf86e073a232665f92f90cd5df25e0a6332304b4c3c15784404f2aae8d25f050cc77405d7520e57f
|
7
|
+
data.tar.gz: 1bdaaebb6e5b59fcf25250c4ba7695dadda3313854c8db343199bb8ec61529ed21deff5e948ac0545f6fde0eab6cde7bde73067b96943061e5bd013a22faa9db
|
@@ -1,19 +1,13 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
require '
|
1
|
+
# Whatever is here is just a compatibility layer
|
2
|
+
# Once all the _core have been migrated, this can be dropped.
|
3
|
+
|
4
|
+
require 'smart_proxy_dynflow'
|
5
|
+
|
6
|
+
# REX core explicitly requires this file, otherwise we could use the trick we
|
7
|
+
# use with callback
|
5
8
|
require 'smart_proxy_dynflow_core/task_launcher_registry'
|
6
|
-
require 'foreman_tasks_core'
|
7
|
-
require 'smart_proxy_dynflow_core/log'
|
8
|
-
require 'smart_proxy_dynflow_core/settings'
|
9
|
-
require 'smart_proxy_dynflow_core/core'
|
10
|
-
require 'smart_proxy_dynflow_core/helpers'
|
11
|
-
require 'smart_proxy_dynflow_core/callback'
|
12
|
-
require 'smart_proxy_dynflow_core/api'
|
13
9
|
|
14
10
|
module SmartProxyDynflowCore
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
Core.register_silencer_matchers ForemanTasksCore.silent_dead_letter_matchers
|
11
|
+
Callback = Proxy::Dynflow::Callback
|
12
|
+
Log = Proxy::Dynflow::Log
|
19
13
|
end
|
@@ -1,31 +1,3 @@
|
|
1
1
|
module SmartProxyDynflowCore
|
2
|
-
|
3
|
-
class << self
|
4
|
-
def register(name, launcher)
|
5
|
-
registry[name] = launcher
|
6
|
-
end
|
7
|
-
|
8
|
-
def fetch(name, default = nil)
|
9
|
-
if default.nil?
|
10
|
-
registry.fetch(name)
|
11
|
-
else
|
12
|
-
registry.fetch(name, default)
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
def key?(name)
|
17
|
-
registry.key?(name)
|
18
|
-
end
|
19
|
-
|
20
|
-
def operations
|
21
|
-
registry.keys
|
22
|
-
end
|
23
|
-
|
24
|
-
private
|
25
|
-
|
26
|
-
def registry
|
27
|
-
@registry ||= {}
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
2
|
+
TaskLauncherRegistry = Proxy::Dynflow::TaskLauncherRegistry
|
31
3
|
end
|
@@ -13,10 +13,8 @@ Gem::Specification.new do |gem|
|
|
13
13
|
Use the Dynflow inside Foreman smart proxy
|
14
14
|
EOS
|
15
15
|
|
16
|
-
gem.
|
17
|
-
|
18
|
-
'lib/smart_proxy_dynflow_core/**/*', 'LICENSE', 'Gemfile',
|
19
|
-
'bin/smart_proxy_dynflow_core', 'deploy/*', 'smart_proxy_dynflow_core.gemspec']
|
16
|
+
gem.files = Dir['lib/smart_proxy_dynflow_core.rb', 'lib/smart_proxy_dynflow_core/**/*',
|
17
|
+
'LICENSE', 'Gemfile', 'smart_proxy_dynflow_core.gemspec']
|
20
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
21
19
|
gem.require_paths = ["lib"]
|
22
20
|
gem.license = 'GPL-3.0'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_proxy_dynflow_core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
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: 2021-
|
11
|
+
date: 2021-06-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -223,31 +223,14 @@ dependencies:
|
|
223
223
|
description: " Use the Dynflow inside Foreman smart proxy\n"
|
224
224
|
email:
|
225
225
|
- inecas@redhat.com
|
226
|
-
executables:
|
227
|
-
- smart_proxy_dynflow_core
|
226
|
+
executables: []
|
228
227
|
extensions: []
|
229
228
|
extra_rdoc_files: []
|
230
229
|
files:
|
231
230
|
- Gemfile
|
232
231
|
- LICENSE
|
233
|
-
- bin/smart_proxy_dynflow_core
|
234
|
-
- config/settings.yml.example
|
235
|
-
- deploy/smart_proxy_dynflow_core.init
|
236
|
-
- deploy/smart_proxy_dynflow_core.service
|
237
232
|
- lib/smart_proxy_dynflow_core.rb
|
238
|
-
- lib/smart_proxy_dynflow_core/api.rb
|
239
|
-
- lib/smart_proxy_dynflow_core/bundler_helper.rb
|
240
|
-
- lib/smart_proxy_dynflow_core/callback.rb
|
241
|
-
- lib/smart_proxy_dynflow_core/core.rb
|
242
|
-
- lib/smart_proxy_dynflow_core/helpers.rb
|
243
|
-
- lib/smart_proxy_dynflow_core/launcher.rb
|
244
|
-
- lib/smart_proxy_dynflow_core/log.rb
|
245
|
-
- lib/smart_proxy_dynflow_core/logger_middleware.rb
|
246
|
-
- lib/smart_proxy_dynflow_core/middleware/keep_current_request_id.rb
|
247
|
-
- lib/smart_proxy_dynflow_core/request_id_middleware.rb
|
248
|
-
- lib/smart_proxy_dynflow_core/settings.rb
|
249
233
|
- lib/smart_proxy_dynflow_core/task_launcher_registry.rb
|
250
|
-
- lib/smart_proxy_dynflow_core/testing.rb
|
251
234
|
- lib/smart_proxy_dynflow_core/version.rb
|
252
235
|
- smart_proxy_dynflow_core.gemspec
|
253
236
|
homepage: https://github.com/theforeman/smart_proxy_dynflow
|
@@ -1,32 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
require 'dynflow'
|
3
|
-
require 'rack'
|
4
|
-
require 'smart_proxy_dynflow_core/launcher'
|
5
|
-
require 'yaml'
|
6
|
-
require 'optparse'
|
7
|
-
|
8
|
-
options = {}
|
9
|
-
OptionParser.new do |opts|
|
10
|
-
opts.on('-c', '--config-dir CONFIG_DIR', String, 'Directory to load settings from') do |value|
|
11
|
-
options[:config_dir] = value
|
12
|
-
end
|
13
|
-
|
14
|
-
opts.on('-1', '--one-config', 'Do not load more than 1 config') do |value|
|
15
|
-
options[:one_config] = true
|
16
|
-
end
|
17
|
-
|
18
|
-
opts.on('-d', '--[no-]daemonize', 'Fork to background after start') do |value|
|
19
|
-
options[:daemonize] = value
|
20
|
-
end
|
21
|
-
|
22
|
-
opts.on('-p', '--pid-file PID_FILE', String, 'Write pid to this file') do |value|
|
23
|
-
options[:pid_file] = value
|
24
|
-
end
|
25
|
-
|
26
|
-
opts.on_tail('-h', '--help', 'Show usage help') do
|
27
|
-
puts opts
|
28
|
-
exit
|
29
|
-
end
|
30
|
-
end.parse!
|
31
|
-
|
32
|
-
SmartProxyDynflowCore::Launcher.launch! options
|
data/config/settings.yml.example
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
---
|
2
|
-
# Path to dynflow database, leave blank for in-memory non-persistent database
|
3
|
-
:database:
|
4
|
-
|
5
|
-
# URL of the foreman, used for reporting back
|
6
|
-
:foreman_url: 'http://localhost:3000'
|
7
|
-
|
8
|
-
# SSL settings for client authentication against Foreman
|
9
|
-
# :foreman_ssl_ca: ssl/foreman_ca.pem
|
10
|
-
# :foreman_ssl_key: ssl/foreman_key.pem
|
11
|
-
# :foreman_ssl_cert: ssl/foreman_cert.pem
|
12
|
-
|
13
|
-
:console_auth: false
|
14
|
-
|
15
|
-
# Set to true to make the core fork to background after start
|
16
|
-
# :daemonize: false
|
17
|
-
# :pid_file: /var/run/foreman-proxy/smart_proxy_dynflow_core.pid
|
18
|
-
|
19
|
-
# Listen on address
|
20
|
-
:listen: 127.0.0.1
|
21
|
-
|
22
|
-
# Listen on port
|
23
|
-
:port: 8008
|
24
|
-
|
25
|
-
# SSL settings for running core as https service
|
26
|
-
# :use_https: false
|
27
|
-
# :ssl_ca_file: ssl/ca.pem
|
28
|
-
# :ssl_private_key: ssl/localhost.pem
|
29
|
-
# :ssl_certificate: ssl/certs/localhost.pem
|
30
|
-
|
31
|
-
# Use this option only if you need to disable certain cipher suites.
|
32
|
-
# Note: we use the OpenSSL suite name, take a look at:
|
33
|
-
# https://www.openssl.org/docs/manmaster/apps/ciphers.html#CIPHER-SUITE-NAMES
|
34
|
-
# for more information.
|
35
|
-
#:ssl_disabled_ciphers: [CIPHER-SUITE-1, CIPHER-SUITE-2]
|
36
|
-
|
37
|
-
# Use this option only if you need to strictly specify TLS versions to be
|
38
|
-
# disabled. SSLv3 and TLS v1.0 are always disabled and cannot be configured.
|
39
|
-
# Specify versions like: '1.1', or '1.2'
|
40
|
-
#:tls_disabled_versions: []
|
41
|
-
|
42
|
-
# File to log to, leave empty for stdout or use STDOUT, STDERR, SYSLOG or JOURNAL
|
43
|
-
#:log_file: /var/log/foreman-proxy/smart_proxy_dynflow_core.log
|
44
|
-
|
45
|
-
# Log level, one of UNKNOWN, FATAL, ERROR, WARN, INFO, DEBUG
|
46
|
-
#:log_level: ERROR
|
47
|
-
|
48
|
-
# The maximum size of a log file before it's rolled (in MiB) or zero to disable file rolling
|
49
|
-
# and rely on logrotate or similar tool
|
50
|
-
#:file_rolling_size: 0
|
51
|
-
|
52
|
-
# The maximum age of a log file before it's rolled (in seconds). Also accepts 'daily', 'weekly', or 'monthly'.
|
53
|
-
#:file_rolling_age: weekly
|
54
|
-
|
55
|
-
# Number of log files to keep
|
56
|
-
#:file_rolling_keep: 6
|
57
|
-
|
58
|
-
# Logging pattern for file-based loging
|
59
|
-
#:file_logging_pattern: '%d %.8X{request} [%.1l] %m'
|
60
|
-
|
61
|
-
# Logging pattern for syslog or journal loging
|
62
|
-
#:system_logging_pattern: '%.8X{request} [%.1l] %m'
|
63
|
-
|
64
|
-
# Maximum age of execution plans to keep before having them cleaned
|
65
|
-
# by the execution plan cleaner (in seconds), defaults to 24 hours
|
66
|
-
# :execution_plan_cleaner_age: 86400
|
@@ -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,102 +0,0 @@
|
|
1
|
-
require 'sinatra/base'
|
2
|
-
require 'multi_json'
|
3
|
-
|
4
|
-
# rubocop:disable Lint/HandleExceptions
|
5
|
-
begin
|
6
|
-
require 'proxy/log'
|
7
|
-
require 'proxy/helpers'
|
8
|
-
require 'sinatra/authorization'
|
9
|
-
rescue LoadError
|
10
|
-
end
|
11
|
-
# rubocop:enable Lint/HandleExceptions
|
12
|
-
|
13
|
-
module SmartProxyDynflowCore
|
14
|
-
class Api < ::Sinatra::Base
|
15
|
-
TASK_UPDATE_REGEXP_PATH = %r{/tasks/(\S+)/(update|done)}
|
16
|
-
helpers Helpers
|
17
|
-
|
18
|
-
include ::Sinatra::Authorization::Helpers if defined?(::Sinatra::Authorization::Helpers)
|
19
|
-
|
20
|
-
configure do
|
21
|
-
if Settings.instance.standalone
|
22
|
-
::Sinatra::Base.set :logging, false
|
23
|
-
::Sinatra::Base.use ::SmartProxyDynflowCore::RequestIdMiddleware
|
24
|
-
::Sinatra::Base.use ::SmartProxyDynflowCore::LoggerMiddleware
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
before do
|
29
|
-
if match = request.path_info.match(TASK_UPDATE_REGEXP_PATH)
|
30
|
-
task_id = match[1]
|
31
|
-
action = match[2]
|
32
|
-
authorize_with_token(task_id: task_id, clear: action == 'done')
|
33
|
-
elsif Settings.instance.standalone
|
34
|
-
authorize_with_ssl_client
|
35
|
-
else
|
36
|
-
do_authorize_any
|
37
|
-
end
|
38
|
-
content_type :json
|
39
|
-
end
|
40
|
-
|
41
|
-
post "/tasks/status" do
|
42
|
-
params = MultiJson.load(request.body.read)
|
43
|
-
ids = params.fetch('task_ids', [])
|
44
|
-
result = world.persistence
|
45
|
-
.find_execution_plans(:filters => { :uuid => ids }).reduce({}) do |acc, plan|
|
46
|
-
acc.update(plan.id => { 'state' => plan.state, 'result' => plan.result })
|
47
|
-
end
|
48
|
-
MultiJson.dump(result)
|
49
|
-
end
|
50
|
-
|
51
|
-
post "/tasks/launch/?" do
|
52
|
-
params = MultiJson.load(request.body.read)
|
53
|
-
launcher = launcher_class(params).new(world, callback_host(params, request), params.fetch('options', {}))
|
54
|
-
launcher.launch!(params['input'])
|
55
|
-
launcher.results.to_json
|
56
|
-
end
|
57
|
-
|
58
|
-
post "/tasks/?" do
|
59
|
-
params = MultiJson.load(request.body.read)
|
60
|
-
trigger_task(::Dynflow::Utils.constantize(params['action_name']),
|
61
|
-
params['action_input'].merge(:callback_host => callback_host(params, request))).to_json
|
62
|
-
end
|
63
|
-
|
64
|
-
post "/tasks/:task_id/cancel" do |task_id|
|
65
|
-
cancel_task(task_id).to_json
|
66
|
-
end
|
67
|
-
|
68
|
-
get "/tasks/:task_id/status" do |task_id|
|
69
|
-
task_status(task_id).to_json
|
70
|
-
end
|
71
|
-
|
72
|
-
get "/tasks/count" do
|
73
|
-
tasks_count(params['state']).to_json
|
74
|
-
end
|
75
|
-
|
76
|
-
# capturing post "/tasks/:task_id/(update|done)"
|
77
|
-
post TASK_UPDATE_REGEXP_PATH do |task_id, _action|
|
78
|
-
data = MultiJson.load(request.body.read)
|
79
|
-
dispatch_external_event(task_id, data)
|
80
|
-
end
|
81
|
-
|
82
|
-
get "/tasks/operations" do
|
83
|
-
TaskLauncherRegistry.operations.to_json
|
84
|
-
end
|
85
|
-
|
86
|
-
private
|
87
|
-
|
88
|
-
def callback_host(params, request)
|
89
|
-
params.fetch('action_input', {})['proxy_url'] ||
|
90
|
-
request.env.values_at('HTTP_X_FORWARDED_FOR', 'HTTP_HOST').compact.first
|
91
|
-
end
|
92
|
-
|
93
|
-
def launcher_class(params)
|
94
|
-
operation = params.fetch('operation')
|
95
|
-
if TaskLauncherRegistry.key?(operation)
|
96
|
-
TaskLauncherRegistry.fetch(operation)
|
97
|
-
else
|
98
|
-
halt 404, MultiJson.dump(:error => "Unknown operation '#{operation}' requested.")
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
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
|
@@ -1,166 +0,0 @@
|
|
1
|
-
require 'webrick/https'
|
2
|
-
require 'smart_proxy_dynflow_core/bundler_helper'
|
3
|
-
require 'smart_proxy_dynflow_core/settings'
|
4
|
-
require 'sd_notify'
|
5
|
-
|
6
|
-
module SmartProxyDynflowCore
|
7
|
-
class Launcher
|
8
|
-
CIPHERS = ['ECDHE-RSA-AES128-GCM-SHA256', 'ECDHE-RSA-AES256-GCM-SHA384',
|
9
|
-
'AES128-GCM-SHA256', 'AES256-GCM-SHA384', 'AES128-SHA256',
|
10
|
-
'AES256-SHA256', 'AES128-SHA', 'AES256-SHA'].freeze
|
11
|
-
|
12
|
-
def self.launch!(options)
|
13
|
-
self.new.start options
|
14
|
-
end
|
15
|
-
|
16
|
-
def start(options)
|
17
|
-
Settings.instance.standalone = true
|
18
|
-
load_settings!(options)
|
19
|
-
install_usr1_trap
|
20
|
-
Rack::Server.new(rack_settings).start do |_server|
|
21
|
-
SmartProxyDynflowCore::Core.ensure_initialized
|
22
|
-
::SdNotify.ready
|
23
|
-
end
|
24
|
-
Log.instance.info "Finished shutting down"
|
25
|
-
Logging.shutdown
|
26
|
-
end
|
27
|
-
|
28
|
-
def load_settings!(options = {})
|
29
|
-
config_dir, one_config = options.values_at(:config_dir, :one_config)
|
30
|
-
possible_config_dirs = [
|
31
|
-
'/etc/smart_proxy_dynflow_core',
|
32
|
-
File.expand_path('~/.config/smart_proxy_dynflow_core'),
|
33
|
-
File.join(File.dirname(__FILE__), '..', '..', 'config'),
|
34
|
-
]
|
35
|
-
possible_config_dirs << config_dir if config_dir
|
36
|
-
BundlerHelper.require_groups(:default)
|
37
|
-
possible_config_dirs.reverse! if one_config
|
38
|
-
possible_config_dirs.select { |config_dir| File.directory? config_dir }.each do |config_dir|
|
39
|
-
break if load_config_dir(config_dir) && one_config
|
40
|
-
end
|
41
|
-
Settings.instance.daemonize = options[:daemonize] if options.key?(:daemonize)
|
42
|
-
Settings.instance.pid_file = options[:pid_file] if options.key?(:pid_file)
|
43
|
-
Settings.loaded!
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.route_mapping(rack_builder)
|
47
|
-
rack_builder.map '/console' do
|
48
|
-
run Core.web_console
|
49
|
-
end
|
50
|
-
|
51
|
-
rack_builder.map '/' do
|
52
|
-
run Api
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def install_usr1_trap
|
57
|
-
trap(:USR1) do
|
58
|
-
Log.reopen
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
private
|
63
|
-
|
64
|
-
def rack_settings
|
65
|
-
settings = if https_enabled?
|
66
|
-
Log.instance.debug "Using HTTPS"
|
67
|
-
https_app
|
68
|
-
else
|
69
|
-
Log.instance.debug "Using HTTP"
|
70
|
-
{}
|
71
|
-
end
|
72
|
-
settings.merge(base_settings)
|
73
|
-
end
|
74
|
-
|
75
|
-
def app
|
76
|
-
Rack::Builder.new do
|
77
|
-
SmartProxyDynflowCore::Launcher.route_mapping(self)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def base_settings
|
82
|
-
{
|
83
|
-
:app => app,
|
84
|
-
:Host => Settings.instance.listen,
|
85
|
-
:Port => Settings.instance.port,
|
86
|
-
:AccessLog => [],
|
87
|
-
:Logger => Log.instance,
|
88
|
-
:daemonize => Settings.instance.daemonize,
|
89
|
-
:pid => Settings.instance.daemonize && Settings.instance.pid_file,
|
90
|
-
:server => :webrick
|
91
|
-
}
|
92
|
-
end
|
93
|
-
|
94
|
-
def https_app
|
95
|
-
ssl_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
|
96
|
-
ssl_options |= OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE if defined?(OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE)
|
97
|
-
# This is required to disable SSLv3 on Ruby 1.8.7
|
98
|
-
ssl_options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2)
|
99
|
-
ssl_options |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3)
|
100
|
-
ssl_options |= OpenSSL::SSL::OP_NO_TLSv1 if defined?(OpenSSL::SSL::OP_NO_TLSv1)
|
101
|
-
ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_1 if defined?(OpenSSL::SSL::OP_NO_TLSv1_1)
|
102
|
-
|
103
|
-
Settings.instance.tls_disabled_versions&.each do |version|
|
104
|
-
constant = OpenSSL::SSL.const_get("OP_NO_TLSv#{version.to_s.tr('.', '_')}") rescue nil
|
105
|
-
|
106
|
-
if constant
|
107
|
-
Log.instance.info "TLSv#{version} will be disabled."
|
108
|
-
ssl_options |= constant
|
109
|
-
else
|
110
|
-
Log.instance.warn "TLSv#{version} was not found."
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
{
|
115
|
-
:SSLEnable => true,
|
116
|
-
:SSLVerifyClient => OpenSSL::SSL::VERIFY_PEER,
|
117
|
-
:SSLPrivateKey => ssl_private_key,
|
118
|
-
:SSLCertificate => ssl_certificate,
|
119
|
-
:SSLCACertificateFile => Settings.instance.ssl_ca_file,
|
120
|
-
:SSLCiphers => CIPHERS - SmartProxyDynflowCore::Settings.instance.ssl_disabled_ciphers,
|
121
|
-
:SSLOptions => ssl_options
|
122
|
-
}
|
123
|
-
end
|
124
|
-
# rubocop:enable Metrics/PerceivedComplexity
|
125
|
-
|
126
|
-
def https_enabled?
|
127
|
-
Settings.instance.use_https
|
128
|
-
end
|
129
|
-
|
130
|
-
def ssl_private_key
|
131
|
-
OpenSSL::PKey::RSA.new(File.read(Settings.instance.ssl_private_key))
|
132
|
-
rescue Exception => e
|
133
|
-
Log.instance.fatal "Unable to load private SSL key. Are the values "\
|
134
|
-
"correct in settings.yml and do permissions allow reading?: #{e}"
|
135
|
-
raise e
|
136
|
-
end
|
137
|
-
|
138
|
-
def ssl_certificate
|
139
|
-
OpenSSL::X509::Certificate.new(File.read(Settings.instance.ssl_certificate))
|
140
|
-
rescue Exception => e
|
141
|
-
Log.instance.fatal "Unable to load SSL certificate. Are the values " \
|
142
|
-
"correct in settings.yml and do permissions allow reading?: #{e}"
|
143
|
-
raise e
|
144
|
-
end
|
145
|
-
|
146
|
-
def load_config_dir(dir)
|
147
|
-
settings_yml = File.join(dir, 'settings.yml')
|
148
|
-
if File.exist? settings_yml
|
149
|
-
Log.instance.debug "Loading settings from #{dir}"
|
150
|
-
Settings.load_global_settings settings_yml
|
151
|
-
Dir[File.join(dir, 'settings.d', '*.yml')].each { |path| Settings.load_plugin_settings(path) }
|
152
|
-
true
|
153
|
-
end
|
154
|
-
ForemanTasksCore::SettingsLoader.settings_registry.each_key do |settings_keys|
|
155
|
-
settings = settings_keys.inject({}) do |h, settings_key|
|
156
|
-
if SETTINGS.plugins.key?(settings_key.to_s)
|
157
|
-
h.merge(SETTINGS.plugins[settings_key.to_s].to_h)
|
158
|
-
else
|
159
|
-
h
|
160
|
-
end
|
161
|
-
end
|
162
|
-
ForemanTasksCore::SettingsLoader.setup_settings(settings_keys.first, settings)
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
@@ -1,146 +0,0 @@
|
|
1
|
-
require 'logging'
|
2
|
-
|
3
|
-
module SmartProxyDynflowCore
|
4
|
-
class ReopenAppender < ::Logging::Appender
|
5
|
-
def initialize(name, logger, opts = {})
|
6
|
-
@reopen = false
|
7
|
-
@logger = logger
|
8
|
-
super(name, opts)
|
9
|
-
end
|
10
|
-
|
11
|
-
def set(status = true)
|
12
|
-
@reopen = status
|
13
|
-
end
|
14
|
-
|
15
|
-
def append(_event)
|
16
|
-
if @reopen
|
17
|
-
Logging.reopen
|
18
|
-
@reopen = false
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
class Log
|
24
|
-
BASE_LOG_SIZE = 1024 * 1024 # 1 MiB
|
25
|
-
LOGGER_NAME = 'dynflow-core'.freeze
|
26
|
-
|
27
|
-
begin
|
28
|
-
require 'syslog/logger'
|
29
|
-
@syslog_available = true
|
30
|
-
rescue LoadError
|
31
|
-
@syslog_available = false
|
32
|
-
end
|
33
|
-
|
34
|
-
class << self
|
35
|
-
def reload!
|
36
|
-
Logging.logger[LOGGER_NAME].appenders.each(&:close)
|
37
|
-
Logging.logger[LOGGER_NAME].clear_appenders
|
38
|
-
@logger = nil
|
39
|
-
instance
|
40
|
-
end
|
41
|
-
|
42
|
-
def reopen
|
43
|
-
return if @logger.nil? || @reopen.nil?
|
44
|
-
if Settings.instance.log_file !~ /^(STDOUT|SYSLOG|JOURNALD?)$/i
|
45
|
-
@reopen.set
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def instance
|
50
|
-
return ::Proxy::LogBuffer::Decorator.instance unless Settings.instance.standalone
|
51
|
-
return @logger if @logger
|
52
|
-
layout = Logging::Layouts.pattern(pattern: Settings.instance.file_logging_pattern + "\n")
|
53
|
-
notime_layout = Logging::Layouts.pattern(pattern: Settings.instance.system_logging_pattern + "\n")
|
54
|
-
log_file = Settings.instance.log_file || ''
|
55
|
-
@logger = Logging.logger[LOGGER_NAME]
|
56
|
-
@reopen = ReopenAppender.new("Reopen dummy appender", @logger)
|
57
|
-
@logger.add_appenders(@reopen)
|
58
|
-
if !Settings.instance.loaded || log_file.casecmp('STDOUT').zero?
|
59
|
-
@logger.add_appenders(Logging.appenders.stdout(LOGGER_NAME, layout: layout))
|
60
|
-
elsif log_file.casecmp('SYSLOG').zero?
|
61
|
-
unless @syslog_available
|
62
|
-
puts "Syslog is not supported on this platform, use STDOUT or a file"
|
63
|
-
exit(1)
|
64
|
-
end
|
65
|
-
@logger.add_appenders(Logging.appenders.syslog(LOGGER_NAME, layout: notime_layout, facility: ::Syslog::Constants::LOG_LOCAL5))
|
66
|
-
elsif log_file.casecmp('JOURNAL').zero? || log_file.casecmp('JOURNALD').zero?
|
67
|
-
begin
|
68
|
-
@logger.add_appenders(Logging.appenders.journald(LOGGER_NAME, LOGGER_NAME: :proxy_logger, layout: notime_layout, facility: ::Syslog::Constants::LOG_LOCAL5))
|
69
|
-
rescue NoMethodError
|
70
|
-
@logger.add_appenders(Logging.appenders.stdout(LOGGER_NAME, layout: layout))
|
71
|
-
@logger.warn "Journald is not available on this platform. Falling back to STDOUT."
|
72
|
-
end
|
73
|
-
else
|
74
|
-
begin
|
75
|
-
keep = Settings.instance.file_rolling_keep
|
76
|
-
size = BASE_LOG_SIZE * Settings.instance.file_rolling_size
|
77
|
-
age = Settings.instance.file_rolling_age
|
78
|
-
if size.positive?
|
79
|
-
@logger.add_appenders(Logging.appenders.rolling_file(LOGGER_NAME, layout: layout, filename: log_file, keep: keep, size: size, age: age, roll_by: 'number'))
|
80
|
-
else
|
81
|
-
@logger.add_appenders(Logging.appenders.file(LOGGER_NAME, layout: layout, filename: log_file))
|
82
|
-
end
|
83
|
-
rescue ArgumentError => ae
|
84
|
-
@logger.add_appenders(Logging.appenders.stdout(LOGGER_NAME, layout: layout))
|
85
|
-
@logger.warn "Log file #{log_file} cannot be opened. Falling back to STDOUT: #{ae}"
|
86
|
-
end
|
87
|
-
end
|
88
|
-
@logger.level = ::Logging.level_num(Settings.instance.log_level)
|
89
|
-
@logger
|
90
|
-
end
|
91
|
-
|
92
|
-
def with_fields(fields = {})
|
93
|
-
::Logging.ndc.push(fields) do
|
94
|
-
yield
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
# Standard way for logging exceptions to get the most data in the log. By default
|
99
|
-
# it logs via warn level, this can be changed via options[:level]
|
100
|
-
def exception(context_message, exception, options = {})
|
101
|
-
level = options[:level] || :warn
|
102
|
-
unless ::Logging::LEVELS.keys.include?(level.to_s)
|
103
|
-
raise "Unexpected log level #{level}, expected one of #{::Logging::LEVELS.keys}"
|
104
|
-
end
|
105
|
-
# send class, message and stack as structured fields in addition to message string
|
106
|
-
backtrace = exception.backtrace ? exception.backtrace : []
|
107
|
-
extra_fields = {
|
108
|
-
exception_class: exception.class.name,
|
109
|
-
exception_message: exception.message,
|
110
|
-
exception_backtrace: backtrace
|
111
|
-
}
|
112
|
-
extra_fields[:foreman_code] = exception.code if exception.respond_to?(:code)
|
113
|
-
with_fields(extra_fields) do
|
114
|
-
@logger.public_send(level) do
|
115
|
-
([context_message, "#{exception.class}: #{exception.message}"] + backtrace).join("\n")
|
116
|
-
end
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
class ProxyStructuredFormater < ::Dynflow::LoggerAdapters::Formatters::Abstract
|
122
|
-
def format(message)
|
123
|
-
if message.is_a?(Exception)
|
124
|
-
subject = "#{message.message} (#{message.class})"
|
125
|
-
if @base.respond_to?(:exception)
|
126
|
-
@base.exception("Error details", message)
|
127
|
-
subject
|
128
|
-
else
|
129
|
-
"#{subject}\n#{message.backtrace.join("\n")}"
|
130
|
-
end
|
131
|
-
else
|
132
|
-
@original_formatter.call(severity, datetime, prog_name, message)
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
class ProxyAdapter < ::Dynflow::LoggerAdapters::Simple
|
138
|
-
def initialize(logger, level = Logger::DEBUG, _formatters = [])
|
139
|
-
@logger = logger
|
140
|
-
@logger.level = level
|
141
|
-
@action_logger = apply_formatters(ProgNameWrapper.new(@logger, ' action'), [])
|
142
|
-
@dynflow_logger = apply_formatters(ProgNameWrapper.new(@logger, 'dynflow'), [])
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
@@ -1,31 +0,0 @@
|
|
1
|
-
module SmartProxyDynflowCore
|
2
|
-
class LoggerMiddleware
|
3
|
-
def initialize(app)
|
4
|
-
@logger = SmartProxyDynflowCore::Log.instance
|
5
|
-
@app = app
|
6
|
-
end
|
7
|
-
|
8
|
-
def call(env)
|
9
|
-
before = Time.now.to_f
|
10
|
-
status = 500
|
11
|
-
env['rack.logger'] = @logger
|
12
|
-
@logger.info { "Started #{env['REQUEST_METHOD']} #{env['PATH_INFO']} #{env['QUERY_STRING']}" }
|
13
|
-
@logger.debug { 'Headers: ' + env.select { |k, v| k.start_with? 'HTTP_' }.inspect }
|
14
|
-
if @logger.debug? && env['rack.input']
|
15
|
-
body = env['rack.input'].read
|
16
|
-
@logger.debug('Body: ' + body) unless body.empty?
|
17
|
-
env['rack.input'].rewind
|
18
|
-
end
|
19
|
-
status, = @app.call(env)
|
20
|
-
rescue Exception => e
|
21
|
-
Log.exception "Error processing request '#{::Logging.mdc['request']}", e
|
22
|
-
raise e
|
23
|
-
ensure
|
24
|
-
@logger.info do
|
25
|
-
after = Time.now.to_f
|
26
|
-
duration = (after - before) * 1000
|
27
|
-
"Finished #{env['REQUEST_METHOD']} #{env['PATH_INFO']} with #{status} (#{duration.round(2)} ms)"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
module Actions
|
2
|
-
module Middleware
|
3
|
-
class KeepCurrentRequestID < Dynflow::Middleware
|
4
|
-
def delay(*args)
|
5
|
-
pass(*args).tap { store_current_request_id }
|
6
|
-
end
|
7
|
-
|
8
|
-
def plan(*args)
|
9
|
-
with_current_request_id do
|
10
|
-
pass(*args).tap { store_current_request_id }
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def run(*args)
|
15
|
-
restore_current_request_id { pass(*args) }
|
16
|
-
end
|
17
|
-
|
18
|
-
def finalize
|
19
|
-
restore_current_request_id { pass }
|
20
|
-
end
|
21
|
-
|
22
|
-
# Run all execution plan lifecycle hooks as the original request_id
|
23
|
-
def hook(*args)
|
24
|
-
restore_current_request_id { pass(*args) }
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def with_current_request_id
|
30
|
-
if action.input[:current_request_id].nil?
|
31
|
-
yield
|
32
|
-
else
|
33
|
-
restore_current_request_id { yield }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def store_current_request_id
|
38
|
-
action.input[:current_request_id] = ::Logging.mdc['request']
|
39
|
-
end
|
40
|
-
|
41
|
-
def restore_current_request_id
|
42
|
-
unless (restored_id = action.input[:current_request_id]).nil?
|
43
|
-
old_id = ::Logging.mdc['request']
|
44
|
-
if !old_id.nil? && old_id != restored_id
|
45
|
-
action.action_logger.warn('Changing request id %{request_id} to saved id %{saved_id}' % { :saved_id => restored_id, :request_id => old_id })
|
46
|
-
end
|
47
|
-
::Logging.mdc['request'] = restored_id
|
48
|
-
end
|
49
|
-
yield
|
50
|
-
ensure
|
51
|
-
# Reset to original request id only when not nil
|
52
|
-
# Otherwise, keep the id until it's cleaned in Dynflow's run_user_code block
|
53
|
-
# so that it will stay valid for the rest of the processing of the current step
|
54
|
-
# (even outside of the middleware lifecycle)
|
55
|
-
::Logging.mdc['request'] = old_id unless old_id.nil?
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module SmartProxyDynflowCore
|
2
|
-
class RequestIdMiddleware
|
3
|
-
def initialize(app)
|
4
|
-
@app = app
|
5
|
-
end
|
6
|
-
|
7
|
-
def call(env)
|
8
|
-
::Logging.mdc['remote_ip'] = env['REMOTE_ADDR']
|
9
|
-
if env.has_key?('HTTP_X_REQUEST_ID')
|
10
|
-
::Logging.mdc['request'] = env['HTTP_X_REQUEST_ID']
|
11
|
-
else
|
12
|
-
::Logging.mdc['request'] = SecureRandom.uuid
|
13
|
-
end
|
14
|
-
status, header, body = @app.call(env)
|
15
|
-
[status, header, ::Rack::BodyProxy.new(body) { ::Logging.mdc.clear }]
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
require 'ostruct'
|
2
|
-
|
3
|
-
module SmartProxyDynflowCore
|
4
|
-
class Settings < OpenStruct
|
5
|
-
DEFAULT_SETTINGS = {
|
6
|
-
:database => '/var/lib/foreman-proxy/dynflow/dynflow.sqlite',
|
7
|
-
:foreman_url => 'https://127.0.0.1:3000',
|
8
|
-
:console_auth => true,
|
9
|
-
:listen => '127.0.0.1',
|
10
|
-
:port => '8008',
|
11
|
-
:use_https => false,
|
12
|
-
:ssl_ca_file => nil,
|
13
|
-
:ssl_private_key => nil,
|
14
|
-
:ssl_certificate => nil,
|
15
|
-
:ssl_disabled_ciphers => [],
|
16
|
-
:tls_disabled_versions => [],
|
17
|
-
:foreman_ssl_ca => nil,
|
18
|
-
:foreman_ssl_key => nil,
|
19
|
-
:foreman_ssl_cert => nil,
|
20
|
-
:standalone => false,
|
21
|
-
:log_file => '/var/log/foreman-proxy/smart_proxy_dynflow_core.log',
|
22
|
-
:log_level => :ERROR,
|
23
|
-
:plugins => {},
|
24
|
-
:pid_file => '/var/run/foreman-proxy/smart_proxy_dynflow_core.pid',
|
25
|
-
:daemonize => false,
|
26
|
-
:execution_plan_cleaner_age => 60 * 60 * 24,
|
27
|
-
:loaded => false,
|
28
|
-
:file_logging_pattern => '%d %.8X{request} [%.1l] %m',
|
29
|
-
:system_logging_pattern => '%.8X{request} [%.1l] %m',
|
30
|
-
:file_rolling_keep => 6,
|
31
|
-
:file_rolling_size => 0,
|
32
|
-
:file_rolling_age => 'weekly'
|
33
|
-
}.freeze
|
34
|
-
|
35
|
-
PROXY_SETTINGS = %i[ssl_ca_file ssl_certificate ssl_private_key foreman_url
|
36
|
-
foreman_ssl_ca foreman_ssl_cert foreman_ssl_key
|
37
|
-
log_file log_level ssl_disabled_ciphers].freeze
|
38
|
-
PLUGIN_SETTINGS = %i[database core_url console_auth
|
39
|
-
execution_plan_cleaner_age].freeze
|
40
|
-
|
41
|
-
def initialize(settings = {})
|
42
|
-
super(DEFAULT_SETTINGS.merge(settings))
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.instance
|
46
|
-
SmartProxyDynflowCore::SETTINGS
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.load_global_settings(path)
|
50
|
-
if File.exist? File.join(path)
|
51
|
-
YAML.load_file(path).each do |key, value|
|
52
|
-
SETTINGS[key] = value
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def self.loaded!
|
58
|
-
Settings.instance.loaded = true
|
59
|
-
Log.instance.info 'Settings loaded, reloading logger'
|
60
|
-
Log.reload!
|
61
|
-
end
|
62
|
-
|
63
|
-
def self.load_from_proxy(plugin)
|
64
|
-
settings = plugin.settings.to_h
|
65
|
-
PROXY_SETTINGS.each do |key|
|
66
|
-
SETTINGS[key] = Proxy::SETTINGS[key]
|
67
|
-
end
|
68
|
-
PLUGIN_SETTINGS.each do |key|
|
69
|
-
SETTINGS[key] = settings[key] if settings.key?(key)
|
70
|
-
end
|
71
|
-
SETTINGS.plugins.values.each(&:load_settings_from_proxy)
|
72
|
-
Settings.loaded!
|
73
|
-
end
|
74
|
-
|
75
|
-
def self.load_plugin_settings(path)
|
76
|
-
settings = YAML.load_file(path)
|
77
|
-
name = File.basename(path).gsub(/\.yml$/, '')
|
78
|
-
if SETTINGS.plugins.key? name
|
79
|
-
settings = SETTINGS.plugins[name].to_h.merge(settings || {})
|
80
|
-
end
|
81
|
-
SETTINGS.plugins[name] = OpenStruct.new settings
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
SmartProxyDynflowCore::SETTINGS = SmartProxyDynflowCore::Settings.new
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'dynflow/testing'
|
2
|
-
|
3
|
-
unless defined? DYNFLOW_TESTING_LOG_LEVEL
|
4
|
-
DYNFLOW_TESTING_LOG_LEVEL = 4
|
5
|
-
end
|
6
|
-
|
7
|
-
module SmartProxyDynflowCore
|
8
|
-
class Dynflow
|
9
|
-
# Helper for usage in other dependent plugins that need Dynflow
|
10
|
-
# related things, such as testing instance of world etc.
|
11
|
-
module Testing
|
12
|
-
class << self
|
13
|
-
def create_world(&block)
|
14
|
-
Core.ensure_initialized
|
15
|
-
Core.instance.create_world do |config|
|
16
|
-
config.exit_on_terminate = false
|
17
|
-
config.auto_terminate = false
|
18
|
-
config.logger_adapter = ::Dynflow::LoggerAdapters::Simple.new $stderr, DYNFLOW_TESTING_LOG_LEVEL
|
19
|
-
config.execution_plan_cleaner = nil
|
20
|
-
yield(config) if block
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
Concurrent.disable_at_exit_handlers!
|