smart_proxy_dynflow 0.2.1 → 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
- SHA1:
3
- metadata.gz: ebb495fc4f587cc0d94521bec26020bad3948109
4
- data.tar.gz: 87a7ba0afc1876f92f336edbf5b38e523de11101
2
+ SHA256:
3
+ metadata.gz: b3c381a5358391ea92bacf8c59c3a279b49a50a49369bd962c0ff94e7c4d459e
4
+ data.tar.gz: 3d83db86f0ce2a76b5d1e1f4ca5afd57eb82a5b327ba1ad2f561c77217e4ae0f
5
5
  SHA512:
6
- metadata.gz: 1764f5ec0797cbafa4182dd848f7faffb3aae8cdc6053cf08e127cbd0ff9a654acd6994b985caea64267cceea3cde1256bddfb3d232f359b933c0f546c34ced9
7
- data.tar.gz: 65494db608d5c67aed31219afb9c289ca900ed26911b034e1bf55dcb1472f17dab1d7537539f54f28407484d94e7ed27052e0a2a5f007dbc92392662b5641ee2
6
+ metadata.gz: da9ce875255e93fcc491fc438eb6cbe3fb0b803b6524539a71575f47b29e0a65bcffb1378a6bacc3eee85e4c5194a3d0e3a9f77faff3fe5a82211f4242f671ea
7
+ data.tar.gz: 55b4f92fdf94ca2b0cde9282f02be12d167674bd358729bd26a766d320a45de0dcc2f8535d94dc31b164963691e076b6b3740c60f385b4f9ca18b2d06c8abf82
data/Gemfile CHANGED
@@ -7,32 +7,17 @@ group :development do
7
7
  end
8
8
 
9
9
  group :test do
10
- gem 'smart_proxy_dynflow', :path => '.'
11
10
  gem 'smart_proxy', :git => "https://github.com/theforeman/smart-proxy", :branch => "develop"
11
+ gem 'smart_proxy_dynflow', :path => '.'
12
12
 
13
- if RUBY_VERSION < '2.1'
14
- gem 'public_suffix', '< 3'
15
- gem 'rubocop', '< 0.51.0'
16
- gem 'rainbow', '< 3'
17
- else
18
- gem 'rubocop', '~> 0.52.1'
19
- gem 'public_suffix'
20
- end
21
-
22
- if RUBY_VERSION < '2.2'
23
- gem 'rack-test', '< 0.8'
24
- else
25
- gem 'rack-test'
26
- end
13
+ gem 'public_suffix'
14
+ gem 'rack-test'
15
+ gem 'rubocop', '~> 0.52.1'
27
16
  end
28
17
 
29
- if RUBY_VERSION < '2.2'
30
- gem 'sinatra', '< 2'
31
- gem 'rack', '>= 1.1', '< 2.0.0'
32
- else
33
- gem 'sinatra'
34
- gem 'rack', '>= 1.1'
35
- end
18
+ gem 'logging-journald', '~> 2.0', :platforms => [:ruby], :require => false
19
+ gem 'rack', '>= 1.1'
20
+ gem 'sinatra'
36
21
 
37
22
  # load bundler.d
38
23
  Dir["#{File.dirname(__FILE__)}/bundler.d/*.rb"].each do |bundle|
@@ -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.env['PATH_INFO'].end_with?('/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
 
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 })
33
+ end
34
+ MultiJson.dump(result)
35
+ end
23
36
 
24
- # TODO: move this to foreman-proxy to reduce code duplicities
25
- def do_authorize_with_trusted_hosts
26
- # When :trusted_hosts is given, we check the client against the list
27
- # HTTPS: test the certificate CN
28
- # HTTP: test the reverse DNS entry of the remote IP
29
- trusted_hosts = Proxy::SETTINGS.trusted_hosts
30
- if trusted_hosts
31
- if [ 'yes', 'on', 1 ].include? request.env['HTTPS'].to_s
32
- fqdn = https_cert_cn
33
- source = 'SSL_CLIENT_CERT'
34
- else
35
- fqdn = remote_fqdn(Proxy::SETTINGS.forward_verify)
36
- source = 'REMOTE_ADDR'
37
- end
38
- fqdn = fqdn.downcase
39
- logger.debug "verifying remote client #{fqdn} (based on #{source}) against trusted_hosts #{trusted_hosts}"
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
40
43
 
41
- unless Proxy::SETTINGS.trusted_hosts.include?(fqdn)
42
- log_halt 403, "Untrusted client #{fqdn} attempted to access #{request.path_info}. Check :trusted_hosts: in settings.yml"
43
- end
44
- end
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
45
48
  end
46
49
 
47
- def do_authorize_with_ssl_client
48
- if ['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
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,32 +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
- Proxy::LogBuffer::Decorator.instance.debug "Proxy request from #{request.host_with_port}#{request.path} to #{uri.to_s}#{path}"
14
- req = case request.env['REQUEST_METHOD']
15
- when 'GET'
16
- request_factory.create_get path, request.env['rack.request.query_hash']
17
- when 'POST'
18
- request_factory.create_post path, request.body.read
19
- end
20
- req['X-Forwarded-For'] = request.env['HTTP_HOST']
21
- req['AUTHORIZATION'] = request.env['HTTP_AUTHORIZATION']
22
- response = send_request req
23
- Proxy::LogBuffer::Decorator.instance.debug "Proxy request status #{response.code} - #{response}"
24
- response
35
+ private
36
+
37
+ def prepare_payload(callback, data)
38
+ { :callback => callback, :data => data }.to_json
25
39
  end
40
+ end
26
41
 
27
- def self.relay(request, from, to)
28
- 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}"
29
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
30
75
  end
