smart_proxy_dynflow_core 0.2.6 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 84ea792e8e6d9f6ddcd145428485d5faadf92da27acd184fa42956fa4873e7fd
4
- data.tar.gz: 07adc8b3c623462086d0797455afaf822e27212bcdcda345a950df7e49296814
3
+ metadata.gz: 3226e9b143ecf53b28e97a765c2e760d42dc030ea3809736f5e7733b2db96cfe
4
+ data.tar.gz: eb70345b228d35506d36faa0a3fe59e09c8b1787d438f73d6554699736a70d3a
5
5
  SHA512:
6
- metadata.gz: 989f9619b041039a3897de03cac1edc5ce1415a19792b3a5b2e907b174b97472b3a56f19cd5cab9977c26e5d5c83c38636afe8c49186dbd2bfce1c7e3dbfcc84
7
- data.tar.gz: 7caa3ee4ddce3560970bd8f7a7b8e2b99dd96f1fae456e78475673ec98478e0c608e5feb29442b7046e4eed07e61f0d9a009dade6646e534f6ef6f033192f572
6
+ metadata.gz: cf3292bd295765e51968781f4a0a3678a5825977da06b56ddf86e073a232665f92f90cd5df25e0a6332304b4c3c15784404f2aae8d25f050cc77405d7520e57f
7
+ data.tar.gz: 1bdaaebb6e5b59fcf25250c4ba7695dadda3313854c8db343199bb8ec61529ed21deff5e948ac0545f6fde0eab6cde7bde73067b96943061e5bd013a22faa9db
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
- if RUBY_VERSION < '2.1'
14
- gem 'public_suffix', '< 3'
15
- gem 'rainbow', '< 3'
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
- if RUBY_VERSION < '2.2'
29
- gem 'rack', '>= 1.1', '< 2.0.0'
30
- gem 'sinatra', '< 2'
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|
@@ -1,18 +1,13 @@
1
- raise LoadError, 'Ruby >= 2.1 is required' unless RUBY_VERSION >= '2.1'
1
+ # Whatever is here is just a compatibility layer
2
+ # Once all the _core have been migrated, this can be dropped.
2
3
 
3
- require 'dynflow'
4
+ require 'smart_proxy_dynflow'
5
+
6
+ # REX core explicitly requires this file, otherwise we could use the trick we
7
+ # use with callback
4
8
  require 'smart_proxy_dynflow_core/task_launcher_registry'
5
- require 'foreman_tasks_core'
6
- require 'smart_proxy_dynflow_core/log'
7
- require 'smart_proxy_dynflow_core/settings'
8
- require 'smart_proxy_dynflow_core/core'
9
- require 'smart_proxy_dynflow_core/helpers'
10
- require 'smart_proxy_dynflow_core/callback'
11
- require 'smart_proxy_dynflow_core/api'
12
9
 
13
10
  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
11
+ Callback = Proxy::Dynflow::Callback
12
+ Log = Proxy::Dynflow::Log
18
13
  end
@@ -1,31 +1,3 @@
1
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
2
+ TaskLauncherRegistry = Proxy::Dynflow::TaskLauncherRegistry
31
3
  end
@@ -1,3 +1,3 @@
1
1
  module SmartProxyDynflowCore
2
- VERSION = '0.2.6'.freeze
2
+ VERSION = '0.4.0'.freeze
3
3
  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
@@ -14,14 +13,14 @@ Gem::Specification.new do |gem|
14
13
  Use the Dynflow inside Foreman smart proxy
15
14
  EOS
16
15
 
17
- gem.executables = ['smart_proxy_dynflow_core']
18
- gem.files = Dir['lib/smart_proxy_dynflow_core.rb', 'config/settings.yml.example',
19
- 'lib/smart_proxy_dynflow_core/*', 'LICENSE', 'Gemfile',
20
- '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']
21
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
22
19
  gem.require_paths = ["lib"]
23
20
  gem.license = 'GPL-3.0'
24
21
 
22
+ gem.required_ruby_version = '~> 2.5'
23
+
25
24
  gem.add_development_dependency "bundler", ">= 1.7"
26
25
  gem.add_development_dependency('minitest')
27
26
  gem.add_development_dependency('mocha', '~> 1')
