smart_proxy_dynflow 0.3.0 → 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: ed31ab888b7a2f5fbc4891fe60a0332f5a43347a15175f12f96a3215c0e73447
4
- data.tar.gz: 534c395d634b227cd5570687e83e3e6d227a8f1898b6fed76a7760c6c66a33e2
3
+ metadata.gz: b3c381a5358391ea92bacf8c59c3a279b49a50a49369bd962c0ff94e7c4d459e
4
+ data.tar.gz: 3d83db86f0ce2a76b5d1e1f4ca5afd57eb82a5b327ba1ad2f561c77217e4ae0f
5
5
  SHA512:
6
- metadata.gz: a1836fa6ab0f19b43b7321d133d69632bb248ceec3e113a46952c5fc0e5f5170f29fcec0cfd52ade22db7755dcc727c03bb8f1184215787de9e37beaffc3c901
7
- data.tar.gz: 3a20420452784df395b61cb3f75bba8ef88e8627fd984d17894e9a7003262d74b9beda70ab41eff0843e6816e8c74dc798f89b4b97b4f93d80e1dc7436ac5763
6
+ metadata.gz: da9ce875255e93fcc491fc438eb6cbe3fb0b803b6524539a71575f47b29e0a65bcffb1378a6bacc3eee85e4c5194a3d0e3a9f77faff3fe5a82211f4242f671ea
7
+ data.tar.gz: 55b4f92fdf94ca2b0cde9282f02be12d167674bd358729bd26a766d320a45de0dcc2f8535d94dc31b164963691e076b6b3740c60f385b4f9ca18b2d06c8abf82
data/Gemfile CHANGED
@@ -15,7 +15,7 @@ group :test do
15
15
  gem 'rubocop', '~> 0.52.1'
16
16
  end
17
17
 
18
- gem 'logging-journald', '~> 2.0', :platforms => [:ruby]
18
+ gem 'logging-journald', '~> 2.0', :platforms => [:ruby], :require => false
19
19
  gem 'rack', '>= 1.1'
20
20
  gem 'sinatra'
21
21
 
@@ -1,5 +1,25 @@
1
+ require 'dynflow'
2
+
3
+ require 'smart_proxy_dynflow/task_launcher_registry'
4
+ require 'smart_proxy_dynflow/middleware/keep_current_request_id'
5
+
6
+ require 'foreman_tasks_core'
7
+
8
+ require 'smart_proxy_dynflow/log'
9
+ require 'smart_proxy_dynflow/settings'
10
+ require 'smart_proxy_dynflow/core'
11
+ require 'smart_proxy_dynflow/callback'
12
+
1
13
  require 'smart_proxy_dynflow/version'
2
14
  require 'smart_proxy_dynflow/plugin'
3
- require 'smart_proxy_dynflow/callback'
4
15
  require 'smart_proxy_dynflow/helpers'
5
16
  require 'smart_proxy_dynflow/api'
17
+
18
+ module Proxy
19
+ class Dynflow
20
+ Core.after_initialize do |dynflow_core|
21
+ ForemanTasksCore.dynflow_setup(dynflow_core.world)
22
+ end
23
+ Core.register_silencer_matchers ForemanTasksCore.silent_dead_letter_matchers
24
+ end
25
+ end
@@ -9,57 +9,80 @@ module Proxy
9
9
  helpers ::Proxy::Log
10
10
  helpers ::Proxy::Dynflow::Helpers
11
11
 
12
+ include ::Sinatra::Authorization::Helpers
13
+
14
+ TASK_UPDATE_REGEXP_PATH = %r{/tasks/(\S+)/(update|done)}
15
+
12
16
  before do
13
- content_type :json
14
- if request.env['HTTP_AUTHORIZATION'] && request.path_info =~ %r{/tasks/.*/(update|done)}
15
- # Halt running before callbacks if a token is provided and the request is notifying about task being done
16
- return
17
+ if match = request.path_info.match(TASK_UPDATE_REGEXP_PATH)
18
+ task_id = match[1]
19
+ action = match[2]
20
+ authorize_with_token(task_id: task_id, clear: action == 'done')
17
21
  else
