smart_proxy_dynflow_core 0.2.3 → 0.3.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 +6 -20
- data/config/settings.yml.example +19 -3
- data/deploy/smart_proxy_dynflow_core.service +2 -3
- data/lib/smart_proxy_dynflow_core.rb +3 -2
- data/lib/smart_proxy_dynflow_core/api.rb +8 -0
- data/lib/smart_proxy_dynflow_core/bundler_helper.rb +0 -1
- data/lib/smart_proxy_dynflow_core/callback.rb +0 -1
- data/lib/smart_proxy_dynflow_core/core.rb +3 -1
- data/lib/smart_proxy_dynflow_core/launcher.rb +22 -15
- data/lib/smart_proxy_dynflow_core/log.rb +103 -43
- data/lib/smart_proxy_dynflow_core/logger_middleware.rb +31 -0
- data/lib/smart_proxy_dynflow_core/middleware/keep_current_request_id.rb +59 -0
- data/lib/smart_proxy_dynflow_core/request_id_middleware.rb +18 -0
- data/lib/smart_proxy_dynflow_core/settings.rb +7 -25
- data/lib/smart_proxy_dynflow_core/version.rb +1 -1
- data/smart_proxy_dynflow_core.gemspec +6 -4
- metadata +41 -11
- data/lib/smart_proxy_dynflow_core/webrick-patch.rb +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e99a82c7e0d25bbf1e386b234423881d4742f14e243fb68dfcafa6a625a47b44
|
4
|
+
data.tar.gz: a5ca245cf3085f1f4d24631d7ce6a45fc08419e5455116a6f8e3e2a4621138a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d4422e274f8672c7cbe8ba3dcdc33ea2568f5a25661de0f11fffedd70e75b0c9e29078b84e8162f467ff750b9f71028d20d2231931443b84a8140f7bce89774d
|
7
|
+
data.tar.gz: af183777ce4813da50fc734fcf8f6e873093d2d3e1b051f7e5d2e9b64d4d2d40e1a3adaa5626fec188c3577253fd7e068a056bbb85ad295f22171db5b8995ca6
|
data/Gemfile
CHANGED
@@ -10,28 +10,14 @@ group :test do
|
|
10
10
|
gem 'smart_proxy', :git => "https://github.com/theforeman/smart-proxy", :branch => "develop"
|
11
11
|
gem 'smart_proxy_dynflow', :path => '.'
|
12
12
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
else
|
17
|
-
gem 'public_suffix'
|
18
|
-
gem 'rubocop', '~> 0.52.1'
|
19
|
-
end
|
20
|
-
|
21
|
-
if RUBY_VERSION < '2.2'
|
22
|
-
gem 'rack-test', '< 0.8'
|
23
|
-
else
|
24
|
-
gem 'rack-test'
|
25
|
-
end
|
13
|
+
gem 'public_suffix'
|
14
|
+
gem 'rack-test'
|
15
|
+
gem 'rubocop', '~> 0.52.1'
|
26
16
|
end
|
27
17
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
else
|
32
|
-
gem 'rack', '>= 1.1'
|
33
|
-
gem 'sinatra'
|
34
|
-
end
|
18
|
+
gem 'logging-journald', '~> 2.0', :platforms => [:ruby]
|
19
|
+
gem 'rack', '>= 1.1'
|
20
|
+
gem 'sinatra'
|
35
21
|
|
36
22
|
# load bundler.d
|
37
23
|
Dir["#{File.dirname(__FILE__)}/bundler.d/*.rb"].each do |bundle|
|
data/config/settings.yml.example
CHANGED
@@ -39,11 +39,27 @@
|
|
39
39
|
# Specify versions like: '1.1', or '1.2'
|
40
40
|
#:tls_disabled_versions: []
|
41
41
|
|
42
|
-
# File to log to, leave empty for
|
43
|
-
|
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
44
|
|
45
45
|
# Log level, one of UNKNOWN, FATAL, ERROR, WARN, INFO, DEBUG
|
46
|
-
|
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'
|
47
63
|
|
48
64
|
# Maximum age of execution plans to keep before having them cleaned
|
49
65
|
# by the execution plan cleaner (in seconds), defaults to 24 hours
|
@@ -4,10 +4,9 @@ Documentation=https://github.com/theforeman/smart_proxy_dynflow
|
|
4
4
|
After=network.target remote-fs.target nss-lookup.target
|
5
5
|
|
6
6
|
[Service]
|
7
|
-
Type=
|
7
|
+
Type=notify
|
8
8
|
User=foreman-proxy
|
9
|
-
|
10
|
-
ExecStart=/usr/bin/smart_proxy_dynflow_core -d -p /var/run/foreman-proxy/smart_proxy_dynflow_core.pid
|
9
|
+
ExecStart=/usr/bin/smart_proxy_dynflow_core --no-daemonize
|
11
10
|
EnvironmentFile=-/etc/sysconfig/smart_proxy_dynflow_core
|
12
11
|
|
13
12
|
[Install]
|
@@ -1,6 +1,7 @@
|
|
1
|
-
raise LoadError, 'Ruby >= 2.1 is required' unless RUBY_VERSION >= '2.1'
|
2
|
-
|
3
1
|
require 'dynflow'
|
2
|
+
require 'smart_proxy_dynflow_core/request_id_middleware'
|
3
|
+
require 'smart_proxy_dynflow_core/logger_middleware'
|
4
|
+
require 'smart_proxy_dynflow_core/middleware/keep_current_request_id'
|
4
5
|
require 'smart_proxy_dynflow_core/task_launcher_registry'
|
5
6
|
require 'foreman_tasks_core'
|
6
7
|
require 'smart_proxy_dynflow_core/log'
|
@@ -6,6 +6,14 @@ module SmartProxyDynflowCore
|
|
6
6
|
TASK_UPDATE_REGEXP_PATH = %r{/tasks/(\S+)/(update|done)}
|
7
7
|
helpers Helpers
|
8
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
|
+
|
9
17
|
before do
|
10
18
|
if match = request.path_info.match(TASK_UPDATE_REGEXP_PATH)
|
11
19
|
task_id = match[1]
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module SmartProxyDynflowCore
|
2
2
|
class BundlerHelper
|
3
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
4
3
|
def self.require_groups(*groups)
|
5
4
|
if File.exist?(File.expand_path('../../../Gemfile.in', __FILE__))
|
6
5
|
# If there is a Gemfile.in file, we will not use Bundler but BundlerExt
|
@@ -15,7 +15,9 @@ module SmartProxyDynflowCore
|
|
15
15
|
|
16
16
|
def create_world(&block)
|
17
17
|
config = default_world_config(&block)
|
18
|
-
::Dynflow::World.new(config)
|
18
|
+
world = ::Dynflow::World.new(config)
|
19
|
+
world.middleware.use ::Actions::Middleware::KeepCurrentRequestID
|
20
|
+
world
|
19
21
|
end
|
20
22
|
|
21
23
|
def persistence_conn_string
|
@@ -1,20 +1,28 @@
|
|
1
1
|
require 'webrick/https'
|
2
2
|
require 'smart_proxy_dynflow_core/bundler_helper'
|
3
3
|
require 'smart_proxy_dynflow_core/settings'
|
4
|
-
require '
|
4
|
+
require 'sd_notify'
|
5
|
+
|
5
6
|
module SmartProxyDynflowCore
|
6
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
|
+
|
7
12
|
def self.launch!(options)
|
8
13
|
self.new.start options
|
9
14
|
end
|
10
15
|
|
11
16
|
def start(options)
|
12
|
-
load_settings!(options)
|
13
17
|
Settings.instance.standalone = true
|
18
|
+
load_settings!(options)
|
14
19
|
install_usr1_trap
|
15
20
|
Rack::Server.new(rack_settings).start do |_server|
|
16
21
|
SmartProxyDynflowCore::Core.ensure_initialized
|
22
|
+
::SdNotify.ready
|
17
23
|
end
|
24
|
+
Log.instance.info "Finished shutting down"
|
25
|
+
Logging.shutdown
|
18
26
|
end
|
19
27
|
|
20
28
|
def load_settings!(options = {})
|
@@ -47,7 +55,7 @@ module SmartProxyDynflowCore
|
|
47
55
|
|
48
56
|
def install_usr1_trap
|
49
57
|
trap(:USR1) do
|
50
|
-
Log.
|
58
|
+
Log.reopen
|
51
59
|
end
|
52
60
|
end
|
53
61
|
|
@@ -75,15 +83,14 @@ module SmartProxyDynflowCore
|
|
75
83
|
:app => app,
|
76
84
|
:Host => Settings.instance.listen,
|
77
85
|
:Port => Settings.instance.port,
|
78
|
-
:AccessLog => [
|
86
|
+
:AccessLog => [],
|
79
87
|
:Logger => Log.instance,
|
80
88
|
:daemonize => Settings.instance.daemonize,
|
81
|
-
:pid => Settings.instance.pid_file,
|
89
|
+
:pid => Settings.instance.daemonize && Settings.instance.pid_file,
|
82
90
|
:server => :webrick
|
83
91
|
}
|
84
92
|
end
|
85
93
|
|
86
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
87
94
|
def https_app
|
88
95
|
ssl_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
|
89
96
|
ssl_options |= OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE if defined?(OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE)
|
@@ -91,17 +98,16 @@ module SmartProxyDynflowCore
|
|
91
98
|
ssl_options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2)
|
92
99
|
ssl_options |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3)
|
93
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)
|
94
102
|
|
95
|
-
|
96
|
-
|
97
|
-
constant = OpenSSL::SSL.const_get("OP_NO_TLSv#{version.to_s.tr('.', '_')}") rescue nil
|
103
|
+
Settings.instance.tls_disabled_versions&.each do |version|
|
104
|
+
constant = OpenSSL::SSL.const_get("OP_NO_TLSv#{version.to_s.tr('.', '_')}") rescue nil
|
98
105
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end
|
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."
|
105
111
|
end
|
106
112
|
end
|
107
113
|
|
@@ -111,6 +117,7 @@ module SmartProxyDynflowCore
|
|
111
117
|
:SSLPrivateKey => ssl_private_key,
|
112
118
|
:SSLCertificate => ssl_certificate,
|
113
119
|
:SSLCACertificateFile => Settings.instance.ssl_ca_file,
|
120
|
+
:SSLCiphers => CIPHERS - SmartProxyDynflowCore::Settings.instance.ssl_disabled_ciphers,
|
114
121
|
:SSLOptions => ssl_options
|
115
122
|
}
|
116
123
|
end
|
@@ -1,61 +1,126 @@
|
|
1
|
-
require '
|
1
|
+
require 'logging'
|
2
2
|
|
3
3
|
module SmartProxyDynflowCore
|
4
|
-
class
|
5
|
-
|
4
|
+
class ReopenAppender < ::Logging::Appender
|
5
|
+
def initialize(name, logger, opts = {})
|
6
|
+
@reopen = false
|
7
|
+
@logger = logger
|
8
|
+
super(name, opts)
|
9
|
+
end
|
6
10
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
@logger = self.new log_file
|
11
|
-
@logger.level = log_level
|
12
|
-
end
|
13
|
-
@logger
|
14
|
-
end
|
11
|
+
def set(status = true)
|
12
|
+
@reopen = status
|
13
|
+
end
|
15
14
|
|
16
|
-
|
17
|
-
|
15
|
+
def append(_event)
|
16
|
+
if @reopen
|
17
|
+
Logging.reopen
|
18
|
+
@reopen = false
|
18
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
|
19
33
|
|
34
|
+
class << self
|
20
35
|
def reload!
|
36
|
+
Logging.logger[LOGGER_NAME].appenders.each(&:close)
|
37
|
+
Logging.logger[LOGGER_NAME].clear_appenders
|
21
38
|
@logger = nil
|
22
39
|
instance
|
23
40
|
end
|
24
41
|
|
25
|
-
def
|
26
|
-
if
|
27
|
-
|
28
|
-
|
29
|
-
Logger::WARN
|
42
|
+
def reopen
|
43
|
+
return if @logger.nil? || @reopen.nil?
|
44
|
+
if Settings.instance.log_file !~ /^(STDOUT|SYSLOG|JOURNALD?)$/i
|
45
|
+
@reopen.set
|
30
46
|
end
|
31
47
|
end
|
32
48
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
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
|
36
73
|
else
|
37
|
-
|
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
|
38
87
|
end
|
88
|
+
@logger.level = ::Logging.level_num(Settings.instance.log_level)
|
89
|
+
@logger
|
39
90
|
end
|
40
|
-
end
|
41
91
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
92
|
+
def with_fields(fields = {})
|
93
|
+
::Logging.ndc.push(fields) do
|
94
|
+
yield
|
95
|
+
end
|
96
|
+
end
|
48
97
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
53
118
|
end
|
54
119
|
end
|
55
120
|
|
56
121
|
class ProxyStructuredFormater < ::Dynflow::LoggerAdapters::Formatters::Abstract
|
57
|
-
def
|
58
|
-
if message.is_a?(
|
122
|
+
def format(message)
|
123
|
+
if message.is_a?(Exception)
|
59
124
|
subject = "#{message.message} (#{message.class})"
|
60
125
|
if @base.respond_to?(:exception)
|
61
126
|
@base.exception("Error details", message)
|
@@ -64,22 +129,17 @@ module SmartProxyDynflowCore
|
|
64
129
|
"#{subject}\n#{message.backtrace.join("\n")}"
|
65
130
|
end
|
66
131
|
else
|
67
|
-
message
|
132
|
+
@original_formatter.call(severity, datetime, prog_name, message)
|
68
133
|
end
|
69
134
|
end
|
70
|
-
|
71
|
-
def format(message)
|
72
|
-
call(nil, nil, nil, message)
|
73
|
-
end
|
74
135
|
end
|
75
136
|
|
76
137
|
class ProxyAdapter < ::Dynflow::LoggerAdapters::Simple
|
77
138
|
def initialize(logger, level = Logger::DEBUG, _formatters = [])
|
78
139
|
@logger = logger
|
79
140
|
@logger.level = level
|
80
|
-
@
|
81
|
-
@
|
82
|
-
@dynflow_logger = apply_formatters(ProgNameWrapper.new(@logger, 'dynflow'), [ProxyStructuredFormater])
|
141
|
+
@action_logger = apply_formatters(ProgNameWrapper.new(@logger, ' action'), [])
|
142
|
+
@dynflow_logger = apply_formatters(ProgNameWrapper.new(@logger, 'dynflow'), [])
|
83
143
|
end
|
84
144
|
end
|
85
145
|
end
|
@@ -0,0 +1,31 @@
|
|
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
|
@@ -0,0 +1,59 @@
|
|
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
|
@@ -0,0 +1,18 @@
|
|
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,22 +1,5 @@
|
|
1
1
|
require 'ostruct'
|
2
2
|
|
3
|
-
# Implement hash-like access for 1.9.3 and older
|
4
|
-
if RUBY_VERSION.split('.').first.to_i < 2
|
5
|
-
class OpenStruct
|
6
|
-
def [](key)
|
7
|
-
self.send key
|
8
|
-
end
|
9
|
-
|
10
|
-
def []=(key, value)
|
11
|
-
self.send "#{key}=", value
|
12
|
-
end
|
13
|
-
|
14
|
-
def to_h
|
15
|
-
marshal_dump
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
3
|
module SmartProxyDynflowCore
|
21
4
|
class Settings < OpenStruct
|
22
5
|
DEFAULT_SETTINGS = {
|
@@ -41,7 +24,12 @@ module SmartProxyDynflowCore
|
|
41
24
|
:pid_file => '/var/run/foreman-proxy/smart_proxy_dynflow_core.pid',
|
42
25
|
:daemonize => false,
|
43
26
|
:execution_plan_cleaner_age => 60 * 60 * 24,
|
44
|
-
:loaded => false
|
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'
|
45
33
|
}.freeze
|
46
34
|
|
47
35
|
PROXY_SETTINGS = %i[ssl_ca_file ssl_certificate ssl_private_key foreman_url
|
@@ -73,13 +61,7 @@ module SmartProxyDynflowCore
|
|
73
61
|
end
|
74
62
|
|
75
63
|
def self.load_from_proxy(plugin)
|
76
|
-
|
77
|
-
plugin
|
78
|
-
else
|
79
|
-
# DEPRECATION: Remove this branch when dropping support for smart-proxy < 1.16
|
80
|
-
plugin[:class]
|
81
|
-
end
|
82
|
-
settings = plugin_class.settings.to_h
|
64
|
+
settings = plugin.settings.to_h
|
83
65
|
PROXY_SETTINGS.each do |key|
|
84
66
|
SETTINGS[key] = Proxy::SETTINGS[key]
|
85
67
|
end
|
@@ -2,7 +2,6 @@ lib = File.expand_path('../lib', __FILE__)
|
|
2
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
3
|
require 'smart_proxy_dynflow_core/version'
|
4
4
|
|
5
|
-
# rubocop:disable Metrics/BlockLength
|
6
5
|
Gem::Specification.new do |gem|
|
7
6
|
gem.name = "smart_proxy_dynflow_core"
|
8
7
|
gem.version = SmartProxyDynflowCore::VERSION
|
@@ -16,13 +15,15 @@ Gem::Specification.new do |gem|
|
|
16
15
|
|
17
16
|
gem.executables = ['smart_proxy_dynflow_core']
|
18
17
|
gem.files = Dir['lib/smart_proxy_dynflow_core.rb', 'config/settings.yml.example',
|
19
|
-
'lib/smart_proxy_dynflow_core
|
18
|
+
'lib/smart_proxy_dynflow_core/**/*', 'LICENSE', 'Gemfile',
|
20
19
|
'bin/smart_proxy_dynflow_core', 'deploy/*', 'smart_proxy_dynflow_core.gemspec']
|
21
20
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
22
21
|
gem.require_paths = ["lib"]
|
23
22
|
gem.license = 'GPL-3.0'
|
24
23
|
|
25
|
-
gem.
|
24
|
+
gem.required_ruby_version = '~> 2.5'
|
25
|
+
|
26
|
+
gem.add_development_dependency "bundler", ">= 1.7"
|
26
27
|
gem.add_development_dependency('minitest')
|
27
28
|
gem.add_development_dependency('mocha', '~> 1')
|
28
29
|
gem.add_development_dependency('rack-test', '~> 0')
|
@@ -31,10 +32,11 @@ Gem::Specification.new do |gem|
|
|
31
32
|
|
32
33
|
gem.add_runtime_dependency('dynflow', "~> 1.1")
|
33
34
|
gem.add_runtime_dependency('foreman-tasks-core', '>= 0.3.3')
|
35
|
+
gem.add_runtime_dependency('logging')
|
34
36
|
gem.add_runtime_dependency('rack')
|
35
37
|
gem.add_runtime_dependency('rest-client')
|
38
|
+
gem.add_runtime_dependency('sd_notify', '~> 0.1')
|
36
39
|
gem.add_runtime_dependency('sequel')
|
37
40
|
gem.add_runtime_dependency('sinatra')
|
38
41
|
gem.add_runtime_dependency('sqlite3')
|
39
42
|
end
|
40
|
-
# rubocop:enable Metrics/BlockLength
|
metadata
CHANGED
@@ -1,27 +1,27 @@
|
|
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.3.1
|
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:
|
11
|
+
date: 1980-01-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '1.7'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.7'
|
27
27
|
- !ruby/object:Gem::Dependency
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: 0.3.3
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: logging
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: rack
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -150,6 +164,20 @@ dependencies:
|
|
150
164
|
- - ">="
|
151
165
|
- !ruby/object:Gem::Version
|
152
166
|
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: sd_notify
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - "~>"
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0.1'
|
174
|
+
type: :runtime
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - "~>"
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0.1'
|
153
181
|
- !ruby/object:Gem::Dependency
|
154
182
|
name: sequel
|
155
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -214,33 +242,35 @@ files:
|
|
214
242
|
- lib/smart_proxy_dynflow_core/helpers.rb
|
215
243
|
- lib/smart_proxy_dynflow_core/launcher.rb
|
216
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
|
217
248
|
- lib/smart_proxy_dynflow_core/settings.rb
|
218
249
|
- lib/smart_proxy_dynflow_core/task_launcher_registry.rb
|
219
250
|
- lib/smart_proxy_dynflow_core/testing.rb
|
220
251
|
- lib/smart_proxy_dynflow_core/version.rb
|
221
|
-
- lib/smart_proxy_dynflow_core/webrick-patch.rb
|
222
252
|
- smart_proxy_dynflow_core.gemspec
|
223
253
|
homepage: https://github.com/theforeman/smart_proxy_dynflow
|
224
254
|
licenses:
|
225
255
|
- GPL-3.0
|
226
256
|
metadata: {}
|
227
|
-
post_install_message:
|
257
|
+
post_install_message:
|
228
258
|
rdoc_options: []
|
229
259
|
require_paths:
|
230
260
|
- lib
|
231
261
|
required_ruby_version: !ruby/object:Gem::Requirement
|
232
262
|
requirements:
|
233
|
-
- - "
|
263
|
+
- - "~>"
|
234
264
|
- !ruby/object:Gem::Version
|
235
|
-
version: '
|
265
|
+
version: '2.5'
|
236
266
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
237
267
|
requirements:
|
238
268
|
- - ">="
|
239
269
|
- !ruby/object:Gem::Version
|
240
270
|
version: '0'
|
241
271
|
requirements: []
|
242
|
-
rubygems_version: 3.
|
243
|
-
signing_key:
|
272
|
+
rubygems_version: 3.1.2
|
273
|
+
signing_key:
|
244
274
|
specification_version: 4
|
245
275
|
summary: Dynflow runtime for Foreman smart proxy
|
246
276
|
test_files: []
|
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'webrick/https'
|
2
|
-
|
3
|
-
CIPHERS = ['ECDHE-RSA-AES128-GCM-SHA256', 'ECDHE-RSA-AES256-GCM-SHA384',
|
4
|
-
'ECDHE-RSA-AES128-CBC-SHA', 'ECDHE-RSA-AES256-CBC-SHA',
|
5
|
-
'AES128-GCM-SHA256', 'AES256-GCM-SHA384', 'AES128-SHA256',
|
6
|
-
'AES256-SHA256', 'AES128-SHA', 'AES256-SHA'].freeze
|
7
|
-
|
8
|
-
module WEBrick
|
9
|
-
class GenericServer
|
10
|
-
# rubocop:disable Metrics/AbcSize
|
11
|
-
def setup_ssl_context(config) # :nodoc:
|
12
|
-
unless config[:SSLCertificate]
|
13
|
-
cn = config[:SSLCertName]
|
14
|
-
comment = config[:SSLCertComment]
|
15
|
-
cert, key = Utils.create_self_signed_cert(1024, cn, comment)
|
16
|
-
config[:SSLCertificate] = cert
|
17
|
-
config[:SSLPrivateKey] = key
|
18
|
-
end
|
19
|
-
ctx = OpenSSL::SSL::SSLContext.new
|
20
|
-
ctx.set_params
|
21
|
-
ctx.ciphers = (CIPHERS - SmartProxyDynflowCore::Settings.instance.ssl_disabled_ciphers).join(':')
|
22
|
-
ctx.key = config[:SSLPrivateKey]
|
23
|
-
ctx.cert = config[:SSLCertificate]
|
24
|
-
ctx.client_ca = config[:SSLClientCA]
|
25
|
-
ctx.extra_chain_cert = config[:SSLExtraChainCert]
|
26
|
-
ctx.ca_file = config[:SSLCACertificateFile]
|
27
|
-
ctx.ca_path = config[:SSLCACertificatePath]
|
28
|
-
ctx.cert_store = config[:SSLCertificateStore]
|
29
|
-
ctx.tmp_dh_callback = config[:SSLTmpDhCallback]
|
30
|
-
ctx.verify_mode = config[:SSLVerifyClient]
|
31
|
-
ctx.verify_depth = config[:SSLVerifyDepth]
|
32
|
-
ctx.verify_callback = config[:SSLVerifyCallback]
|
33
|
-
ctx.timeout = config[:SSLTimeout]
|
34
|
-
ctx.options |= config[:SSLOptions] unless config[:SSLOptions].nil?
|
35
|
-
ctx
|
36
|
-
end
|
37
|
-
# rubocop:enable Metrics/AbcSize
|
38
|
-
end
|
39
|
-
end
|