console_kit 1.1.0 → 1.3.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 +4 -4
- data/.DS_Store +0 -0
- data/.reek.yml.new +0 -0
- data/lib/console_kit/configuration.rb +20 -16
- data/lib/console_kit/connections/base_connection_handler.rb +36 -2
- data/lib/console_kit/connections/dashboard.rb +28 -0
- data/lib/console_kit/connections/diagnostic_helpers.rb +22 -0
- data/lib/console_kit/connections/elasticsearch_connection_handler.rb +48 -5
- data/lib/console_kit/connections/mongo_connection_handler.rb +54 -5
- data/lib/console_kit/connections/redis_connection_handler.rb +49 -11
- data/lib/console_kit/connections/sql_connection_handler.rb +49 -7
- data/lib/console_kit/connections/table_formatter.rb +37 -0
- data/lib/console_kit/connections/table_renderer.rb +49 -0
- data/lib/console_kit/console_helpers.rb +42 -16
- data/lib/console_kit/output.rb +19 -11
- data/lib/console_kit/prompt.rb +17 -10
- data/lib/console_kit/railtie.rb +3 -3
- data/lib/console_kit/setup.rb +6 -116
- data/lib/console_kit/setup_ui.rb +37 -0
- data/lib/console_kit/tenant_configurator/context_wrapper.rb +79 -0
- data/lib/console_kit/tenant_configurator.rb +64 -56
- data/lib/console_kit/tenant_orchestrator.rb +108 -0
- data/lib/console_kit/tenant_selector.rb +8 -4
- data/lib/console_kit/version.rb +1 -1
- data/lib/console_kit.rb +6 -0
- data/lib/generators/console_kit/templates/console_kit.rb +4 -0
- metadata +41 -9
- data/CHANGELOG.md +0 -109
- data/CODE_OF_CONDUCT.md +0 -132
- data/README.md +0 -163
- data/SECURITY.md +0 -31
- data/sig/console_kit.rbs +0 -4
data/lib/console_kit/output.rb
CHANGED
|
@@ -34,7 +34,7 @@ module ConsoleKit
|
|
|
34
34
|
return if silent
|
|
35
35
|
|
|
36
36
|
formatted = (type == :header ? "\n--- #{text} ---" : text)
|
|
37
|
-
print_with(type, formatted, timestamp,
|
|
37
|
+
print_with(type, formatted, timestamp: timestamp, newline: newline)
|
|
38
38
|
end
|
|
39
39
|
end
|
|
40
40
|
|
|
@@ -51,19 +51,33 @@ module ConsoleKit
|
|
|
51
51
|
puts text
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
# Backtrace prints always with timestamp, no param
|
|
55
54
|
def print_backtrace(exception)
|
|
56
55
|
return if silent
|
|
57
56
|
|
|
58
|
-
exception&.backtrace&.each
|
|
57
|
+
exception&.backtrace&.each do |line|
|
|
58
|
+
print_with(:trace, " #{line}", timestamp: true)
|
|
59
|
+
end
|
|
59
60
|
end
|
|
60
61
|
|
|
61
62
|
private
|
|
62
63
|
|
|
63
|
-
def print_with(type, text,
|
|
64
|
+
def print_with(type, text, options = {})
|
|
65
|
+
opts = options.is_a?(Hash) ? options : { timestamp: options }
|
|
66
|
+
message = build_formatted_message(type, text, opts[:timestamp])
|
|
67
|
+
|
|
68
|
+
opts.fetch(:newline, true) ? puts(message) : print(message)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def build_formatted_message(type, text, timestamp)
|
|
64
72
|
meta = TYPES.fetch(type)
|
|
65
73
|
message = build_message(text, meta[:symbol], timestamp)
|
|
66
|
-
|
|
74
|
+
colorize(message, meta[:color])
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def colorize(message, color)
|
|
78
|
+
return message unless ConsoleKit.configuration.pretty_output && color
|
|
79
|
+
|
|
80
|
+
"\e[#{color}m#{message}\e[0m"
|
|
67
81
|
end
|
|
68
82
|
|
|
69
83
|
def build_message(text, symbol, timestamp)
|
|
@@ -73,12 +87,6 @@ module ConsoleKit
|
|
|
73
87
|
def prefix_for(value) = value ? yield(value) : ''
|
|
74
88
|
def timestamp_prefix(timestamp) = prefix_for(timestamp) { Time.current.strftime('[%Y-%m-%d %H:%M:%S] ') }
|
|
75
89
|
def symbol_prefix(symbol) = prefix_for(symbol) { |sym| "#{sym} " }
|
|
76
|
-
|
|
77
|
-
def emit(message, color, newline)
|
|
78
|
-
writer = newline ? :puts : :print
|
|
79
|
-
formatted = ConsoleKit.configuration.pretty_output && color ? "\e[#{color}m#{message}\e[0m" : message
|
|
80
|
-
send(writer, formatted)
|
|
81
|
-
end
|
|
82
90
|
end
|
|
83
91
|
end
|
|
84
92
|
end
|
data/lib/console_kit/prompt.rb
CHANGED
|
@@ -18,8 +18,8 @@ module ConsoleKit
|
|
|
18
18
|
|
|
19
19
|
def apply_irb_prompt
|
|
20
20
|
conf = IRB.conf
|
|
21
|
-
conf[:PROMPT] ||= {}
|
|
22
|
-
|
|
21
|
+
prompt = conf[:PROMPT] ||= {}
|
|
22
|
+
prompt[:CONSOLE_KIT] = {
|
|
23
23
|
PROMPT_I: "#{tenant_label} %N(%m):%03n> ",
|
|
24
24
|
PROMPT_S: "#{tenant_label} %N(%m):%03n%l ",
|
|
25
25
|
PROMPT_C: "#{tenant_label} %N(%m):%03n* ",
|
|
@@ -29,14 +29,21 @@ module ConsoleKit
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def apply_pry_prompt
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
32
|
+
procs = pry_prompt_procs(tenant_label)
|
|
33
|
+
Pry.config.prompt = build_pry_prompt(procs)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def pry_prompt_procs(label)
|
|
37
|
+
[
|
|
38
|
+
proc { |obj, nest, _| "#{label} (#{obj}):#{nest}> " },
|
|
39
|
+
proc { |obj, nest, _| "#{label} (#{obj}):#{nest}* " }
|
|
40
|
+
]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def build_pry_prompt(procs)
|
|
44
|
+
return procs unless defined?(Pry::Prompt)
|
|
45
|
+
|
|
46
|
+
Pry::Prompt.try(:new, 'console_kit', 'ConsoleKit tenant prompt', procs) || procs
|
|
40
47
|
end
|
|
41
48
|
end
|
|
42
49
|
end
|
data/lib/console_kit/railtie.rb
CHANGED
|
@@ -6,10 +6,10 @@ module ConsoleKit
|
|
|
6
6
|
console do
|
|
7
7
|
ConsoleKit::Setup.setup
|
|
8
8
|
ConsoleKit::Prompt.apply
|
|
9
|
-
if defined?(Pry)
|
|
10
|
-
TOPLEVEL_BINDING.receiver.extend(ConsoleKit::ConsoleHelpers)
|
|
11
|
-
elsif defined?(IRB::ExtendCommandBundle)
|
|
9
|
+
if defined?(IRB::ExtendCommandBundle) && !defined?(Pry)
|
|
12
10
|
IRB::ExtendCommandBundle.include(ConsoleKit::ConsoleHelpers)
|
|
11
|
+
else
|
|
12
|
+
TOPLEVEL_BINDING.receiver.extend(ConsoleKit::ConsoleHelpers)
|
|
13
13
|
end
|
|
14
14
|
end
|
|
15
15
|
|
data/lib/console_kit/setup.rb
CHANGED
|
@@ -3,135 +3,25 @@
|
|
|
3
3
|
require_relative 'tenant_selector'
|
|
4
4
|
require_relative 'tenant_configurator'
|
|
5
5
|
require_relative 'output'
|
|
6
|
+
require_relative 'setup_ui'
|
|
7
|
+
require_relative 'tenant_orchestrator'
|
|
6
8
|
|
|
7
9
|
# Core Logic for initial Setup
|
|
8
10
|
module ConsoleKit
|
|
9
11
|
# Does the initial setup
|
|
10
12
|
module Setup
|
|
11
13
|
class << self
|
|
12
|
-
ENVIRONMENT_WARNINGS = {
|
|
13
|
-
'production' => -> { Output.print_error('WARNING: You are connected to a PRODUCTION environment!') },
|
|
14
|
-
'staging' => -> { Output.print_warning('You are connected to a staging environment.') }
|
|
15
|
-
}.freeze
|
|
16
|
-
|
|
17
14
|
def current_tenant = Thread.current[:console_kit_current_tenant]
|
|
18
15
|
|
|
19
16
|
def current_tenant=(val)
|
|
20
17
|
Thread.current[:console_kit_current_tenant] = val
|
|
21
18
|
end
|
|
22
19
|
|
|
23
|
-
def setup =
|
|
20
|
+
def setup = TenantOrchestrator.run
|
|
24
21
|
def tenant_setup_successful? = !current_tenant.to_s.empty?
|
|
25
|
-
|
|
26
|
-
def
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Output.silence { TenantConfigurator.configure_tenant(current_tenant) }
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def reset_current_tenant
|
|
33
|
-
return warn_no_tenants unless tenants?
|
|
34
|
-
|
|
35
|
-
key = select_tenant_key
|
|
36
|
-
return cancel_switch if key == :abort || key.blank?
|
|
37
|
-
|
|
38
|
-
clear_current_tenant
|
|
39
|
-
return skip_tenant_message if %i[exit none].include?(key)
|
|
40
|
-
|
|
41
|
-
configure(key)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
private
|
|
45
|
-
|
|
46
|
-
def run_setup
|
|
47
|
-
return if tenant_setup_successful?
|
|
48
|
-
|
|
49
|
-
ConsoleKit.configuration.validate!
|
|
50
|
-
select_and_configure
|
|
51
|
-
rescue StandardError => e
|
|
52
|
-
handle_error(e)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def select_and_configure
|
|
56
|
-
key = select_tenant_key
|
|
57
|
-
return handle_selection_result(key) if %i[exit abort none].include?(key) || key.blank?
|
|
58
|
-
|
|
59
|
-
configure(key)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def handle_selection_result(key)
|
|
63
|
-
exit_on_key if %i[exit abort].include?(key)
|
|
64
|
-
|
|
65
|
-
case key
|
|
66
|
-
when :none
|
|
67
|
-
Output.print_info('No tenant selected. Loading without tenant configuration.')
|
|
68
|
-
when nil, ''
|
|
69
|
-
Output.print_error('Tenant selection failed. Loading without tenant configuration.')
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def exit_on_key
|
|
74
|
-
Output.print_info('Exiting console...')
|
|
75
|
-
Kernel.exit
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def configure(key)
|
|
79
|
-
TenantConfigurator.configure_tenant(key)
|
|
80
|
-
return unless TenantConfigurator.configuration_success
|
|
81
|
-
|
|
82
|
-
self.current_tenant = key
|
|
83
|
-
Prompt.apply
|
|
84
|
-
print_tenant_banner(key)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def print_tenant_banner(key)
|
|
88
|
-
constants = ConsoleKit.configuration.tenants[key]&.[](:constants) || {}
|
|
89
|
-
env = constants[:environment]&.to_s&.downcase
|
|
90
|
-
Output.print_success("Tenant initialized: #{key}")
|
|
91
|
-
print_environment_warning(env) if env
|
|
92
|
-
print_active_connections
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def print_environment_warning(env) = ENVIRONMENT_WARNINGS[env]&.call
|
|
96
|
-
|
|
97
|
-
def print_active_connections
|
|
98
|
-
names = active_connection_names
|
|
99
|
-
Output.print_info("Active connections: #{names.join(', ')}") unless names.empty?
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def active_connection_names
|
|
103
|
-
ctx = context_class
|
|
104
|
-
return [] unless ctx
|
|
105
|
-
|
|
106
|
-
handlers = ConsoleKit::Connections::ConnectionManager.available_handlers(ctx)
|
|
107
|
-
handlers.map { |h| h.class.name.demodulize.delete_suffix('ConnectionHandler') }
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def tenants = ConsoleKit.configuration.tenants
|
|
111
|
-
def context_class = ConsoleKit.configuration.context_class
|
|
112
|
-
def tenants? = tenants&.any?
|
|
113
|
-
def no_tenants? = !tenants?
|
|
114
|
-
def select_tenant_key = auto_select? ? tenants.keys.first : TenantSelector.select
|
|
115
|
-
def auto_select? = single_tenant? || non_interactive?
|
|
116
|
-
def single_tenant? = tenants.size == 1
|
|
117
|
-
def non_interactive? = !$stdin.tty?
|
|
118
|
-
def warn_no_tenants = Output.print_warning('Cannot reset tenant: No tenants configured.')
|
|
119
|
-
def warn_reset = Output.print_warning("Resetting tenant: #{current_tenant}")
|
|
120
|
-
def cancel_switch = Output.print_warning('Tenant switch cancelled.')
|
|
121
|
-
def skip_tenant_message = Output.print_info('No tenant selected. Loading without tenant configuration.')
|
|
122
|
-
|
|
123
|
-
def clear_current_tenant
|
|
124
|
-
if current_tenant
|
|
125
|
-
warn_reset
|
|
126
|
-
TenantConfigurator.clear
|
|
127
|
-
end
|
|
128
|
-
self.current_tenant = nil
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
def handle_error(error)
|
|
132
|
-
Output.print_error("Error setting up tenant: #{error.message}")
|
|
133
|
-
Output.print_backtrace(error)
|
|
134
|
-
end
|
|
22
|
+
def reapply = TenantOrchestrator.reapply
|
|
23
|
+
def reset_current_tenant = TenantOrchestrator.reset
|
|
24
|
+
def auto_select? = TenantOrchestrator.auto_select?
|
|
135
25
|
end
|
|
136
26
|
end
|
|
137
27
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConsoleKit
|
|
4
|
+
# UI helpers for Setup
|
|
5
|
+
module SetupUI
|
|
6
|
+
ENVIRONMENT_WARNINGS = {
|
|
7
|
+
'production' => -> { Output.print_error('!!! CAUTION: YOU ARE IN PRODUCTION ENVIRONMENT !!!') },
|
|
8
|
+
'staging' => -> { Output.print_warning('CAUTION: You are in staging environment.') }
|
|
9
|
+
}.freeze
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
def print_tenant_banner(key, config)
|
|
13
|
+
Output.print_success("Tenant initialized: #{key}")
|
|
14
|
+
print_env_warning(key, config)
|
|
15
|
+
print_active_connections
|
|
16
|
+
ConsoleKit::Connections::Dashboard.display if config.show_dashboard
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def print_env_warning(key, config)
|
|
22
|
+
constants = config.tenants[key]&.[](:constants) || {}
|
|
23
|
+
env = constants[:environment]&.to_s&.downcase
|
|
24
|
+
ENVIRONMENT_WARNINGS[env]&.call if env
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def print_active_connections
|
|
28
|
+
ctx = ConsoleKit.configuration.context_class
|
|
29
|
+
active = Connections::ConnectionManager.available_handlers(ctx).map do |handler|
|
|
30
|
+
handler.class.name.demodulize.delete_suffix('ConnectionHandler')
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Output.print_info("Active connections: #{active.join(', ')}") if active.any?
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConsoleKit
|
|
4
|
+
module TenantConfigurator
|
|
5
|
+
# Encapsulates context and attributes to resolve DataClump smells
|
|
6
|
+
class ContextWrapper
|
|
7
|
+
HANDLER_ATTRIBUTES = {
|
|
8
|
+
Connections::SqlConnectionHandler => :tenant_shard,
|
|
9
|
+
Connections::MongoConnectionHandler => :tenant_mongo_db,
|
|
10
|
+
Connections::RedisConnectionHandler => :tenant_redis_db,
|
|
11
|
+
Connections::ElasticsearchConnectionHandler => :tenant_elasticsearch_prefix
|
|
12
|
+
}.freeze
|
|
13
|
+
|
|
14
|
+
attr_reader :ctx, :attributes
|
|
15
|
+
|
|
16
|
+
class << self
|
|
17
|
+
def for_context(ctx)
|
|
18
|
+
new(ctx, detect_attributes(ctx))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def detect_attributes(ctx)
|
|
24
|
+
methods = ctx.public_methods
|
|
25
|
+
partner_attrs(methods) + handler_attrs(methods)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def partner_attrs(methods)
|
|
29
|
+
methods.include?(:partner_identifier=) ? [:partner_identifier] : []
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def handler_attrs(methods)
|
|
33
|
+
HANDLER_ATTRIBUTES.each_with_object([]) do |(handler, attr), list|
|
|
34
|
+
next unless methods.include?(:"#{attr}=")
|
|
35
|
+
next unless handler_available?(handler)
|
|
36
|
+
|
|
37
|
+
list << attr
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def handler_available?(handler_class)
|
|
42
|
+
handler_class.new(nil).available?
|
|
43
|
+
rescue NotImplementedError, StandardError
|
|
44
|
+
false
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def initialize(ctx, attributes)
|
|
49
|
+
@ctx = ctx
|
|
50
|
+
@attributes = attributes
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def any_set?
|
|
54
|
+
attributes.any? { |attr| ctx.public_send(attr).present? }
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def reset
|
|
58
|
+
attributes.each { |attr| ctx.public_send("#{attr}=", nil) }
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def assign(constant, mapping)
|
|
62
|
+
attributes.map do |attr|
|
|
63
|
+
existing = safe_read(attr)
|
|
64
|
+
new_value = constant[mapping[attr]]
|
|
65
|
+
ctx.public_send("#{attr}=", new_value)
|
|
66
|
+
[attr, existing, new_value]
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def safe_read(attr)
|
|
73
|
+
ctx.public_send(attr)
|
|
74
|
+
rescue StandardError
|
|
75
|
+
nil
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -2,53 +2,71 @@
|
|
|
2
2
|
|
|
3
3
|
require_relative 'output'
|
|
4
4
|
require_relative 'connections/connection_manager'
|
|
5
|
+
require_relative 'connections/dashboard'
|
|
6
|
+
require_relative 'tenant_configurator/context_wrapper'
|
|
5
7
|
|
|
6
8
|
module ConsoleKit
|
|
7
9
|
# For tenant configuration
|
|
8
10
|
module TenantConfigurator
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
CONTEXT_MAPPING = {
|
|
12
|
+
partner_identifier: :partner_code,
|
|
13
|
+
tenant_shard: :shard,
|
|
14
|
+
tenant_mongo_db: :mongo_db,
|
|
15
|
+
tenant_redis_db: :redis_db,
|
|
16
|
+
tenant_elasticsearch_prefix: :elasticsearch_prefix
|
|
17
|
+
}.freeze
|
|
16
18
|
|
|
19
|
+
class << self
|
|
17
20
|
def configuration_success = Thread.current[:console_kit_configuration_success]
|
|
18
21
|
|
|
19
22
|
def configuration_success=(val)
|
|
20
23
|
Thread.current[:console_kit_configuration_success] = val
|
|
21
24
|
end
|
|
22
25
|
|
|
26
|
+
def current_tenant_key = Thread.current[:console_kit_current_tenant_key]
|
|
27
|
+
|
|
28
|
+
def current_tenant_key=(val)
|
|
29
|
+
Thread.current[:console_kit_current_tenant_key] = val
|
|
30
|
+
end
|
|
31
|
+
|
|
23
32
|
def configure_tenant(key)
|
|
24
|
-
|
|
25
|
-
return missing_config_error(key) unless constants
|
|
33
|
+
return true if key == current_tenant_key && configuration_success
|
|
26
34
|
|
|
27
|
-
|
|
35
|
+
attempt_configuration(key)
|
|
28
36
|
rescue StandardError => e
|
|
29
|
-
handle_error(e, key)
|
|
37
|
+
handle_error?(e, key)
|
|
30
38
|
end
|
|
31
39
|
|
|
32
40
|
def clear
|
|
33
41
|
ctx = ConsoleKit.configuration.context_class
|
|
34
42
|
return unless ctx
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
Output.print_info('Tenant context has been cleared.')
|
|
44
|
+
perform_clear(ContextWrapper.for_context(ctx))
|
|
38
45
|
end
|
|
39
46
|
|
|
40
47
|
private
|
|
41
48
|
|
|
42
|
-
def
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
def attempt_configuration(key)
|
|
50
|
+
constants = ConsoleKit.configuration.tenants[key]&.[](:constants)
|
|
51
|
+
return missing_config_error?(key) unless constants
|
|
52
|
+
|
|
53
|
+
execute_configuration(key, constants)
|
|
54
|
+
configuration_success
|
|
46
55
|
end
|
|
47
56
|
|
|
48
|
-
def
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
57
|
+
def perform_clear(wrapper)
|
|
58
|
+
return unless configuration_success || wrapper.any_set?
|
|
59
|
+
|
|
60
|
+
reset_tenant(wrapper)
|
|
61
|
+
Output.print_info('Tenant context has been cleared.')
|
|
62
|
+
true
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def reset_tenant(wrapper)
|
|
66
|
+
self.configuration_success = false
|
|
67
|
+
self.current_tenant_key = nil
|
|
68
|
+
wrapper.reset
|
|
69
|
+
setup_connections(wrapper.ctx)
|
|
52
70
|
end
|
|
53
71
|
|
|
54
72
|
def validate_constants!(constants)
|
|
@@ -56,64 +74,54 @@ module ConsoleKit
|
|
|
56
74
|
raise Error, "Tenant constants missing keys: #{missing.join(', ')}" unless missing.empty?
|
|
57
75
|
end
|
|
58
76
|
|
|
59
|
-
def missing_config_error(key)
|
|
77
|
+
def missing_config_error?(key)
|
|
60
78
|
self.configuration_success = false
|
|
61
79
|
Output.print_error("No configuration found for tenant: #{key}")
|
|
80
|
+
false
|
|
62
81
|
end
|
|
63
82
|
|
|
64
|
-
def
|
|
83
|
+
def execute_configuration(key, constants)
|
|
65
84
|
validate_constants!(constants)
|
|
66
85
|
apply_context(constants)
|
|
67
|
-
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def handler_available?(handler_class)
|
|
71
|
-
handler_class.new(nil).available?
|
|
72
|
-
rescue NotImplementedError, StandardError
|
|
73
|
-
false
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def available_context_attributes(ctx)
|
|
77
|
-
attributes = %i[partner_identifier]
|
|
78
|
-
HANDLER_ATTRIBUTES.each do |handler, attr|
|
|
79
|
-
attributes << attr if handler_available?(handler) && ctx.respond_to?("#{attr}=")
|
|
80
|
-
end
|
|
81
|
-
attributes.select { |attr| ctx.respond_to?("#{attr}=") }
|
|
86
|
+
mark_success(key)
|
|
82
87
|
end
|
|
83
88
|
|
|
84
89
|
def apply_context(constant)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
90
|
+
wrapper = ContextWrapper.for_context(ConsoleKit.configuration.context_class)
|
|
91
|
+
wrapper.assign(constant, CONTEXT_MAPPING).each do |attr, existing, configured|
|
|
92
|
+
warn_case_mismatch(attr, existing, configured) if case_mismatch?(existing, configured)
|
|
93
|
+
end
|
|
94
|
+
setup_connections(wrapper.ctx)
|
|
88
95
|
end
|
|
89
96
|
|
|
90
|
-
def
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
tenant_mongo_db: :mongo_db,
|
|
95
|
-
tenant_redis_db: :redis_db,
|
|
96
|
-
tenant_elasticsearch_prefix: :elasticsearch_prefix
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
available_context_attributes(ctx).each do |attr|
|
|
100
|
-
ctx.public_send("#{attr}=", constant[attribute_to_constant[attr]])
|
|
101
|
-
end
|
|
97
|
+
def case_mismatch?(existing, new_value)
|
|
98
|
+
existing.is_a?(String) && new_value.is_a?(String) &&
|
|
99
|
+
existing != new_value &&
|
|
100
|
+
existing.casecmp(new_value).zero?
|
|
102
101
|
end
|
|
103
102
|
|
|
104
103
|
def setup_connections(context)
|
|
105
|
-
|
|
104
|
+
Connections::ConnectionManager.available_handlers(context).each(&:connect)
|
|
106
105
|
end
|
|
107
106
|
|
|
108
|
-
def
|
|
107
|
+
def mark_success(key)
|
|
109
108
|
Output.print_success("Tenant set to: #{key}")
|
|
110
109
|
self.configuration_success = true
|
|
110
|
+
self.current_tenant_key = key
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
-
def
|
|
113
|
+
def warn_case_mismatch(attr, existing, configured)
|
|
114
|
+
Output.print_warning(
|
|
115
|
+
"#{attr} case mismatch: context had '#{existing}', config set '#{configured}'. " \
|
|
116
|
+
'Check your ConsoleKit tenant configuration.'
|
|
117
|
+
)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def handle_error?(error, key)
|
|
114
121
|
self.configuration_success = false
|
|
115
122
|
Output.print_error("Failed to configure tenant '#{key}': #{error.message}")
|
|
116
123
|
Output.print_backtrace(error)
|
|
124
|
+
false
|
|
117
125
|
end
|
|
118
126
|
end
|
|
119
127
|
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ConsoleKit
|
|
4
|
+
# Orchestrates tenant lifecycle, selection, and configuration
|
|
5
|
+
class TenantOrchestrator
|
|
6
|
+
class << self
|
|
7
|
+
def auto_select? = (tenants.size == 1) || !$stdin.tty?
|
|
8
|
+
|
|
9
|
+
def current_tenant = Setup.current_tenant
|
|
10
|
+
|
|
11
|
+
def current_tenant=(val)
|
|
12
|
+
Setup.current_tenant = val
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def reapply
|
|
16
|
+
return unless tenant_setup_successful?
|
|
17
|
+
|
|
18
|
+
Output.silence do
|
|
19
|
+
TenantConfigurator.current_tenant_key = nil
|
|
20
|
+
TenantConfigurator.configure_tenant(current_tenant)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def reset
|
|
25
|
+
return warn_no_tenants unless tenants?
|
|
26
|
+
|
|
27
|
+
perform_reset
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def run
|
|
31
|
+
return if tenant_setup_successful?
|
|
32
|
+
|
|
33
|
+
perform_setup
|
|
34
|
+
rescue StandardError => e
|
|
35
|
+
handle_error(e)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def tenants = ConsoleKit.configuration.tenants
|
|
39
|
+
def tenants? = tenants&.any?
|
|
40
|
+
def select_tenant_key = auto_select? ? tenants.keys.first : TenantSelector.select
|
|
41
|
+
def warn_no_tenants = Output.print_warning('Cannot reset tenant: No tenants configured.')
|
|
42
|
+
def cancel_switch = Output.print_warning('Tenant switch cancelled.')
|
|
43
|
+
def skip_tenant_message = Output.print_info('No tenant selected. Loading without tenant configuration.')
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def tenant_setup_successful? = !current_tenant.to_s.empty?
|
|
48
|
+
|
|
49
|
+
def perform_reset
|
|
50
|
+
key = select_tenant_key
|
|
51
|
+
return cancel_switch if key == :abort || key.blank?
|
|
52
|
+
return already_on_tenant?(key) if key == current_tenant
|
|
53
|
+
|
|
54
|
+
clear_current_tenant
|
|
55
|
+
return skip_tenant_message if %i[exit none].include?(key)
|
|
56
|
+
|
|
57
|
+
configure(key)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def perform_setup
|
|
61
|
+
ConsoleKit.configuration.validate!
|
|
62
|
+
key = select_tenant_key
|
|
63
|
+
return handle_selection_result(key) if %i[exit abort none].include?(key) || key.blank?
|
|
64
|
+
|
|
65
|
+
configure(key)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def handle_selection_result(key)
|
|
69
|
+
exit_on_key if %i[exit abort].include?(key)
|
|
70
|
+
|
|
71
|
+
skip_tenant_message if key == :none
|
|
72
|
+
Output.print_error('Tenant selection failed. Loading without tenant configuration.') if key.blank?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def exit_on_key
|
|
76
|
+
Output.print_info('Exiting console...')
|
|
77
|
+
Kernel.exit
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def configure(key)
|
|
81
|
+
TenantConfigurator.configure_tenant(key)
|
|
82
|
+
return unless TenantConfigurator.configuration_success
|
|
83
|
+
|
|
84
|
+
Setup.current_tenant = key
|
|
85
|
+
Prompt.apply
|
|
86
|
+
SetupUI.print_tenant_banner(key, ConsoleKit.configuration)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def already_on_tenant?(key)
|
|
90
|
+
Output.print_info("Already using tenant: #{key}. No changes made.")
|
|
91
|
+
true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def clear_current_tenant
|
|
95
|
+
if current_tenant
|
|
96
|
+
Output.print_warning("Resetting tenant: #{current_tenant}")
|
|
97
|
+
TenantConfigurator.clear
|
|
98
|
+
end
|
|
99
|
+
self.current_tenant = nil
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def handle_error(error)
|
|
103
|
+
Output.print_error("Error setting up tenant: #{error.message}")
|
|
104
|
+
Output.print_backtrace(error)
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|