18
- do_authorize_with_ssl_client
19
- do_authorize_with_trusted_hosts
22
+ do_authorize_any
20
23
  end
24
+ content_type :json
21
25
  end
22
26
 
23
- # TODO: move this to foreman-proxy to reduce code duplicities
24
- def do_authorize_with_trusted_hosts
25
- # When :trusted_hosts is given, we check the client against the list
26
- # HTTPS: test the certificate CN
27
- # HTTP: test the reverse DNS entry of the remote IP
28
- trusted_hosts = Proxy::SETTINGS.trusted_hosts
29
- if trusted_hosts
30
- if ['yes', 'on', 1].include? request.env['HTTPS'].to_s
31
- fqdn = https_cert_cn
32
- source = 'SSL_CLIENT_CERT'
33
- else
34
- fqdn = remote_fqdn(Proxy::SETTINGS.forward_verify)
35
- source = 'REMOTE_ADDR'
36
- end
37
- fqdn = fqdn.downcase
38
- logger.debug "verifying remote client #{fqdn} (based on #{source}) against trusted_hosts #{trusted_hosts}"
39
-
40
- unless Proxy::SETTINGS.trusted_hosts.include?(fqdn)
41
- log_halt 403, "Untrusted client #{fqdn} attempted " \
42
- "to access #{request.path_info}. Check :trusted_hosts: in settings.yml"
43
- end
27
+ post "/tasks/status" do
28
+ params = MultiJson.load(request.body.read)
29
+ ids = params.fetch('task_ids', [])
30
+ result = world.persistence
31
+ .find_execution_plans(:filters => { :uuid => ids }).reduce({}) do |acc, plan|
32
+ acc.update(plan.id => { 'state' => plan.state, 'result' => plan.result })
44
33
  end
34
+ MultiJson.dump(result)
45
35
  end
46
36
 
47
- def do_authorize_with_ssl_client
48
- if %w[yes on 1].include? request.env['HTTPS'].to_s
49
- if request.env['SSL_CLIENT_CERT'].to_s.empty?
50
- log_halt 403, "No client SSL certificate supplied"
51
- end
52
- else
53
- logger.debug('require_ssl_client_verification: skipping, non-HTTPS request')
54
- end
37
+ post "/tasks/launch/?" do
38
+ params = MultiJson.load(request.body.read)
39
+ launcher = launcher_class(params).new(world, callback_host(params, request), params.fetch('options', {}))
40
+ launcher.launch!(params['input'])
41
+ launcher.results.to_json
42
+ end
43
+
44
+ post "/tasks/?" do
45
+ params = MultiJson.load(request.body.read)
46
+ trigger_task(::Dynflow::Utils.constantize(params['action_name']),
47
+ params['action_input'].merge(:callback_host => callback_host(params, request))).to_json
48
+ end
49
+
50
+ post "/tasks/:task_id/cancel" do |task_id|
51
+ cancel_task(task_id).to_json
55
52
  end
56
53
 
57
- post "/*" do
58
- relay_request
54
+ get "/tasks/:task_id/status" do |task_id|
55
+ task_status(task_id).to_json
59
56
  end
60
57
 
61
- get "/*" do
62
- relay_request
58
+ get "/tasks/count" do
59
+ tasks_count(params['state']).to_json
60
+ end
61
+
62
+ # capturing post "/tasks/:task_id/(update|done)"
63
+ post TASK_UPDATE_REGEXP_PATH do |task_id, _action|
64
+ data = MultiJson.load(request.body.read)
65
+ dispatch_external_event(task_id, data)
66
+ end
67
+
68
+ get "/tasks/operations" do
69
+ TaskLauncherRegistry.operations.to_json
70
+ end
71
+
72
+ private
73
+
74
+ def callback_host(params, request)
75
+ params.fetch('action_input', {})['proxy_url'] ||
76
+ request.env.values_at('HTTP_X_FORWARDED_FOR', 'HTTP_HOST').compact.first
77
+ end
78
+
79
+ def launcher_class(params)
80
+ operation = params.fetch('operation')
81
+ if TaskLauncherRegistry.key?(operation)
82
+ TaskLauncherRegistry.fetch(operation)
83
+ else
84
+ halt 404, MultiJson.dump(:error => "Unknown operation '#{operation}' requested.")
85
+ end
63
86
  end
