smart_proxy_dynflow_core 0.2.1 → 0.2.6
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 +5 -5
- data/Gemfile +4 -5
- data/deploy/smart_proxy_dynflow_core.service +2 -3
- data/lib/smart_proxy_dynflow_core.rb +8 -3
- data/lib/smart_proxy_dynflow_core/api.rb +33 -5
- data/lib/smart_proxy_dynflow_core/bundler_helper.rb +2 -0
- data/lib/smart_proxy_dynflow_core/callback.rb +37 -39
- data/lib/smart_proxy_dynflow_core/core.rb +3 -3
- data/lib/smart_proxy_dynflow_core/helpers.rb +5 -4
- data/lib/smart_proxy_dynflow_core/launcher.rb +17 -6
- data/lib/smart_proxy_dynflow_core/log.rb +26 -7
- data/lib/smart_proxy_dynflow_core/sd_notify.rb +33 -0
- data/lib/smart_proxy_dynflow_core/settings.rb +30 -31
- data/lib/smart_proxy_dynflow_core/task_launcher_registry.rb +31 -0
- data/lib/smart_proxy_dynflow_core/testing.rb +1 -1
- data/lib/smart_proxy_dynflow_core/version.rb +1 -1
- data/smart_proxy_dynflow_core.gemspec +10 -10
- metadata +25 -39
- data/lib/smart_proxy_dynflow_core/webrick-patch.rb +0 -37
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 84ea792e8e6d9f6ddcd145428485d5faadf92da27acd184fa42956fa4873e7fd
|
4
|
+
data.tar.gz: 07adc8b3c623462086d0797455afaf822e27212bcdcda345a950df7e49296814
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 989f9619b041039a3897de03cac1edc5ce1415a19792b3a5b2e907b174b97472b3a56f19cd5cab9977c26e5d5c83c38636afe8c49186dbd2bfce1c7e3dbfcc84
|
7
|
+
data.tar.gz: 7caa3ee4ddce3560970bd8f7a7b8e2b99dd96f1fae456e78475673ec98478e0c608e5feb29442b7046e4eed07e61f0d9a009dade6646e534f6ef6f033192f572
|
data/Gemfile
CHANGED
@@ -7,16 +7,15 @@ group :development do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
group :test do
|
10
|
-
gem 'smart_proxy_dynflow', :path => '.'
|
11
10
|
gem 'smart_proxy', :git => "https://github.com/theforeman/smart-proxy", :branch => "develop"
|
11
|
+
gem 'smart_proxy_dynflow', :path => '.'
|
12
12
|
|
13
13
|
if RUBY_VERSION < '2.1'
|
14
14
|
gem 'public_suffix', '< 3'
|
15
|
-
gem 'rubocop', '< 0.51.0'
|
16
15
|
gem 'rainbow', '< 3'
|
17
16
|
else
|
18
|
-
gem 'rubocop', '~> 0.52.1'
|
19
17
|
gem 'public_suffix'
|
18
|
+
gem 'rubocop', '~> 0.52.1'
|
20
19
|
end
|
21
20
|
|
22
21
|
if RUBY_VERSION < '2.2'
|
@@ -27,11 +26,11 @@ group :test do
|
|
27
26
|
end
|
28
27
|
|
29
28
|
if RUBY_VERSION < '2.2'
|
30
|
-
gem 'sinatra', '< 2'
|
31
29
|
gem 'rack', '>= 1.1', '< 2.0.0'
|
30
|
+
gem 'sinatra', '< 2'
|
32
31
|
else
|
33
|
-
gem 'sinatra'
|
34
32
|
gem 'rack', '>= 1.1'
|
33
|
+
gem 'sinatra'
|
35
34
|
end
|
36
35
|
|
37
36
|
# load bundler.d
|
@@ -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,4 +1,7 @@
|
|
1
|
+
raise LoadError, 'Ruby >= 2.1 is required' unless RUBY_VERSION >= '2.1'
|
2
|
+
|
1
3
|
require 'dynflow'
|
4
|
+
require 'smart_proxy_dynflow_core/task_launcher_registry'
|
2
5
|
require 'foreman_tasks_core'
|
3
6
|
require 'smart_proxy_dynflow_core/log'
|
4
7
|
require 'smart_proxy_dynflow_core/settings'
|
@@ -7,7 +10,9 @@ require 'smart_proxy_dynflow_core/helpers'
|
|
7
10
|
require 'smart_proxy_dynflow_core/callback'
|
8
11
|
require 'smart_proxy_dynflow_core/api'
|
9
12
|
|
10
|
-
SmartProxyDynflowCore
|
11
|
-
|
13
|
+
module SmartProxyDynflowCore
|
14
|
+
Core.after_initialize do |dynflow_core|
|
15
|
+
ForemanTasksCore.dynflow_setup(dynflow_core.world)
|
16
|
+
end
|
17
|
+
Core.register_silencer_matchers ForemanTasksCore.silent_dead_letter_matchers
|
12
18
|
end
|
13
|
-
SmartProxyDynflowCore::Core.register_silencer_matchers ForemanTasksCore.silent_dead_letter_matchers
|
@@ -3,11 +3,17 @@ require 'multi_json'
|
|
3
3
|
|
4
4
|
module SmartProxyDynflowCore
|
5
5
|
class Api < ::Sinatra::Base
|
6
|
+
TASK_UPDATE_REGEXP_PATH = %r{/tasks/(\S+)/(update|done)}
|
6
7
|
helpers Helpers
|
7
8
|
|
8
9
|
before do
|
9
|
-
|
10
|
-
|
10
|
+
if match = request.path_info.match(TASK_UPDATE_REGEXP_PATH)
|
11
|
+
task_id = match[1]
|
12
|
+
action = match[2]
|
13
|
+
authorize_with_token(task_id: task_id, clear: action == 'done')
|
14
|
+
else
|
15
|
+
authorize_with_ssl_client
|
16
|
+
end
|
11
17
|
content_type :json
|
12
18
|
end
|
13
19
|
|
@@ -21,6 +27,13 @@ module SmartProxyDynflowCore
|
|
21
27
|
MultiJson.dump(result)
|
22
28
|
end
|
23
29
|
|
30
|
+
post "/tasks/launch/?" do
|
31
|
+
params = MultiJson.load(request.body.read)
|
32
|
+
launcher = launcher_class(params).new(world, callback_host(params, request), params.fetch('options', {}))
|
33
|
+
launcher.launch!(params['input'])
|
34
|
+
launcher.results.to_json
|
35
|
+
end
|
36
|
+
|
24
37
|
post "/tasks/?" do
|
25
38
|
params = MultiJson.load(request.body.read)
|
26
39
|
trigger_task(::Dynflow::Utils.constantize(params['action_name']),
|
@@ -39,15 +52,30 @@ module SmartProxyDynflowCore
|
|
39
52
|
tasks_count(params['state']).to_json
|
40
53
|
end
|
41
54
|
|
42
|
-
post "/tasks/:task_id/done"
|
55
|
+
# capturing post "/tasks/:task_id/(update|done)"
|
56
|
+
post TASK_UPDATE_REGEXP_PATH do |task_id, _action|
|
43
57
|
data = MultiJson.load(request.body.read)
|
44
|
-
|
58
|
+
dispatch_external_event(task_id, data)
|
59
|
+
end
|
60
|
+
|
61
|
+
get "/tasks/operations" do
|
62
|
+
TaskLauncherRegistry.operations.to_json
|
45
63
|
end
|
46
64
|
|
47
65
|
private
|
48
66
|
|
49
67
|
def callback_host(params, request)
|
50
|
-
params.fetch('action_input', {})['proxy_url'] ||
|
68
|
+
params.fetch('action_input', {})['proxy_url'] ||
|
69
|
+
request.env.values_at('HTTP_X_FORWARDED_FOR', 'HTTP_HOST').compact.first
|
70
|
+
end
|
71
|
+
|
72
|
+
def launcher_class(params)
|
73
|
+
operation = params.fetch('operation')
|
74
|
+
if TaskLauncherRegistry.key?(operation)
|
75
|
+
TaskLauncherRegistry.fetch(operation)
|
76
|
+
else
|
77
|
+
halt 404, MultiJson.dump(:error => "Unknown operation '#{operation}' requested.")
|
78
|
+
end
|
51
79
|
end
|
52
80
|
end
|
53
81
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module SmartProxyDynflowCore
|
2
2
|
class BundlerHelper
|
3
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
3
4
|
def self.require_groups(*groups)
|
4
5
|
if File.exist?(File.expand_path('../../../Gemfile.in', __FILE__))
|
5
6
|
# If there is a Gemfile.in file, we will not use Bundler but BundlerExt
|
@@ -25,5 +26,6 @@ module SmartProxyDynflowCore
|
|
25
26
|
Bundler.require(*groups)
|
26
27
|
end
|
27
28
|
end
|
29
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
28
30
|
end
|
29
31
|
end
|
@@ -1,23 +1,52 @@
|
|
1
1
|
require 'rest-client'
|
2
2
|
|
3
|
+
# rubocop:disable Lint/HandleExceptions
|
3
4
|
begin
|
4
5
|
require 'smart_proxy_dynflow/callback'
|
5
6
|
rescue LoadError
|
6
7
|
end
|
8
|
+
# rubocop:enable Lint/HandleExceptions
|
7
9
|
|
8
10
|
module SmartProxyDynflowCore
|
9
11
|
module Callback
|
10
|
-
class
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
14
17
|
|
15
|
-
|
16
|
-
|
18
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
19
|
+
def ssl_options
|
20
|
+
return @ssl_options if defined? @ssl_options
|
21
|
+
@ssl_options = {}
|
22
|
+
settings = Settings.instance
|
23
|
+
return @ssl_options unless URI.parse(settings.foreman_url).scheme == 'https'
|
24
|
+
|
25
|
+
@ssl_options[:verify_ssl] = OpenSSL::SSL::VERIFY_PEER
|
26
|
+
|
27
|
+
private_key_file = settings.foreman_ssl_key || settings.ssl_private_key
|
28
|
+
if private_key_file
|
29
|
+
private_key = File.read(private_key_file)
|
30
|
+
@ssl_options[:ssl_client_key] = OpenSSL::PKey::RSA.new(private_key)
|
31
|
+
end
|
32
|
+
certificate_file = settings.foreman_ssl_cert || settings.ssl_certificate
|
33
|
+
if certificate_file
|
34
|
+
certificate = File.read(certificate_file)
|
35
|
+
@ssl_options[:ssl_client_cert] = OpenSSL::X509::Certificate.new(certificate)
|
36
|
+
end
|
37
|
+
ca_file = settings.foreman_ssl_ca || settings.ssl_ca_file
|
38
|
+
@ssl_options[:ssl_ca_file] = ca_file if ca_file
|
39
|
+
@ssl_options
|
40
|
+
end
|
41
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def prepare_payload(callback, data)
|
46
|
+
{ :callback => callback, :data => data }.to_json
|
47
|
+
end
|
17
48
|
end
|
18
|
-
end
|
19
49
|
|
20
|
-
class Request
|
21
50
|
def callback(payload)
|
22
51
|
response = callback_resource.post(payload, :content_type => :json)
|
23
52
|
if response.code.to_s != "200"
|
@@ -26,43 +55,12 @@ module SmartProxyDynflowCore
|
|
26
55
|
response
|
27
56
|
end
|
28
57
|
|
29
|
-
def self.send_to_foreman_tasks(callback_info, data)
|
30
|
-
self.new.callback(self.prepare_payload(callback_info, data))
|
31
|
-
end
|
32
|
-
|
33
58
|
private
|
34
59
|
|
35
|
-
def self.prepare_payload(callback, data)
|
36
|
-
{ :callback => callback, :data => data }.to_json
|
37
|
-
end
|
38
|
-
|
39
60
|
def callback_resource
|
40
61
|
@resource ||= RestClient::Resource.new(Settings.instance.foreman_url + '/foreman_tasks/api/tasks/callback',
|
41
62
|
self.class.ssl_options)
|
42
63
|
end
|
43
|
-
|
44
|
-
def self.ssl_options
|
45
|
-
return @ssl_options if defined? @ssl_options
|
46
|
-
@ssl_options = {}
|
47
|
-
settings = Settings.instance
|
48
|
-
return @ssl_options unless URI.parse(settings.foreman_url).scheme == 'https'
|
49
|
-
|
50
|
-
@ssl_options[:verify_ssl] = OpenSSL::SSL::VERIFY_PEER
|
51
|
-
|
52
|
-
private_key_file = settings.foreman_ssl_key || settings.ssl_private_key
|
53
|
-
if private_key_file
|
54
|
-
private_key = File.read(private_key_file)
|
55
|
-
@ssl_options[:ssl_client_key] = OpenSSL::PKey::RSA.new(private_key)
|
56
|
-
end
|
57
|
-
certificate_file = settings.foreman_ssl_cert || settings.ssl_certificate
|
58
|
-
if certificate_file
|
59
|
-
certificate = File.read(certificate_file)
|
60
|
-
@ssl_options[:ssl_client_cert] = OpenSSL::X509::Certificate.new(certificate)
|
61
|
-
end
|
62
|
-
ca_file = settings.foreman_ssl_ca || settings.ssl_ca_file
|
63
|
-
@ssl_options[:ssl_ca_file] = ca_file if ca_file
|
64
|
-
@ssl_options
|
65
|
-
end
|
66
64
|
end
|
67
65
|
|
68
66
|
class Action < ::Dynflow::Action
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module SmartProxyDynflowCore
|
2
2
|
class Core
|
3
|
-
|
4
3
|
attr_accessor :world, :accepted_cert_serial
|
5
4
|
|
6
5
|
def initialize
|
@@ -25,7 +24,8 @@ module SmartProxyDynflowCore
|
|
25
24
|
|
26
25
|
db_file = Settings.instance.database
|
27
26
|
if db_file.nil? || db_file.empty?
|
28
|
-
Log.instance.warn "Could not open DB for dynflow at '#{db_file}',
|
27
|
+
Log.instance.warn "Could not open DB for dynflow at '#{db_file}', " \
|
28
|
+
"will keep data in memory. Restart will drop all dynflow data."
|
29
29
|
else
|
30
30
|
db_conn_string += "/#{db_file}"
|
31
31
|
end
|
@@ -44,7 +44,7 @@ module SmartProxyDynflowCore
|
|
44
44
|
config.persistence_adapter = persistence_adapter
|
45
45
|
config.execution_plan_cleaner = execution_plan_cleaner
|
46
46
|
# TODO: There has to be a better way
|
47
|
-
matchers = config.silent_dead_letter_matchers.call
|
47
|
+
matchers = config.silent_dead_letter_matchers.call.concat(self.class.silencer_matchers)
|
48
48
|
config.silent_dead_letter_matchers = matchers
|
49
49
|
yield config if block_given?
|
50
50
|
end
|
@@ -4,13 +4,14 @@ module SmartProxyDynflowCore
|
|
4
4
|
SmartProxyDynflowCore::Core.world
|
5
5
|
end
|
6
6
|
|
7
|
-
def authorize_with_token
|
7
|
+
def authorize_with_token(task_id:, clear: true)
|
8
8
|
if request.env.key? 'HTTP_AUTHORIZATION'
|
9
9
|
if defined?(::ForemanTasksCore)
|
10
10
|
auth = request.env['HTTP_AUTHORIZATION']
|
11
11
|
basic_prefix = /\ABasic /
|
12
12
|
if !auth.to_s.empty? && auth =~ basic_prefix &&
|
13
|
-
|
13
|
+
ForemanTasksCore::OtpManager.authenticate(auth.gsub(basic_prefix, ''),
|
14
|
+
expected_user: task_id, clear: clear)
|
14
15
|
Log.instance.debug('authorized with token')
|
15
16
|
return true
|
16
17
|
end
|
@@ -21,7 +22,7 @@ module SmartProxyDynflowCore
|
|
21
22
|
end
|
22
23
|
|
23
24
|
def authorize_with_ssl_client
|
24
|
-
if %w
|
25
|
+
if %w[yes on 1].include? request.env['HTTPS'].to_s
|
25
26
|
if request.env['SSL_CLIENT_CERT'].to_s.empty?
|
26
27
|
Log.instance.error "No client SSL certificate supplied"
|
27
28
|
halt 403, MultiJson.dump(:error => "No client SSL certificate supplied")
|
@@ -63,7 +64,7 @@ module SmartProxyDynflowCore
|
|
63
64
|
{ :count => tasks.count, :state => state }
|
64
65
|
end
|
65
66
|
|
66
|
-
def
|
67
|
+
def dispatch_external_event(task_id, params)
|
67
68
|
world.event(task_id,
|
68
69
|
params['step_id'].to_i,
|
69
70
|
::ForemanTasksCore::Runner::ExternalEvent.new(params))
|
@@ -1,9 +1,13 @@
|
|
1
1
|
require 'webrick/https'
|
2
2
|
require 'smart_proxy_dynflow_core/bundler_helper'
|
3
3
|
require 'smart_proxy_dynflow_core/settings'
|
4
|
-
require 'smart_proxy_dynflow_core/
|
4
|
+
require 'smart_proxy_dynflow_core/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
|
7
11
|
|
8
12
|
def self.launch!(options)
|
9
13
|
self.new.start options
|
@@ -15,6 +19,7 @@ module SmartProxyDynflowCore
|
|
15
19
|
install_usr1_trap
|
16
20
|
Rack::Server.new(rack_settings).start do |_server|
|
17
21
|
SmartProxyDynflowCore::Core.ensure_initialized
|
22
|
+
SmartProxyDynflowCore::SdNotify.new.tap { |sd| sd.ready if sd.active? }
|
18
23
|
end
|
19
24
|
end
|
20
25
|
|
@@ -79,11 +84,12 @@ module SmartProxyDynflowCore
|
|
79
84
|
:AccessLog => [[Log.instance, WEBrick::AccessLog::COMMON_LOG_FORMAT]],
|
80
85
|
:Logger => Log.instance,
|
81
86
|
:daemonize => Settings.instance.daemonize,
|
82
|
-
:pid => Settings.instance.pid_file,
|
87
|
+
:pid => Settings.instance.daemonize && Settings.instance.pid_file,
|
83
88
|
:server => :webrick
|
84
89
|
}
|
85
90
|
end
|
86
91
|
|
92
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
87
93
|
def https_app
|
88
94
|
ssl_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
|
89
95
|
ssl_options |= OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE if defined?(OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE)
|
@@ -91,10 +97,11 @@ module SmartProxyDynflowCore
|
|
91
97
|
ssl_options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2)
|
92
98
|
ssl_options |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3)
|
93
99
|
ssl_options |= OpenSSL::SSL::OP_NO_TLSv1 if defined?(OpenSSL::SSL::OP_NO_TLSv1)
|
100
|
+
ssl_options |= OpenSSL::SSL::OP_NO_TLSv1_1 if defined?(OpenSSL::SSL::OP_NO_TLSv1_1)
|
94
101
|
|
95
102
|
if Settings.instance.tls_disabled_versions
|
96
103
|
Settings.instance.tls_disabled_versions.each do |version|
|
97
|
-
constant = OpenSSL::SSL.const_get("OP_NO_TLSv#{version.to_s.
|
104
|
+
constant = OpenSSL::SSL.const_get("OP_NO_TLSv#{version.to_s.tr('.', '_')}") rescue nil
|
98
105
|
|
99
106
|
if constant
|
100
107
|
Log.instance.info "TLSv#{version} will be disabled."
|
@@ -111,9 +118,11 @@ module SmartProxyDynflowCore
|
|
111
118
|
:SSLPrivateKey => ssl_private_key,
|
112
119
|
:SSLCertificate => ssl_certificate,
|
113
120
|
:SSLCACertificateFile => Settings.instance.ssl_ca_file,
|
121
|
+
:SSLCiphers => CIPHERS - SmartProxyDynflowCore::Settings.instance.ssl_disabled_ciphers,
|
114
122
|
:SSLOptions => ssl_options
|
115
123
|
}
|
116
124
|
end
|
125
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
117
126
|
|
118
127
|
def https_enabled?
|
119
128
|
Settings.instance.use_https
|
@@ -122,14 +131,16 @@ module SmartProxyDynflowCore
|
|
122
131
|
def ssl_private_key
|
123
132
|
OpenSSL::PKey::RSA.new(File.read(Settings.instance.ssl_private_key))
|
124
133
|
rescue Exception => e
|
125
|
-
Log.instance.fatal "Unable to load private SSL key. Are the values
|
134
|
+
Log.instance.fatal "Unable to load private SSL key. Are the values "\
|
135
|
+
"correct in settings.yml and do permissions allow reading?: #{e}"
|
126
136
|
raise e
|
127
137
|
end
|
128
138
|
|
129
139
|
def ssl_certificate
|
130
140
|
OpenSSL::X509::Certificate.new(File.read(Settings.instance.ssl_certificate))
|
131
141
|
rescue Exception => e
|
132
|
-
Log.instance.fatal "Unable to load SSL certificate. Are the values
|
142
|
+
Log.instance.fatal "Unable to load SSL certificate. Are the values " \
|
143
|
+
"correct in settings.yml and do permissions allow reading?: #{e}"
|
133
144
|
raise e
|
134
145
|
end
|
135
146
|
|
@@ -141,7 +152,7 @@ module SmartProxyDynflowCore
|
|
141
152
|
Dir[File.join(dir, 'settings.d', '*.yml')].each { |path| Settings.load_plugin_settings(path) }
|
142
153
|
true
|
143
154
|
end
|
144
|
-
ForemanTasksCore::SettingsLoader.settings_registry.
|
155
|
+
ForemanTasksCore::SettingsLoader.settings_registry.each_key do |settings_keys|
|
145
156
|
settings = settings_keys.inject({}) do |h, settings_key|
|
146
157
|
if SETTINGS.plugins.key?(settings_key.to_s)
|
147
158
|
h.merge(SETTINGS.plugins[settings_key.to_s].to_h)
|
@@ -2,7 +2,6 @@ require 'logger'
|
|
2
2
|
|
3
3
|
module SmartProxyDynflowCore
|
4
4
|
class Log < ::Logger
|
5
|
-
|
6
5
|
alias_method :write, :debug
|
7
6
|
|
8
7
|
class << self
|
@@ -42,25 +41,45 @@ module SmartProxyDynflowCore
|
|
42
41
|
|
43
42
|
def initialize(file, *rest)
|
44
43
|
@file = file
|
45
|
-
@fd = @file.
|
44
|
+
@fd = @file.is_a?(IO) ? @file : File.open(@file, 'a')
|
46
45
|
@fd.sync = true
|
47
46
|
super(@fd, rest)
|
48
47
|
end
|
49
48
|
|
50
49
|
def roll_log
|
51
|
-
unless @file.
|
50
|
+
unless @file.is_a? IO
|
52
51
|
@fd.reopen @file, 'a'
|
53
52
|
@fd.sync = true
|
54
53
|
end
|
55
54
|
end
|
56
55
|
|
56
|
+
class ProxyStructuredFormater < ::Dynflow::LoggerAdapters::Formatters::Abstract
|
57
|
+
def call(_severity, _datetime, _prog_name, message)
|
58
|
+
if message.is_a?(::Exception)
|
59
|
+
subject = "#{message.message} (#{message.class})"
|
60
|
+
if @base.respond_to?(:exception)
|
61
|
+
@base.exception("Error details", message)
|
62
|
+
subject
|
63
|
+
else
|
64
|
+
"#{subject}\n#{message.backtrace.join("\n")}"
|
65
|
+
end
|
66
|
+
else
|
67
|
+
message
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def format(message)
|
72
|
+
call(nil, nil, nil, message)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
57
76
|
class ProxyAdapter < ::Dynflow::LoggerAdapters::Simple
|
58
|
-
def initialize(logger, level = Logger::DEBUG,
|
77
|
+
def initialize(logger, level = Logger::DEBUG, _formatters = [])
|
59
78
|
@logger = logger
|
60
79
|
@logger.level = level
|
61
|
-
@logger.formatter =
|
62
|
-
@action_logger = apply_formatters
|
63
|
-
@dynflow_logger = apply_formatters
|
80
|
+
@logger.formatter = ProxyStructuredFormater.new(@logger)
|
81
|
+
@action_logger = apply_formatters(ProgNameWrapper.new(@logger, ' action'), [ProxyStructuredFormater])
|
82
|
+
@dynflow_logger = apply_formatters(ProgNameWrapper.new(@logger, 'dynflow'), [ProxyStructuredFormater])
|
64
83
|
end
|
65
84
|
end
|
66
85
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# Shamelessly taken from theforeman/smart-proxy @ 99e9e5b
|
2
|
+
# kudos to domcleal
|
3
|
+
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
# Implementation of libsystemd's sd_notify API, sends current state via socket
|
7
|
+
module SmartProxyDynflowCore
|
8
|
+
class SdNotify
|
9
|
+
def active?
|
10
|
+
!ENV['NOTIFY_SOCKET'].nil?
|
11
|
+
end
|
12
|
+
|
13
|
+
def notify(message)
|
14
|
+
create_socket.tap do |socket|
|
15
|
+
socket.sendmsg(message.chomp + "\n") # ensure trailing \n
|
16
|
+
socket.close
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def ready(state = 1)
|
21
|
+
notify("READY=#{state}")
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def create_socket
|
27
|
+
raise 'Missing NOTIFY_SOCKET environment variable, is this process running under systemd?' unless active?
|
28
|
+
Socket.new(Socket::AF_UNIX, Socket::SOCK_DGRAM, 0).tap do |socket|
|
29
|
+
socket.connect(Socket.pack_sockaddr_un(ENV['NOTIFY_SOCKET']))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -19,37 +19,36 @@ end
|
|
19
19
|
|
20
20
|
module SmartProxyDynflowCore
|
21
21
|
class Settings < OpenStruct
|
22
|
-
|
23
22
|
DEFAULT_SETTINGS = {
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
}
|
23
|
+
:database => '/var/lib/foreman-proxy/dynflow/dynflow.sqlite',
|
24
|
+
:foreman_url => 'https://127.0.0.1:3000',
|
25
|
+
:console_auth => true,
|
26
|
+
:listen => '127.0.0.1',
|
27
|
+
:port => '8008',
|
28
|
+
:use_https => false,
|
29
|
+
:ssl_ca_file => nil,
|
30
|
+
:ssl_private_key => nil,
|
31
|
+
:ssl_certificate => nil,
|
32
|
+
:ssl_disabled_ciphers => [],
|
33
|
+
:tls_disabled_versions => [],
|
34
|
+
:foreman_ssl_ca => nil,
|
35
|
+
:foreman_ssl_key => nil,
|
36
|
+
:foreman_ssl_cert => nil,
|
37
|
+
:standalone => false,
|
38
|
+
:log_file => '/var/log/foreman-proxy/smart_proxy_dynflow_core.log',
|
39
|
+
:log_level => :ERROR,
|
40
|
+
:plugins => {},
|
41
|
+
:pid_file => '/var/run/foreman-proxy/smart_proxy_dynflow_core.pid',
|
42
|
+
:daemonize => false,
|
43
|
+
:execution_plan_cleaner_age => 60 * 60 * 24,
|
44
|
+
:loaded => false
|
45
|
+
}.freeze
|
47
46
|
|
48
|
-
PROXY_SETTINGS = [
|
49
|
-
|
50
|
-
|
51
|
-
PLUGIN_SETTINGS = [
|
52
|
-
|
47
|
+
PROXY_SETTINGS = %i[ssl_ca_file ssl_certificate ssl_private_key foreman_url
|
48
|
+
foreman_ssl_ca foreman_ssl_cert foreman_ssl_key
|
49
|
+
log_file log_level ssl_disabled_ciphers].freeze
|
50
|
+
PLUGIN_SETTINGS = %i[database core_url console_auth
|
51
|
+
execution_plan_cleaner_age].freeze
|
53
52
|
|
54
53
|
def initialize(settings = {})
|
55
54
|
super(DEFAULT_SETTINGS.merge(settings))
|
@@ -60,7 +59,7 @@ module SmartProxyDynflowCore
|
|
60
59
|
end
|
61
60
|
|
62
61
|
def self.load_global_settings(path)
|
63
|
-
if File.
|
62
|
+
if File.exist? File.join(path)
|
64
63
|
YAML.load_file(path).each do |key, value|
|
65
64
|
SETTINGS[key] = value
|
66
65
|
end
|
@@ -87,7 +86,7 @@ module SmartProxyDynflowCore
|
|
87
86
|
PLUGIN_SETTINGS.each do |key|
|
88
87
|
SETTINGS[key] = settings[key] if settings.key?(key)
|
89
88
|
end
|
90
|
-
SETTINGS.plugins.values.each
|
89
|
+
SETTINGS.plugins.values.each(&:load_settings_from_proxy)
|
91
90
|
Settings.loaded!
|
92
91
|
end
|
93
92
|
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module SmartProxyDynflowCore
|
2
|
+
class TaskLauncherRegistry
|
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
|
31
|
+
end
|
@@ -17,7 +17,7 @@ module SmartProxyDynflowCore
|
|
17
17
|
config.auto_terminate = false
|
18
18
|
config.logger_adapter = ::Dynflow::LoggerAdapters::Simple.new $stderr, DYNFLOW_TESTING_LOG_LEVEL
|
19
19
|
config.execution_plan_cleaner = nil
|
20
|
-
|
20
|
+
yield(config) if block
|
21
21
|
end
|
22
22
|
end
|
23
23
|
end
|
@@ -1,8 +1,8 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
lib = File.expand_path('../lib', __FILE__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'smart_proxy_dynflow_core/version'
|
5
4
|
|
5
|
+
# rubocop:disable Metrics/BlockLength
|
6
6
|
Gem::Specification.new do |gem|
|
7
7
|
gem.name = "smart_proxy_dynflow_core"
|
8
8
|
gem.version = SmartProxyDynflowCore::VERSION
|
@@ -22,19 +22,19 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.require_paths = ["lib"]
|
23
23
|
gem.license = 'GPL-3.0'
|
24
24
|
|
25
|
-
gem.add_development_dependency "bundler", "
|
26
|
-
gem.add_development_dependency "rake", "~> 10.0"
|
25
|
+
gem.add_development_dependency "bundler", ">= 1.7"
|
27
26
|
gem.add_development_dependency('minitest')
|
28
27
|
gem.add_development_dependency('mocha', '~> 1')
|
29
|
-
gem.add_development_dependency('webmock', '~> 1')
|
30
28
|
gem.add_development_dependency('rack-test', '~> 0')
|
31
|
-
gem.add_development_dependency
|
29
|
+
gem.add_development_dependency "rake", "~> 10.0"
|
30
|
+
gem.add_development_dependency('webmock', '~> 1')
|
32
31
|
|
33
|
-
gem.add_runtime_dependency('dynflow', "~> 1.
|
34
|
-
gem.add_runtime_dependency('foreman-tasks-core', '>= 0.
|
35
|
-
gem.add_runtime_dependency('sequel')
|
36
|
-
gem.add_runtime_dependency('sqlite3')
|
37
|
-
gem.add_runtime_dependency('sinatra')
|
32
|
+
gem.add_runtime_dependency('dynflow', "~> 1.1")
|
33
|
+
gem.add_runtime_dependency('foreman-tasks-core', '>= 0.3.3')
|
38
34
|
gem.add_runtime_dependency('rack')
|
39
35
|
gem.add_runtime_dependency('rest-client')
|
36
|
+
gem.add_runtime_dependency('sequel')
|
37
|
+
gem.add_runtime_dependency('sinatra')
|
38
|
+
gem.add_runtime_dependency('sqlite3')
|
40
39
|
end
|
40
|
+
# rubocop:enable Metrics/BlockLength
|
metadata
CHANGED
@@ -1,43 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_proxy_dynflow_core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.6
|
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:
|
11
|
+
date: 2020-06-19 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
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rake
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '10.0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '10.0'
|
41
27
|
- !ruby/object:Gem::Dependency
|
42
28
|
name: minitest
|
43
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,77 +53,77 @@ dependencies:
|
|
67
53
|
- !ruby/object:Gem::Version
|
68
54
|
version: '1'
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
56
|
+
name: rack-test
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
72
58
|
requirements:
|
73
59
|
- - "~>"
|
74
60
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
61
|
+
version: '0'
|
76
62
|
type: :development
|
77
63
|
prerelease: false
|
78
64
|
version_requirements: !ruby/object:Gem::Requirement
|
79
65
|
requirements:
|
80
66
|
- - "~>"
|
81
67
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
68
|
+
version: '0'
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
70
|
+
name: rake
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
86
72
|
requirements:
|
87
73
|
- - "~>"
|
88
74
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
75
|
+
version: '10.0'
|
90
76
|
type: :development
|
91
77
|
prerelease: false
|
92
78
|
version_requirements: !ruby/object:Gem::Requirement
|
93
79
|
requirements:
|
94
80
|
- - "~>"
|
95
81
|
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
82
|
+
version: '10.0'
|
97
83
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
84
|
+
name: webmock
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
100
86
|
requirements:
|
101
87
|
- - "~>"
|
102
88
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
89
|
+
version: '1'
|
104
90
|
type: :development
|
105
91
|
prerelease: false
|
106
92
|
version_requirements: !ruby/object:Gem::Requirement
|
107
93
|
requirements:
|
108
94
|
- - "~>"
|
109
95
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
96
|
+
version: '1'
|
111
97
|
- !ruby/object:Gem::Dependency
|
112
98
|
name: dynflow
|
113
99
|
requirement: !ruby/object:Gem::Requirement
|
114
100
|
requirements:
|
115
101
|
- - "~>"
|
116
102
|
- !ruby/object:Gem::Version
|
117
|
-
version: '1.
|
103
|
+
version: '1.1'
|
118
104
|
type: :runtime
|
119
105
|
prerelease: false
|
120
106
|
version_requirements: !ruby/object:Gem::Requirement
|
121
107
|
requirements:
|
122
108
|
- - "~>"
|
123
109
|
- !ruby/object:Gem::Version
|
124
|
-
version: '1.
|
110
|
+
version: '1.1'
|
125
111
|
- !ruby/object:Gem::Dependency
|
126
112
|
name: foreman-tasks-core
|
127
113
|
requirement: !ruby/object:Gem::Requirement
|
128
114
|
requirements:
|
129
115
|
- - ">="
|
130
116
|
- !ruby/object:Gem::Version
|
131
|
-
version: 0.
|
117
|
+
version: 0.3.3
|
132
118
|
type: :runtime
|
133
119
|
prerelease: false
|
134
120
|
version_requirements: !ruby/object:Gem::Requirement
|
135
121
|
requirements:
|
136
122
|
- - ">="
|
137
123
|
- !ruby/object:Gem::Version
|
138
|
-
version: 0.
|
124
|
+
version: 0.3.3
|
139
125
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
126
|
+
name: rack
|
141
127
|
requirement: !ruby/object:Gem::Requirement
|
142
128
|
requirements:
|
143
129
|
- - ">="
|
@@ -151,7 +137,7 @@ dependencies:
|
|
151
137
|
- !ruby/object:Gem::Version
|
152
138
|
version: '0'
|
153
139
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
140
|
+
name: rest-client
|
155
141
|
requirement: !ruby/object:Gem::Requirement
|
156
142
|
requirements:
|
157
143
|
- - ">="
|
@@ -165,7 +151,7 @@ dependencies:
|
|
165
151
|
- !ruby/object:Gem::Version
|
166
152
|
version: '0'
|
167
153
|
- !ruby/object:Gem::Dependency
|
168
|
-
name:
|
154
|
+
name: sequel
|
169
155
|
requirement: !ruby/object:Gem::Requirement
|
170
156
|
requirements:
|
171
157
|
- - ">="
|
@@ -179,7 +165,7 @@ dependencies:
|
|
179
165
|
- !ruby/object:Gem::Version
|
180
166
|
version: '0'
|
181
167
|
- !ruby/object:Gem::Dependency
|
182
|
-
name:
|
168
|
+
name: sinatra
|
183
169
|
requirement: !ruby/object:Gem::Requirement
|
184
170
|
requirements:
|
185
171
|
- - ">="
|
@@ -193,7 +179,7 @@ dependencies:
|
|
193
179
|
- !ruby/object:Gem::Version
|
194
180
|
version: '0'
|
195
181
|
- !ruby/object:Gem::Dependency
|
196
|
-
name:
|
182
|
+
name: sqlite3
|
197
183
|
requirement: !ruby/object:Gem::Requirement
|
198
184
|
requirements:
|
199
185
|
- - ">="
|
@@ -228,10 +214,11 @@ files:
|
|
228
214
|
- lib/smart_proxy_dynflow_core/helpers.rb
|
229
215
|
- lib/smart_proxy_dynflow_core/launcher.rb
|
230
216
|
- lib/smart_proxy_dynflow_core/log.rb
|
217
|
+
- lib/smart_proxy_dynflow_core/sd_notify.rb
|
231
218
|
- lib/smart_proxy_dynflow_core/settings.rb
|
219
|
+
- lib/smart_proxy_dynflow_core/task_launcher_registry.rb
|
232
220
|
- lib/smart_proxy_dynflow_core/testing.rb
|
233
221
|
- lib/smart_proxy_dynflow_core/version.rb
|
234
|
-
- lib/smart_proxy_dynflow_core/webrick-patch.rb
|
235
222
|
- smart_proxy_dynflow_core.gemspec
|
236
223
|
homepage: https://github.com/theforeman/smart_proxy_dynflow
|
237
224
|
licenses:
|
@@ -252,8 +239,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
252
239
|
- !ruby/object:Gem::Version
|
253
240
|
version: '0'
|
254
241
|
requirements: []
|
255
|
-
|
256
|
-
rubygems_version: 2.6.12
|
242
|
+
rubygems_version: 3.0.3
|
257
243
|
signing_key:
|
258
244
|
specification_version: 4
|
259
245
|
summary: Dynflow runtime for Foreman smart proxy
|
@@ -1,37 +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']
|
7
|
-
|
8
|
-
module WEBrick
|
9
|
-
class GenericServer
|
10
|
-
def setup_ssl_context(config) # :nodoc:
|
11
|
-
unless config[:SSLCertificate]
|
12
|
-
cn = config[:SSLCertName]
|
13
|
-
comment = config[:SSLCertComment]
|
14
|
-
cert, key = Utils::create_self_signed_cert(1024, cn, comment)
|
15
|
-
config[:SSLCertificate] = cert
|
16
|
-
config[:SSLPrivateKey] = key
|
17
|
-
end
|
18
|
-
ctx = OpenSSL::SSL::SSLContext.new
|
19
|
-
ctx.set_params
|
20
|
-
ctx.ciphers = (CIPHERS - SmartProxyDynflowCore::Settings.instance.ssl_disabled_ciphers).join(':')
|
21
|
-
ctx.key = config[:SSLPrivateKey]
|
22
|
-
ctx.cert = config[:SSLCertificate]
|
23
|
-
ctx.client_ca = config[:SSLClientCA]
|
24
|
-
ctx.extra_chain_cert = config[:SSLExtraChainCert]
|
25
|
-
ctx.ca_file = config[:SSLCACertificateFile]
|
26
|
-
ctx.ca_path = config[:SSLCACertificatePath]
|
27
|
-
ctx.cert_store = config[:SSLCertificateStore]
|
28
|
-
ctx.tmp_dh_callback = config[:SSLTmpDhCallback]
|
29
|
-
ctx.verify_mode = config[:SSLVerifyClient]
|
30
|
-
ctx.verify_depth = config[:SSLVerifyDepth]
|
31
|
-
ctx.verify_callback = config[:SSLVerifyCallback]
|
32
|
-
ctx.timeout = config[:SSLTimeout]
|
33
|
-
ctx.options |= config[:SSLOptions] unless config[:SSLOptions].nil?
|
34
|
-
ctx
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|