31
76
  end
32
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 = /^\/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,7 +1,11 @@
1
1
  require 'smart_proxy_dynflow/api'
2
2
 
3
3
  map "/dynflow" do
4
- map '/'do
4
+ map '/console' do
5
+ run Proxy::Dynflow::Core.web_console
6
+ end
7
+
8
+ map '/' do
5
9
  run Proxy::Dynflow::Api
6
10
  end
7
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
@@ -4,27 +4,18 @@ require 'proxy/plugin'
4
4
 
5
5
  class Proxy::Dynflow
6
6
  class Plugin < Proxy::Plugin
7
- rackup_path = begin
8
- require 'smart_proxy_dynflow_core'
9
- 'http_config_with_executor.ru'
10
- rescue LoadError
11
- 'http_config.ru'
12
- end
13
- http_rackup_path File.expand_path(rackup_path, File.expand_path("../", __FILE__))
14
- https_rackup_path File.expand_path(rackup_path, File.expand_path("../", __FILE__))
7
+ rackup_path = File.expand_path('http_config.ru', __dir__)
8
+ http_rackup_path rackup_path
9
+ https_rackup_path rackup_path
15
10
 
16
11
  settings_file "dynflow.yml"
17
- requires :foreman_proxy, ">= 1.12.0"
18
- default_settings :core_url => 'http://localhost:8008'
12
+ requires :foreman_proxy, ">= 1.16.0"
13
+ default_settings :console_auth => true,
14
+ :execution_plan_cleaner_age => 60 * 60 * 24
19
15
  plugin :dynflow, Proxy::Dynflow::VERSION
20
16
 
21
17
  after_activation do
22
- begin
23
- require 'smart_proxy_dynflow_core'
24
- rescue LoadError => e
25
- # Dynflow core is not available in the proxy, will be handled
26
- # by standalone Dynflow core
27
- end
18
+ Proxy::Dynflow::Core.ensure_initialized
28
19
  end
29
20
  end
30
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.2.1'
3
+ VERSION = '0.4.0'.freeze
4
4
  end
5
5
  end
@@ -1,4 +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'
4
+
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,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_proxy_dynflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
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: 2018-09-20 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
- name: bundler
14
+ name: logging
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.7'
20
- type: :development
19
+ version: '0'
20
+ type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.7'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: '1.7'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - "~>"
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: '1.7'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: minitest
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -67,33 +67,47 @@ dependencies:
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1'
69
69
  - !ruby/object:Gem::Dependency
70
- name: webmock
70
+ name: rack-test
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '1'
75
+ version: '0'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '1'
82
+ version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rack-test
84
+ name: rake
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: '10.0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: webmock
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1'
97
111
  description: " Use the Dynflow inside Foreman smart proxy\n"
98
112
  email:
99
113
  - inecas@redhat.com
@@ -107,35 +121,39 @@ files:
107
121
  - lib/smart_proxy_dynflow.rb
108
122
  - lib/smart_proxy_dynflow/api.rb
109
123
  - lib/smart_proxy_dynflow/callback.rb
124
+ - lib/smart_proxy_dynflow/core.rb
110
125
  - lib/smart_proxy_dynflow/helpers.rb
111
126
  - lib/smart_proxy_dynflow/http_config.ru
112
- - lib/smart_proxy_dynflow/http_config_with_executor.ru
127
+ - lib/smart_proxy_dynflow/log.rb
128
+ - lib/smart_proxy_dynflow/middleware/keep_current_request_id.rb
113
129
  - lib/smart_proxy_dynflow/plugin.rb
114
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
115
134
  - lib/smart_proxy_dynflow/version.rb
116
135
  - settings.d/dynflow.yml.example
117
136
  homepage: https://github.com/theforeman/smart_proxy_dynflow
118
137
  licenses:
119
138
  - GPL-3.0
120
139
  metadata: {}
121
- post_install_message:
140
+ post_install_message:
122
141
  rdoc_options: []
123
142
  require_paths:
124
143
  - lib
125
144
  required_ruby_version: !ruby/object:Gem::Requirement
126
145
  requirements:
127
- - - ">="
146
+ - - "~>"
128
147
  - !ruby/object:Gem::Version
129
- version: '0'
148
+ version: '2.5'
130
149
  required_rubygems_version: !ruby/object:Gem::Requirement
131
150
  requirements:
132
151
  - - ">="
133
152
  - !ruby/object:Gem::Version
134
153
  version: '0'
135
154
  requirements: []
136
- rubyforge_project:
137
- rubygems_version: 2.6.12
138
- signing_key:
155
+ rubygems_version: 3.1.2
156
+ signing_key:
139
157
  specification_version: 4
140
158
  summary: Dynflow runtime for Foreman smart proxy
141
159
  test_files: []
@@ -1,8 +0,0 @@
1
- require 'smart_proxy_dynflow_core/api'
2
- require 'smart_proxy_dynflow_core/launcher'
3
-
4
- SmartProxyDynflowCore::Settings.load_from_proxy(p)
5
-
6
- map "/dynflow" do
7
- SmartProxyDynflowCore::Launcher.route_mapping(self)
8
- end