64
87
  end
65
88
  end
@@ -1,34 +1,77 @@
1
- require 'proxy/request'
2
-
3
- module Proxy
4
- class Dynflow
5
- module Callback
6
- class Core < Proxy::HttpRequest::ForemanRequest
7
- def uri
8
- @uri ||= URI.parse Proxy::Dynflow::Plugin.settings.core_url
1
+ require 'rest-client'
2
+
3
+ class Proxy::Dynflow
4
+ module Callback
5
+ class Request
6
+ class << self
7
+ def send_to_foreman_tasks(callback_info, data)
8
+ self.new.callback(prepare_payload(callback_info, data))
9
+ end
10
+
11
+ def ssl_options
12
+ return @ssl_options if defined? @ssl_options
13
+ @ssl_options = {}
14
+ settings = Proxy::SETTINGS
15
+ return @ssl_options unless URI.parse(settings.foreman_url).scheme == 'https'
16
+
17
+ @ssl_options[:verify_ssl] = OpenSSL::SSL::VERIFY_PEER
18
+
19
+ private_key_file = settings.foreman_ssl_key || settings.ssl_private_key
20
+ if private_key_file
21
+ private_key = File.read(private_key_file)
22
+ @ssl_options[:ssl_client_key] = OpenSSL::PKey::RSA.new(private_key)
23
+ end
24
+ certificate_file = settings.foreman_ssl_cert || settings.ssl_certificate
25
+ if certificate_file
26
+ certificate = File.read(certificate_file)
27
+ @ssl_options[:ssl_client_cert] = OpenSSL::X509::Certificate.new(certificate)
28
+ end
29
+ ca_file = settings.foreman_ssl_ca || settings.ssl_ca_file
30
+ @ssl_options[:ssl_ca_file] = ca_file if ca_file
31
+ @ssl_options
9
32
  end
33
+ # rubocop:enable Metrics/PerceivedComplexity
10
34
 
11
- def relay(request, from, to)
12
- path = request.path.gsub(from, to)
13
- message = "Proxy request from #{request.host_with_port}#{request.path} to #{uri}#{path}"
14
- Proxy::LogBuffer::Decorator.instance.debug message
15
- req = case request.env['REQUEST_METHOD']
16
- when 'GET'
17
- request_factory.create_get path, request.env['rack.request.query_hash']
18
- when 'POST'
19
- request_factory.create_post path, request.body.read
20
- end
21
- req['X-Forwarded-For'] = request.env['HTTP_HOST']
22
- req['AUTHORIZATION'] = request.env['HTTP_AUTHORIZATION']
23
- req['X-Request-Id'] = ::Logging.mdc['request']
24
- response = send_request req
25
- Proxy::LogBuffer::Decorator.instance.debug "Proxy request status #{response.code} - #{response}"
26
- response
35
+ private
36
+
37
+ def prepare_payload(callback, data)
38
+ { :callback => callback, :data => data }.to_json
27
39
  end
40
+ end
28
41
 
29
- def self.relay(request, from, to)
30
- self.new.relay request, from, to
42
+ def callback(payload)
43
+ response = callback_resource.post(payload, :content_type => :json)
44
+ if response.code.to_s != "200"
45
+ raise "Failed performing callback to Foreman server: #{response.code} #{response.body}"
31
46
  end
47
+ response
48
+ end
49
+
50
+ private
51
+
52
+ def callback_resource
53
+ @resource ||= RestClient::Resource.new(Proxy::SETTINGS.foreman_url + '/foreman_tasks/api/tasks/callback',
54
+ self.class.ssl_options)
55
+ end
56
+ end
57
+
58
+ class Action < ::Dynflow::Action
59
+ def plan(callback, data)
60
+ plan_self(:callback => callback, :data => data)
61
+ end
62
+
63
+ def run
64
+ Callback::Request.send_to_foreman_tasks(input[:callback], input[:data])
65
+ end
66
+ end
67
+
68
+ module PlanHelper
69
+ def plan_with_callback(input)
70
+ input = input.dup
71
+ callback = input.delete('callback')
72
+
73
+ planned_action = plan_self(input)
74
+ plan_action(Callback::Action, callback, planned_action.output) if callback
32
75
  end