@@ -31,10 +30,11 @@ Gem::Specification.new do |gem|
31
30
 
32
31
  gem.add_runtime_dependency('dynflow', "~> 1.1")
33
32
  gem.add_runtime_dependency('foreman-tasks-core', '>= 0.3.3')
33
+ gem.add_runtime_dependency('logging')
34
34
  gem.add_runtime_dependency('rack')
35
35
  gem.add_runtime_dependency('rest-client')
36
+ gem.add_runtime_dependency('sd_notify', '~> 0.1')
36
37
  gem.add_runtime_dependency('sequel')
37
38
  gem.add_runtime_dependency('sinatra')
38
39
  gem.add_runtime_dependency('sqlite3')
39
40
  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.2.6
4
+ version: 0.4.0
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: 2020-06-19 00:00:00.000000000 Z
11
+ date: 2021-06-07 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
@@ -195,52 +223,37 @@ dependencies:
195
223
  description: " Use the Dynflow inside Foreman smart proxy\n"
196
224
  email:
197
225
  - inecas@redhat.com
198
- executables:
199
- - smart_proxy_dynflow_core
226
+ executables: []
200
227
  extensions: []
201
228
  extra_rdoc_files: []
202
229
  files:
203
230
  - Gemfile
204
231
  - LICENSE
205
- - bin/smart_proxy_dynflow_core
206
- - config/settings.yml.example
207
- - deploy/smart_proxy_dynflow_core.init
208
- - deploy/smart_proxy_dynflow_core.service
209
232
  - lib/smart_proxy_dynflow_core.rb
210
- - lib/smart_proxy_dynflow_core/api.rb
211
- - lib/smart_proxy_dynflow_core/bundler_helper.rb
212
- - lib/smart_proxy_dynflow_core/callback.rb
213
- - lib/smart_proxy_dynflow_core/core.rb
214
- - lib/smart_proxy_dynflow_core/helpers.rb
215
- - lib/smart_proxy_dynflow_core/launcher.rb
216
- - lib/smart_proxy_dynflow_core/log.rb
217
- - lib/smart_proxy_dynflow_core/sd_notify.rb
218
- - lib/smart_proxy_dynflow_core/settings.rb
219
233
  - lib/smart_proxy_dynflow_core/task_launcher_registry.rb
220
- - lib/smart_proxy_dynflow_core/testing.rb
221
234
  - lib/smart_proxy_dynflow_core/version.rb
222
235
  - smart_proxy_dynflow_core.gemspec
223
236
  homepage: https://github.com/theforeman/smart_proxy_dynflow
224
237
  licenses:
225
238
  - GPL-3.0
226
239
  metadata: {}
227
- post_install_message:
240
+ post_install_message:
228
241
  rdoc_options: []
229
242
  require_paths:
230
243
  - lib
231
244
  required_ruby_version: !ruby/object:Gem::Requirement
232
245
  requirements:
233
- - - ">="
246
+ - - "~>"
234
247
  - !ruby/object:Gem::Version
235
- version: '0'
248
+ version: '2.5'
236
249
  required_rubygems_version: !ruby/object:Gem::Requirement
237
250
  requirements:
238
251
  - - ">="
239
252
  - !ruby/object:Gem::Version
240
253
  version: '0'
241
254
  requirements: []
242
- rubygems_version: 3.0.3
243
- signing_key:
255
+ rubygems_version: 3.1.2
256
+ signing_key:
244
257
  specification_version: 4
245
258
  summary: Dynflow runtime for Foreman smart proxy
246
259
  test_files: []
