smart_proxy_dynflow 0.3.0 → 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: 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