33
76
  end
34
77
  end
@@ -0,0 +1,121 @@
1
+ class Proxy::Dynflow
2
+ class Core
3
+ attr_accessor :world, :accepted_cert_serial
4
+
5
+ def initialize
6
+ @world = create_world
7
+ cert_file = Proxy::SETTINGS.foreman_ssl_cert || Proxy::SETTINGS.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
+ world = ::Dynflow::World.new(config)
19
+ world.middleware.use ::Actions::Middleware::KeepCurrentRequestID
20
+ world
21
+ end
22
+
23
+ def persistence_conn_string
24
+ return ENV['DYNFLOW_DB_CONN_STRING'] if ENV.key? 'DYNFLOW_DB_CONN_STRING'
25
+ db_conn_string = 'sqlite:/'
26
+
27
+ db_file = Settings.instance.database
28
+ if db_file.nil? || db_file.empty?
29
+ Log.instance.warn "Could not open DB for dynflow at '#{db_file}', " \
30
+ "will keep data in memory. Restart will drop all dynflow data."
31
+ else
32
+ db_conn_string += "/#{db_file}"
33
+ end
34
+
35
+ db_conn_string
36
+ end
37
+
38
+ def persistence_adapter
39
+ ::Dynflow::PersistenceAdapters::Sequel.new persistence_conn_string
40
+ end
41
+
42
+ def default_world_config
43
+ ::Dynflow::Config.new.tap do |config|
44
+ config.auto_rescue = true
45
+ config.logger_adapter = logger_adapter
46
+ config.persistence_adapter = persistence_adapter
47
+ config.execution_plan_cleaner = execution_plan_cleaner
48
+ # TODO: There has to be a better way
49
+ matchers = config.silent_dead_letter_matchers.call.concat(self.class.silencer_matchers)
50
+ config.silent_dead_letter_matchers = matchers
51
+ yield config if block_given?
52
+ end
53
+ end
54
+
55
+ def logger_adapter
56
+ Log::ProxyAdapter.new(Proxy::LogBuffer::Decorator.instance, Log.instance.level)
57
+ end
58
+
59
+ def execution_plan_cleaner
60
+ proc do |world|
61
+ age = Settings.instance.execution_plan_cleaner_age
62
+ options = { :poll_interval => age, :max_age => age }
63
+ ::Dynflow::Actors::ExecutionPlanCleaner.new(world, options)
64
+ end
65
+ end
66
+
67
+ class << self
68
+ attr_reader :instance
69
+
70
+ def ensure_initialized
71
+ return @instance if @instance
72
+ @instance = Core.new
73
+ after_initialize_blocks.each { |block| block.call(@instance) }
74
+ @instance
75
+ end
76
+
77
+ def silencer_matchers
78
+ @matchers ||= []
79
+ end
80
+
81
+ def register_silencer_matchers(matchers)
82
+ silencer_matchers.concat matchers
83
+ end
84
+
85
+ def web_console
86
+ require 'dynflow/web'
87
+ dynflow_console = ::Dynflow::Web.setup do
88
+ # we can't use the proxy's after_activation hook, as
89
+ # it happens before the Daemon forks the process (including
90
+ # closing opened file descriptors)
91
+ # TODO: extend smart proxy to enable hooks that happen after
92
+ # the forking
93
+ helpers Helpers
94
+ include ::Sinatra::Authorization::Helpers
95
+
96
+ before do
97
+ do_authorize_with_ssl_client if Settings.instance.console_auth
98
+ end
99
+
100
+ Core.ensure_initialized
101
+ set :world, Core.world
102
+ end
103
+ dynflow_console
104
+ end
105
+
106
+ def world
107
+ instance.world
108
+ end
109
+
110
+ def after_initialize(&block)
111
+ after_initialize_blocks << block
112
+ end
113
+
114
+ private
115
+
116
+ def after_initialize_blocks
117
+ @after_initialize_blocks ||= []
118
+ end
119
+ end
120
+ end
121
+ end
@@ -1,11 +1,57 @@
1
1
  module Proxy