@@ -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
@@ -1,50 +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 logging to STDOUT
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
- # Maximum age of execution plans to keep before having them cleaned
49
- # by the execution plan cleaner (in seconds), defaults to 24 hours
50
- # :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,81 +0,0 @@
1
- require 'sinatra/base'
2
- require 'multi_json'
3
-
4
- module SmartProxyDynflowCore
5
- class Api < ::Sinatra::Base
6
- TASK_UPDATE_REGEXP_PATH = %r{/tasks/(\S+)/(update|done)}
7
- helpers Helpers
8
-
9
- before do
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
17
- content_type :json
18
- end
19
-
20
- post "/tasks/status" do
21
- params = MultiJson.load(request.body.read)
22
- ids = params.fetch('task_ids', [])
23
- result = world.persistence
24
- .find_execution_plans(:filters => { :uuid => ids }).reduce({}) do |acc, plan|
25
- acc.update(plan.id => { 'state' => plan.state, 'result' => plan.result })
26
- end
27
- MultiJson.dump(result)
28
- end
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
-
37
- post "/tasks/?" do
38
- params = MultiJson.load(request.body.read)
39
- trigger_task(::Dynflow::Utils.constantize(params['action_name']),
40
- params['action_input'].merge(:callback_host => callback_host(params, request))).to_json
41
- end
42
-
43
- post "/tasks/:task_id/cancel" do |task_id|
44
- cancel_task(task_id).to_json
45
- end
46
-
47
- get "/tasks/:task_id/status" do |task_id|
48
- task_status(task_id).to_json
49
- end
50
-
51
- get "/tasks/count" do
52
- tasks_count(params['state']).to_json
53
- end
54
-
55
- # capturing post "/tasks/:task_id/(update|done)"
56
- post TASK_UPDATE_REGEXP_PATH do |task_id, _action|
57
- data = MultiJson.load(request.body.read)
58
- dispatch_external_event(task_id, data)
59
- end
60
-
61
- get "/tasks/operations" do
62
- TaskLauncherRegistry.operations.to_json
63
- end
64
-
65
- private
66
-
67
- def callback_host(params, request)
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
79
- end
80
- end
81
- end
@@ -1,31 +0,0 @@
1
- module SmartProxyDynflowCore
2
- class BundlerHelper
3
- # rubocop:disable Metrics/PerceivedComplexity
4
- def self.require_groups(*groups)
5
- if File.exist?(File.expand_path('../../../Gemfile.in', __FILE__))
6
- # If there is a Gemfile.in file, we will not use Bundler but BundlerExt
7
- # gem which parses this file and loads all dependencies from the system
8
- # rathern then trying to download them from rubygems.org. It always
9
- # loads all gemfile groups.
10
- begin
11
- require 'bundler_ext' unless defined?(BundlerExt)
12
- rescue LoadError
13
- # Debian packaging guidelines state to avoid needing rubygems, so
14
- # we only try to load it if the first require fails (for RPMs)
15
- begin
16
- require 'rubygems' rescue nil
17
- require 'bundler_ext'
18
- rescue LoadError
19
- puts "`bundler_ext` gem is required to run smart_proxy"
20
- exit 1
21
- end
22
- end
23
- BundlerExt.system_require(File.expand_path('../../../Gemfile.in', __FILE__), *groups)
24
- else
25
- require 'bundler' unless defined?(Bundler)
26
- Bundler.require(*groups)
27
- end
28
- end
29
- # rubocop:enable Metrics/PerceivedComplexity
30
- end
31
- end
@@ -1,86 +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
- # 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
48
- end
49
-
50
- def callback(payload)
51
- response = callback_resource.post(payload, :content_type => :json)
52
- if response.code.to_s != "200"
53
- raise "Failed performing callback to Foreman server: #{response.code} #{response.body}"
54
- end
55
- response
56
- end
57
-
58
- private
59
-
60
- def callback_resource
61
- @resource ||= RestClient::Resource.new(Settings.instance.foreman_url + '/foreman_tasks/api/tasks/callback',
62
- self.class.ssl_options)
63
- end
64
- end
65
-
66
- class Action < ::Dynflow::Action
67
- def plan(callback, data)
68
- plan_self(:callback => callback, :data => data)
69
- end
70
-
71
- def run
72
- Callback::Request.send_to_foreman_tasks(input[:callback], input[:data])
73
- end
74
- end
75
-
76
- module PlanHelper
77
- def plan_with_callback(input)
78
- input = input.dup
79
- callback = input.delete('callback')
80
-
81
- planned_action = plan_self(input)
82
- plan_action(Callback::Action, callback, planned_action.output) if callback
83
- end
84
- end
85
- end
86
- end
@@ -1,122 +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
- ::Dynflow::World.new(config)
19
- end
20
-
21
- def persistence_conn_string
22
- return ENV['DYNFLOW_DB_CONN_STRING'] if ENV.key? 'DYNFLOW_DB_CONN_STRING'
23
- db_conn_string = 'sqlite:/'
24
-
25
- db_file = Settings.instance.database
26
- if db_file.nil? || db_file.empty?
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
- else
30
- db_conn_string += "/#{db_file}"
31
- end
32
-
33
- db_conn_string
34
- end
35
-
36
- def persistence_adapter
37
- ::Dynflow::PersistenceAdapters::Sequel.new persistence_conn_string
38
- end
39
-
40
- def default_world_config
41
- ::Dynflow::Config.new.tap do |config|
42
- config.auto_rescue = true
43
- config.logger_adapter = logger_adapter
44
- config.persistence_adapter = persistence_adapter
45
- config.execution_plan_cleaner = execution_plan_cleaner
46
- # TODO: There has to be a better way
47
- matchers = config.silent_dead_letter_matchers.call.concat(self.class.silencer_matchers)
48
- config.silent_dead_letter_matchers = matchers
49
- yield config if block_given?
50
- end
51
- end
52
-
53
- def logger_adapter
54
- if Settings.instance.standalone
55
- Log::ProxyAdapter.new(Log.instance, Log.instance.level)
56
- else
57
- Log::ProxyAdapter.new(Proxy::LogBuffer::Decorator.instance, Log.instance.level)
58
- end
59
- end
60
-
61
- def execution_plan_cleaner
62
- proc do |world|
63
- age = Settings.instance.execution_plan_cleaner_age
64
- options = { :poll_interval => age, :max_age => age }
65
- ::Dynflow::Actors::ExecutionPlanCleaner.new(world, options)
66
- end
67
- end
68
-
69
- class << self
70
- attr_reader :instance
71
-
72
- def ensure_initialized
73
- return @instance if @instance
74
- @instance = Core.new
75
- after_initialize_blocks.each { |block| block.call(@instance) }
76
- @instance
77
- end
78
-
79
- def silencer_matchers
80
- @matchers ||= []
81
- end
82
-
83
- def register_silencer_matchers(matchers)
84
- silencer_matchers.concat matchers
85
- end
86
-
87
- def web_console
88
- require 'dynflow/web'
89
- dynflow_console = ::Dynflow::Web.setup do
90
- # we can't use the proxy's after_activation hook, as
91
- # it happens before the Daemon forks the process (including
92
- # closing opened file descriptors)
93
- # TODO: extend smart proxy to enable hooks that happen after
94
- # the forking
95
- helpers Helpers
96
-
97
- before do
98
- authorize_with_ssl_client if Settings.instance.console_auth
99
- end
100
-
101
- Core.ensure_initialized
102
- set :world, Core.world
103
- end
104
- dynflow_console
105
- end
106
-
107
- def world
108
- instance.world
109
- end
110
-
111
- def after_initialize(&block)
112
- after_initialize_blocks << block
113
- end
114
-
115
- private
116
-
117
- def after_initialize_blocks
118
- @after_initialize_blocks ||= []
119
- end
120
- end
121
- end
122
- 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,167 +0,0 @@
1
- require 'webrick/https'
2
- require 'smart_proxy_dynflow_core/bundler_helper'
3
- require 'smart_proxy_dynflow_core/settings'
4
- require 'smart_proxy_dynflow_core/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
- load_settings!(options)
18
- Settings.instance.standalone = true
19
- install_usr1_trap
20
- Rack::Server.new(rack_settings).start do |_server|
21
- SmartProxyDynflowCore::Core.ensure_initialized
22
- SmartProxyDynflowCore::SdNotify.new.tap { |sd| sd.ready if sd.active? }
23
- end
24
- end
25
-
26
- def load_settings!(options = {})
27
- config_dir, one_config = options.values_at(:config_dir, :one_config)
28
- possible_config_dirs = [
29
- '/etc/smart_proxy_dynflow_core',
30
- File.expand_path('~/.config/smart_proxy_dynflow_core'),
31
- File.join(File.dirname(__FILE__), '..', '..', 'config'),
32
- ]
33
- possible_config_dirs << config_dir if config_dir
34
- BundlerHelper.require_groups(:default)
35
- possible_config_dirs.reverse! if one_config
36
- possible_config_dirs.select { |config_dir| File.directory? config_dir }.each do |config_dir|
37
- break if load_config_dir(config_dir) && one_config
38
- end
39
- Settings.instance.daemonize = options[:daemonize] if options.key?(:daemonize)
40
- Settings.instance.pid_file = options[:pid_file] if options.key?(:pid_file)
41
- Settings.loaded!
42
- end
43
-
44
- def self.route_mapping(rack_builder)
45
- rack_builder.map '/console' do
46
- run Core.web_console
47
- end
48
-
49
- rack_builder.map '/' do
50
- run Api
51
- end
52
- end
53
-
54
- def install_usr1_trap
55
- trap(:USR1) do
56
- Log.instance.roll_log
57
- end
58
- end
59
-
60
- private
61
-
62
- def rack_settings
63
- settings = if https_enabled?
64
- Log.instance.debug "Using HTTPS"
65
- https_app
66
- else
67
- Log.instance.debug "Using HTTP"
68
- {}
69
- end
70
- settings.merge(base_settings)
71
- end
72
-
73
- def app
74
- Rack::Builder.new do
75
- SmartProxyDynflowCore::Launcher.route_mapping(self)
76
- end
77
- end
78
-
79
- def base_settings
80
- {
81
- :app => app,
82
- :Host => Settings.instance.listen,
83
- :Port => Settings.instance.port,
84
- :AccessLog => [[Log.instance, WEBrick::AccessLog::COMMON_LOG_FORMAT]],
85
- :Logger => Log.instance,
86
- :daemonize => Settings.instance.daemonize,
87
- :pid => Settings.instance.daemonize && Settings.instance.pid_file,
88
- :server => :webrick
89
- }
90
- end
91
-
92
- # rubocop:disable Metrics/PerceivedComplexity
93
- def https_app
94
- ssl_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options]
95
- ssl_options |= OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE if defined?(OpenSSL::SSL::OP_CIPHER_SERVER_PREFERENCE)
96
- # This is required to disable SSLv3 on Ruby 1.8.7
97
- ssl_options |= OpenSSL::SSL::OP_NO_SSLv2 if defined?(OpenSSL::SSL::OP_NO_SSLv2)
98
- ssl_options |= OpenSSL::SSL::OP_NO_SSLv3 if defined?(OpenSSL::SSL::OP_NO_SSLv3)
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)
101
-
102
- if Settings.instance.tls_disabled_versions
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
- end
114
-
115
- {
116
- :SSLEnable => true,
117
- :SSLVerifyClient => OpenSSL::SSL::VERIFY_PEER,
118
- :SSLPrivateKey => ssl_private_key,
119
- :SSLCertificate => ssl_certificate,
120
- :SSLCACertificateFile => Settings.instance.ssl_ca_file,
121
- :SSLCiphers => CIPHERS - SmartProxyDynflowCore::Settings.instance.ssl_disabled_ciphers,
122
- :SSLOptions => ssl_options
123
- }
124
- end
125
- # rubocop:enable Metrics/PerceivedComplexity
126
-
127
- def https_enabled?
128
- Settings.instance.use_https
129
- end
130
-
131
- def ssl_private_key
132
- OpenSSL::PKey::RSA.new(File.read(Settings.instance.ssl_private_key))
133
- rescue Exception => e
134
- Log.instance.fatal "Unable to load private SSL key. Are the values "\
135
- "correct in settings.yml and do permissions allow reading?: #{e}"
136
- raise e
137
- end
138
-
139
- def ssl_certificate
140
- OpenSSL::X509::Certificate.new(File.read(Settings.instance.ssl_certificate))
141
- rescue Exception => e
142
- Log.instance.fatal "Unable to load SSL certificate. Are the values " \
143
- "correct in settings.yml and do permissions allow reading?: #{e}"
144
- raise e
145
- end
146
-
147
- def load_config_dir(dir)
148
- settings_yml = File.join(dir, 'settings.yml')
149
- if File.exist? settings_yml
150
- Log.instance.debug "Loading settings from #{dir}"
151
- Settings.load_global_settings settings_yml
152
- Dir[File.join(dir, 'settings.d', '*.yml')].each { |path| Settings.load_plugin_settings(path) }
153
- true
154
- end
155
- ForemanTasksCore::SettingsLoader.settings_registry.each_key do |settings_keys|
156
- settings = settings_keys.inject({}) do |h, settings_key|
157
- if SETTINGS.plugins.key?(settings_key.to_s)
158
- h.merge(SETTINGS.plugins[settings_key.to_s].to_h)
159
- else
160
- h
161
- end
162
- end
163
- ForemanTasksCore::SettingsLoader.setup_settings(settings_keys.first, settings)
164
- end
165
- end
166
- end
167
- end
@@ -1,86 +0,0 @@
1
- require 'logger'
2
-
3
- module SmartProxyDynflowCore
4
- class Log < ::Logger
5
- alias_method :write, :debug
6
-
7
- class << self
8
- def instance
9
- if @logger.nil?
10
- @logger = self.new log_file
11
- @logger.level = log_level
12
- end
13
- @logger
14
- end
15
-
16
- def instance=(logger)
17
- @logger = logger
18
- end
19
-
20
- def reload!
21
- @logger = nil
22
- instance
23
- end
24
-
25
- def log_level
26
- if Settings.instance.loaded && Settings.instance.log_level
27
- ::Logger.const_get(Settings.instance.log_level.upcase)
28
- else
29
- Logger::WARN
30
- end
31
- end
32
-
33
- def log_file
34
- if Settings.instance.loaded && Settings.instance.log_file
35
- Settings.instance.log_file
36
- else
37
- $stdout
38
- end
39
- end
40
- end
41
-
42
- def initialize(file, *rest)
43
- @file = file
44
- @fd = @file.is_a?(IO) ? @file : File.open(@file, 'a')
45
- @fd.sync = true
46
- super(@fd, rest)
47
- end
48
-
49
- def roll_log
50
- unless @file.is_a? IO
51
- @fd.reopen @file, 'a'
52
- @fd.sync = true
53
- end
54
- end
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
-
76
- class ProxyAdapter < ::Dynflow::LoggerAdapters::Simple
77
- def initialize(logger, level = Logger::DEBUG, _formatters = [])
78
- @logger = logger
79
- @logger.level = level
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])
83
- end
84
- end
85
- end
86
- end
@@ -1,33 +0,0 @@
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
@@ -1,104 +0,0 @@
1
- require 'ostruct'
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
- module SmartProxyDynflowCore
21
- class Settings < OpenStruct
22
- DEFAULT_SETTINGS = {
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
46
-
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
52
-
53
- def initialize(settings = {})
54
- super(DEFAULT_SETTINGS.merge(settings))
55
- end
56
-
57
- def self.instance
58
- SmartProxyDynflowCore::SETTINGS
59
- end
60
-
61
- def self.load_global_settings(path)
62
- if File.exist? File.join(path)
63
- YAML.load_file(path).each do |key, value|
64
- SETTINGS[key] = value
65
- end
66
- end
67
- end
68
-
69
- def self.loaded!
70
- Settings.instance.loaded = true
71
- Log.instance.info 'Settings loaded, reloading logger'
72
- Log.reload!
73
- end
74
-
75
- def self.load_from_proxy(plugin)
76
- plugin_class = if Proxy::VERSION >= '1.16.0'
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
83
- PROXY_SETTINGS.each do |key|
84
- SETTINGS[key] = Proxy::SETTINGS[key]
85
- end
86
- PLUGIN_SETTINGS.each do |key|
87
- SETTINGS[key] = settings[key] if settings.key?(key)
88
- end
89
- SETTINGS.plugins.values.each(&:load_settings_from_proxy)
90
- Settings.loaded!
91
- end
92
-
93
- def self.load_plugin_settings(path)
94
- settings = YAML.load_file(path)
95
- name = File.basename(path).gsub(/\.yml$/, '')
96
- if SETTINGS.plugins.key? name
97
- settings = SETTINGS.plugins[name].to_h.merge(settings)
98
- end
99
- SETTINGS.plugins[name] = OpenStruct.new settings
100
- end
101
- end
102
- end
103
-
104
- 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!