smart_proxy_dynflow_core 0.2.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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!