2
2
  class Dynflow
3
3
  module Helpers
4
- def relay_request(from = %r{^/dynflow}, to = '')
5
- response = Proxy::Dynflow::Callback::Core.relay(request, from, to)
6
- content_type response.content_type
7
- status response.code
8
- body response.body
4
+ def world
5
+ Proxy::Dynflow::Core.world
6
+ end
7
+
8
+ def authorize_with_token(task_id:, clear: true)
9
+ if request.env.key? 'HTTP_AUTHORIZATION'
10
+ if defined?(::ForemanTasksCore)
11
+ auth = request.env['HTTP_AUTHORIZATION']
12
+ basic_prefix = /\ABasic /
13
+ if !auth.to_s.empty? && auth =~ basic_prefix &&
14
+ ForemanTasksCore::OtpManager.authenticate(auth.gsub(basic_prefix, ''),
15
+ expected_user: task_id, clear: clear)
16
+ Log.instance.debug('authorized with token')
17
+ return true
18
+ end
19
+ end
20
+ halt 403, MultiJson.dump(:error => 'Invalid username or password supplied')
21
+ end
22
+ false
23
+ end
24
+
25
+ def trigger_task(*args)
26
+ triggered = world.trigger(*args)
27
+ { :task_id => triggered.id }
28
+ end
29
+
30
+ def cancel_task(task_id)
31
+ execution_plan = world.persistence.load_execution_plan(task_id)
32
+ cancel_events = execution_plan.cancel
33
+ { :task_id => task_id, :canceled_steps_count => cancel_events.size }
34
+ end
35
+
36
+ def task_status(task_id)
37
+ ep = world.persistence.load_execution_plan(task_id)
38
+ ep.to_hash.merge(:actions => ep.actions.map(&:to_hash))
39
+ rescue KeyError => _e
40
+ status 404
41
+ {}
42
+ end
43
+
44
+ def tasks_count(state)
45
+ state ||= 'all'
46
+ filter = state != 'all' ? { :filters => { :state => [state] } } : {}
47
+ tasks = world.persistence.find_execution_plans(filter)
48
+ { :count => tasks.count, :state => state }
49
+ end
50
+
51
+ def dispatch_external_event(task_id, params)
52
+ world.event(task_id,
53
+ params['step_id'].to_i,
54
+ ::ForemanTasksCore::Runner::ExternalEvent.new(params))
9
55
  end
10
56
  end
11
57
  end
@@ -1,21 +1,11 @@
1
- # Internal core will be used if external core is either disabled or unset
2
- # and the core gem can be loaded
1
+ require 'smart_proxy_dynflow/api'
3
2
 
4
- if !::Proxy::Dynflow::Plugin.settings.external_core && Proxy::Dynflow::Plugin.internal_core_available?
5
- require 'smart_proxy_dynflow_core/api'
6
- require 'smart_proxy_dynflow_core/launcher'
7
-
8
- SmartProxyDynflowCore::Settings.load_from_proxy(p)
9
-
10
- map "/dynflow" do
11
- SmartProxyDynflowCore::Launcher.route_mapping(self)
3
+ map "/dynflow" do
4
+ map '/console' do
5
+ run Proxy::Dynflow::Core.web_console
12
6
  end
13
- else
14
- require 'smart_proxy_dynflow/api'
15
7
 
16
- map "/dynflow" do
17
- map '/' do
18
- run Proxy::Dynflow::Api
19
- end
8
+ map '/' do
9
+ run Proxy::Dynflow::Api
20
10
  end
21
11
  end
