smart_proxy_dynflow_core 0.2.5 → 0.3.3
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 +22 -1
- 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 +15 -14
- 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 +8 -26
- data/lib/smart_proxy_dynflow_core/version.rb +1 -1
- data/smart_proxy_dynflow_core.gemspec +5 -3
- metadata +39 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c8d92f97c019f9624f042ba66a48f3121b341b1c5f9cfdb3fe5a35a4f83f074
|
4
|
+
data.tar.gz: 9707da718ef1d2d042dd4bade34e2b6d869236058bd9bf0bfcea07011862cf16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 45ced7ab76fefbdbea72fceee20b9b7c77c9394464045d7bff56cccb2882a9fa89f2d01a858e9b2aecd6a29c09f20388b3775873b8bcd65bfdb836a540b0a848
|
7
|
+
data.tar.gz: 4a08ac7bf67111e7644071d2627d341d741496282b13ae134014a8b74956ac1310f397124ea49a9468fc00bf051fd524cba244feae7fc17b050b91cfa14e3048
|
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], :require => false
|
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'
|
@@ -1,18 +1,39 @@
|
|
1
1
|
require 'sinatra/base'
|
2
2
|
require 'multi_json'
|
3
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
|
+
|
4
13
|
module SmartProxyDynflowCore
|
5
14
|
class Api < ::Sinatra::Base
|
6
15
|
TASK_UPDATE_REGEXP_PATH = %r{/tasks/(\S+)/(update|done)}
|
7
16
|
helpers Helpers
|
8
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
|
+
|
9
28
|
before do
|
10
29
|
if match = request.path_info.match(TASK_UPDATE_REGEXP_PATH)
|
11
30
|
task_id = match[1]
|
12
31
|
action = match[2]
|
13
32
|
authorize_with_token(task_id: task_id, clear: action == 'done')
|
14
|
-
|
33
|
+
elsif Settings.instance.standalone
|
15
34
|
authorize_with_ssl_client
|
35
|
+
else
|
36
|
+
do_authorize_any
|
16
37
|
end
|
17
38
|
content_type :json
|
18
39
|
end
|
@@ -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,6 +1,7 @@
|
|
1
1
|
require 'webrick/https'
|
2
2
|
require 'smart_proxy_dynflow_core/bundler_helper'
|
3
3
|
require 'smart_proxy_dynflow_core/settings'
|
4
|
+
require 'sd_notify'
|
4
5
|
|
5
6
|
module SmartProxyDynflowCore
|
6
7
|
class Launcher
|
@@ -13,12 +14,15 @@ module SmartProxyDynflowCore
|
|
13
14
|
end
|
14
15
|
|
15
16
|
def start(options)
|
16
|
-
load_settings!(options)
|
17
17
|
Settings.instance.standalone = true
|
18
|
+
load_settings!(options)
|
18
19
|
install_usr1_trap
|
19
20
|
Rack::Server.new(rack_settings).start do |_server|
|
20
21
|
SmartProxyDynflowCore::Core.ensure_initialized
|
22
|
+
::SdNotify.ready
|
21
23
|
end
|
24
|
+
Log.instance.info "Finished shutting down"
|
25
|
+
Logging.shutdown
|
22
26
|
end
|
23
27
|
|
24
28
|
def load_settings!(options = {})
|
@@ -51,7 +55,7 @@ module SmartProxyDynflowCore
|
|
51
55
|
|
52
56
|
def install_usr1_trap
|
53
57
|
trap(:USR1) do
|
54
|
-
Log.
|
58
|
+
Log.reopen
|
55
59
|
end
|
56
60
|
end
|
57
61
|
|
@@ -79,15 +83,14 @@ module SmartProxyDynflowCore
|
|
79
83
|
:app => app,
|
80
84
|
:Host => Settings.instance.listen,
|
81
85
|
:Port => Settings.instance.port,
|
82
|
-
:AccessLog => [
|
86
|
+
:AccessLog => [],
|
83
87
|
:Logger => Log.instance,
|
84
88
|
:daemonize => Settings.instance.daemonize,
|
85
|
-
:pid => Settings.instance.pid_file,
|
89
|
+
:pid => Settings.instance.daemonize && Settings.instance.pid_file,
|
86
90
|
:server => :webrick
|
87
91
|
}
|
88
92
|
end
|
89
93
|
|
90
|
-
# rubocop:disable Metrics/PerceivedComplexity
|
91
94
|
def https_app
|
92
95
|
ssl_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
|
93
96
|
ssl_options |= OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE if defined?(OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE)
|
@@ -97,16 +100,14 @@ module SmartProxyDynflowCore
|
|
97
100
|
ssl_options |= OpenSSL::SSL::OP_NO_TLSv1 if defined?(OpenSSL::SSL::OP_NO_TLSv1)
|
98
101
|
ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_1 if defined?(OpenSSL::SSL::OP_NO_TLSv1_1)
|
99
102
|
|
100
|
-
|
101
|
-
|
102
|
-
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
|
103
105
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
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."
|
110
111
|
end
|
111
112
|
end
|
112
113
|
|
@@ -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
|
@@ -94,7 +76,7 @@ module SmartProxyDynflowCore
|
|
94
76
|
settings = YAML.load_file(path)
|
95
77
|
name = File.basename(path).gsub(/\.yml$/, '')
|
96
78
|
if SETTINGS.plugins.key? name
|
97
|
-
settings = SETTINGS.plugins[name].to_h.merge(settings)
|
79
|
+
settings = SETTINGS.plugins[name].to_h.merge(settings || {})
|
98
80
|
end
|
99
81
|
SETTINGS.plugins[name] = OpenStruct.new settings
|
100
82
|
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,12 +15,14 @@ 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
|
|
24
|
+
gem.required_ruby_version = '~> 2.5'
|
25
|
+
|
25
26
|
gem.add_development_dependency "bundler", ">= 1.7"
|
26
27
|
gem.add_development_dependency('minitest')
|
27
28
|
gem.add_development_dependency('mocha', '~> 1')
|
@@ -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,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.3.3
|
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: 2021-05-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -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,6 +242,9 @@ 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
|
@@ -223,23 +254,23 @@ homepage: https://github.com/theforeman/smart_proxy_dynflow
|
|
223
254
|
licenses:
|
224
255
|
- GPL-3.0
|
225
256
|
metadata: {}
|
226
|
-
post_install_message:
|
257
|
+
post_install_message:
|
227
258
|
rdoc_options: []
|
228
259
|
require_paths:
|
229
260
|
- lib
|
230
261
|
required_ruby_version: !ruby/object:Gem::Requirement
|
231
262
|
requirements:
|
232
|
-
- - "
|
263
|
+
- - "~>"
|
233
264
|
- !ruby/object:Gem::Version
|
234
|
-
version: '
|
265
|
+
version: '2.5'
|
235
266
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
236
267
|
requirements:
|
237
268
|
- - ">="
|
238
269
|
- !ruby/object:Gem::Version
|
239
270
|
version: '0'
|
240
271
|
requirements: []
|
241
|
-
rubygems_version: 3.
|
242
|
-
signing_key:
|
272
|
+
rubygems_version: 3.1.2
|
273
|
+
signing_key:
|
243
274
|
specification_version: 4
|
244
275
|
summary: Dynflow runtime for Foreman smart proxy
|
245
276
|
test_files: []
|