@@ -0,0 +1,52 @@
1
+ require 'logging'
2
+
3
+ class Proxy::Dynflow
4
+ class Log
5
+ LOGGER_NAME = 'dynflow-core'.freeze
6
+
7
+ begin
8
+ require 'syslog/logger'
9
+ @syslog_available = true
10
+ rescue LoadError
11
+ @syslog_available = false
12
+ end
13
+
14
+ class << self
15
+ def reload!
16
+ Logging.logger[LOGGER_NAME].appenders.each(&:close)
17
+ Logging.logger[LOGGER_NAME].clear_appenders
18
+ @logger = nil
19
+ instance
20
+ end
21
+
22
+ def instance
23
+ ::Proxy::LogBuffer::Decorator.instance
24
+ end
25
+ end
26
+
27
+ class ProxyStructuredFormater < ::Dynflow::LoggerAdapters::Formatters::Abstract
28
+ def format(message)
29
+ if message.is_a?(Exception)
30
+ subject = "#{message.message} (#{message.class})"
31
+ if @base.respond_to?(:exception)
32
+ @base.exception("Error details", message)
33
+ subject
34
+ else
35
+ "#{subject}\n#{message.backtrace.join("\n")}"
36
+ end
37
+ else
38
+ @original_formatter.call(severity, datetime, prog_name, message)
39
+ end
40
+ end
41
+ end
42
+
43
+ class ProxyAdapter < ::Dynflow::LoggerAdapters::Simple
44
+ def initialize(logger, level = Logger::DEBUG, _formatters = [])
45
+ @logger = logger
46
+ @logger.level = level
47
+ @action_logger = apply_formatters(ProgNameWrapper.new(@logger, ' action'), [])
48
+ @dynflow_logger = apply_formatters(ProgNameWrapper.new(@logger, 'dynflow'), [])
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,59 @@
1
+ module Actions
2
+ module Middleware
3
+ class KeepCurrentRequestID < Dynflow::Middleware
4
+ def delay(*args)
5
+ pass(*args).tap { store_current_request_id }
6
+ end
7
+
8
+ def plan(*args)
9
+ with_current_request_id do
10
+ pass(*args).tap { store_current_request_id }
11
+ end
12
+ end
13
+
14
+ def run(*args)
15
+ restore_current_request_id { pass(*args) }
16
+ end
17
+
18
+ def finalize
19
+ restore_current_request_id { pass }
20
+ end
21
+
22
+ # Run all execution plan lifecycle hooks as the original request_id
23
+ def hook(*args)
24
+ restore_current_request_id { pass(*args) }
25
+ end
26
+
27
+ private
28
+
29
+ def with_current_request_id
30
+ if action.input[:current_request_id].nil?
31
+ yield
32
+ else
33
+ restore_current_request_id { yield }
34
+ end
35
+ end
36
+
37
+ def store_current_request_id
38
+ action.input[:current_request_id] = ::Logging.mdc['request']
39
+ end
40
+
41
+ def restore_current_request_id
42
+ unless (restored_id = action.input[:current_request_id]).nil?
43
+ old_id = ::Logging.mdc['request']
44
+ if !old_id.nil? && old_id != restored_id
45
+ action.action_logger.warn('Changing request id %{request_id} to saved id %{saved_id}' % { :saved_id => restored_id, :request_id => old_id })
46
+ end
47
+ ::Logging.mdc['request'] = restored_id
48
+ end
49
+ yield
50
+ ensure
51
+ # Reset to original request id only when not nil
52
+ # Otherwise, keep the id until it's cleaned in Dynflow's run_user_code block
53
+ # so that it will stay valid for the rest of the processing of the current step
54
+ # (even outside of the middleware lifecycle)
55
+ ::Logging.mdc['request'] = old_id unless old_id.nil?
56
+ end
57
+ end
58
+ end
59
+ end
@@ -10,22 +10,12 @@ class Proxy::Dynflow
10
10
 
11
11
  settings_file "dynflow.yml"
12
12
  requires :foreman_proxy, ">= 1.16.0"
13
- default_settings :core_url => 'http://localhost:8008'
13
+ default_settings :console_auth => true,
14
+ :execution_plan_cleaner_age => 60 * 60 * 24
14
15
  plugin :dynflow, Proxy::Dynflow::VERSION
15
16
 
16
17
  after_activation do
17
- # Ensure the core gem is loaded, if configure NOT to use the external core
18
- if Proxy::Dynflow::Plugin.settings.external_core == false && !internal_core_available?
19
- raise "'smart_proxy_dynflow_core' gem is required, but not available"
20
- end
21
- end
22
-
23
- def self.internal_core_available?
24
- @core_available ||= begin
25
- require 'smart_proxy_dynflow_core'
26
- true
27
- rescue LoadError # rubocop:disable Lint/HandleExceptions
28
- end
18
+ Proxy::Dynflow::Core.ensure_initialized
29
19
  end
30
20
  end
31
21
  end
@@ -0,0 +1,9 @@
1
+ # require 'ostruct'
2
+
3
+ class Proxy::Dynflow
4
+ class Settings
5
+ def self.instance
6
+ Proxy::Dynflow::Plugin.settings
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,31 @@
1
+ class Proxy::Dynflow
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
@@ -0,0 +1,24 @@
1
+ require 'dynflow/testing'
2
+
3
+ unless defined? DYNFLOW_TESTING_LOG_LEVEL
4
+ DYNFLOW_TESTING_LOG_LEVEL = 4
5
+ end
6
+
7
+ class Proxy::Dynflow
8
+ # Helper for usage in other dependent plugins that need Dynflow
9
+ # related things, such as testing instance of world etc.
10
+ module Testing
11
+ class << self
12
+ def create_world(&block)
13
+ Core.ensure_initialized
14
+ Core.instance.create_world do |config|
15
+ config.exit_on_terminate = false
16
+ config.auto_terminate = false
17
+ config.logger_adapter = ::Dynflow::LoggerAdapters::Simple.new $stderr, DYNFLOW_TESTING_LOG_LEVEL
18
+ config.execution_plan_cleaner = nil
19
+ yield(config) if block
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -1,5 +1,5 @@
1
1
  module Proxy
2
2
  class Dynflow
3
- VERSION = '0.3.0'.freeze
3
+ VERSION = '0.4.0'.freeze
4
4
  end
5
5
  end
@@ -1,9 +1,10 @@
1
1
  ---
2
2
  :enabled: true
3
3
  :database: /var/lib/foreman-proxy/dynflow/dynflow.sqlite
4
- :core_url: 'http://127.0.0.1:8008'
5
4
 
6
- # If true, external core will be used even if the core gem is available
7
- # If false, the feature will be disabled if the core gem is not available
8
- # If unset, the process will fallback to external core if the core gem is not available
9
- # :external_core: true
5
+ # Require a valid cert to access Dynflow console
6
+ # :console_auth: true
7
+
8
+ # Maximum age of execution plans to keep before having them cleaned
9
+ # by the execution plan cleaner (in seconds), defaults to 24 hours
10
+ # :execution_plan_cleaner_age: 86400
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_proxy_dynflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
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: 1980-01-01 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: logging
@@ -121,10 +121,16 @@ files:
121
121
  - lib/smart_proxy_dynflow.rb
122
122
  - lib/smart_proxy_dynflow/api.rb
123
123
  - lib/smart_proxy_dynflow/callback.rb
124
+ - lib/smart_proxy_dynflow/core.rb
124
125
  - lib/smart_proxy_dynflow/helpers.rb
125
126
  - lib/smart_proxy_dynflow/http_config.ru
127
+ - lib/smart_proxy_dynflow/log.rb
128
+ - lib/smart_proxy_dynflow/middleware/keep_current_request_id.rb
126
129
  - lib/smart_proxy_dynflow/plugin.rb
127
130
  - lib/smart_proxy_dynflow/proxy_adapter.rb
131
+ - lib/smart_proxy_dynflow/settings.rb
132
+ - lib/smart_proxy_dynflow/task_launcher_registry.rb
133
+ - lib/smart_proxy_dynflow/testing.rb
128
134
  - lib/smart_proxy_dynflow/version.rb
129
135
  - settings.d/dynflow.yml.example
130
136
  homepage: https://github.com/theforeman/smart_proxy_dynflow