console_kit 1.4.0 → 1.5.1

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/lib/console_kit/configuration.rb +15 -21
  3. data/lib/console_kit/configuration_validator.rb +179 -0
  4. data/lib/console_kit/connections/base_connection_handler.rb +139 -25
  5. data/lib/console_kit/connections/connection_manager.rb +42 -8
  6. data/lib/console_kit/connections/dashboard.rb +3 -11
  7. data/lib/console_kit/connections/diagnostic_helpers.rb +24 -5
  8. data/lib/console_kit/connections/elasticsearch_connection_handler.rb +87 -32
  9. data/lib/console_kit/connections/elasticsearch_prefix_registry.rb +70 -0
  10. data/lib/console_kit/connections/mongo_connection_handler.rb +51 -46
  11. data/lib/console_kit/connections/mongoid_support.rb +56 -0
  12. data/lib/console_kit/connections/redis_client_adapter.rb +92 -0
  13. data/lib/console_kit/connections/redis_connection_handler.rb +97 -41
  14. data/lib/console_kit/connections/sql_connection_handler.rb +66 -30
  15. data/lib/console_kit/connections/sql_strategy.rb +248 -0
  16. data/lib/console_kit/connections/table_renderer.rb +30 -20
  17. data/lib/console_kit/console_helpers.rb +17 -31
  18. data/lib/console_kit/diagnostics.rb +143 -0
  19. data/lib/console_kit/errors.rb +93 -0
  20. data/lib/console_kit/instrumentation.rb +57 -0
  21. data/lib/console_kit/output.rb +5 -14
  22. data/lib/console_kit/prompt.rb +17 -29
  23. data/lib/console_kit/railtie.rb +2 -3
  24. data/lib/console_kit/setup.rb +66 -13
  25. data/lib/console_kit/setup_ui.rb +1 -4
  26. data/lib/console_kit/tenant_configurator/context_wrapper.rb +51 -23
  27. data/lib/console_kit/tenant_configurator.rb +40 -78
  28. data/lib/console_kit/tenant_orchestrator.rb +31 -27
  29. data/lib/console_kit/tenant_plan.rb +68 -0
  30. data/lib/console_kit/tenant_rollback.rb +77 -0
  31. data/lib/console_kit/tenant_selector.rb +3 -6
  32. data/lib/console_kit/tenant_state.rb +73 -0
  33. data/lib/console_kit/tenant_switch.rb +124 -0
  34. data/lib/console_kit/version.rb +1 -1
  35. data/lib/console_kit.rb +51 -24
  36. data/lib/generators/console_kit/install_generator.rb +1 -12
  37. data/lib/generators/console_kit/templates/console_kit.rb +4 -0
  38. metadata +15 -4
  39. data/lib/console_kit/connections/table_formatter.rb +0 -37
@@ -1,28 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'tenant_state'
4
+ require_relative 'tenant_selector'
5
+ require_relative 'tenant_configurator'
6
+ require_relative 'output'
7
+ require_relative 'setup_ui'
8
+
3
9
  module ConsoleKit
4
- # Orchestrates tenant lifecycle, selection, and configuration
5
10
  class TenantOrchestrator
11
+ NO_TENANT_SELECTED = 'No tenant selected. Loading without tenant configuration.'
12
+
6
13
  class << self
7
14
  def auto_select? = (tenants.size == 1) || !$stdin.tty?
8
15
 
9
- def current_tenant = Setup.current_tenant
16
+ def current_tenant = StateStore.tenant_key
10
17
 
11
18
  def current_tenant=(val)
12
- Setup.current_tenant = val
19
+ return if StateStore.tenant_key == val
20
+
21
+ val.nil? ? StateStore.clear! : StateStore.current = TenantState.new(tenant_key: val)
13
22
  end
14
23
 
15
24
  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
25
+ key = current_tenant
26
+ Output.silence { TenantSwitch.call(key) } if tenant_setup_successful?
27
+ rescue StandardError => e
28
+ Output.print_error("Failed to reapply tenant '#{key}': #{scrub(e.message)}")
22
29
  end
23
30
 
24
31
  def reset
25
- return warn_no_tenants unless tenants?
32
+ return Output.print_warning('Cannot reset tenant: No tenants configured.') unless tenants?
26
33
 
27
34
  perform_reset
28
35
  end
@@ -38,9 +45,6 @@ module ConsoleKit
38
45
  def tenants = ConsoleKit.configuration.tenants
39
46
  def tenants? = tenants&.any?
40
47
  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
48
 
45
49
  private
46
50
 
@@ -48,13 +52,17 @@ module ConsoleKit
48
52
 
49
53
  def perform_reset
50
54
  key = select_tenant_key
51
- return cancel_switch if key == :abort || key.blank?
52
- return already_on_tenant?(key) if key == current_tenant
55
+ return Output.print_warning('Tenant switch cancelled.') if key == :abort || key.blank?
56
+ return Output.print_info("Already using tenant: #{key}. No changes made.") if key == current_tenant
53
57
 
54
- clear_current_tenant
55
- return skip_tenant_message if %i[exit none].include?(key)
58
+ apply_reset(key)
59
+ end
56
60
 
57
- configure(key)
61
+ def apply_reset(key)
62
+ return configure(key) unless %i[exit none].include?(key)
63
+
64
+ clear_current_tenant
65
+ Output.print_info(NO_TENANT_SELECTED)
58
66
  end
59
67
 
60
68
  def perform_setup
@@ -68,7 +76,7 @@ module ConsoleKit
68
76
  def handle_selection_result(key)
69
77
  exit_on_key if %i[exit abort].include?(key)
70
78
 
71
- skip_tenant_message if key == :none
79
+ Output.print_info(NO_TENANT_SELECTED) if key == :none
72
80
  Output.print_error('Tenant selection failed. Loading without tenant configuration.') if key.blank?
73
81
  end
74
82
 
@@ -81,16 +89,10 @@ module ConsoleKit
81
89
  TenantConfigurator.configure_tenant(key)
82
90
  return unless TenantConfigurator.configuration_success
83
91
 
84
- Setup.current_tenant = key
85
- Prompt.apply
92
+ self.current_tenant = key
86
93
  SetupUI.print_tenant_banner(key, ConsoleKit.configuration)
87
94
  end
88
95
 
89
- def already_on_tenant?(key)
90
- Output.print_info("Already using tenant: #{key}. No changes made.")
91
- true
92
- end
93
-
94
96
  def clear_current_tenant
95
97
  if current_tenant
96
98
  Output.print_warning("Resetting tenant: #{current_tenant}")
@@ -100,9 +102,11 @@ module ConsoleKit
100
102
  end
101
103
 
102
104
  def handle_error(error)
103
- Output.print_error("Error setting up tenant: #{error.message}")
105
+ Output.print_error("Error setting up tenant: #{scrub(error.message)}")
104
106
  Output.print_backtrace(error)
105
107
  end
108
+
109
+ def scrub(message) = Connections::DiagnosticHelpers.scrub(message)
106
110
  end
107
111
  end
108
112
  end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'errors'
4
+
5
+ module ConsoleKit
6
+ class TenantPlan
7
+ REQUIRED_KEYS = %i[shard partner_code].freeze
8
+
9
+ attr_reader :tenant_key
10
+
11
+ def initialize(tenant_key, constants: nil)
12
+ @tenant_key = tenant_key
13
+ @constants = constants
14
+ end
15
+
16
+ def constants = @constants ||= resolve_constants
17
+
18
+ def targets_for(handlers)
19
+ handlers.to_h { |handler| [handler.backend_key, target_for(handler)] }
20
+ end
21
+
22
+ private
23
+
24
+ def target_for(handler) = constants[handler.class.constants_key]
25
+
26
+ def resolve_constants
27
+ return {} if tenant_key.nil?
28
+
29
+ found = lookup_constants
30
+ raise TenantNotFoundError, not_found_message unless found
31
+
32
+ validate!(found)
33
+ found
34
+ end
35
+
36
+ def not_found_message
37
+ tenants = ConsoleKit.configuration.tenants
38
+ keys = tenants.is_a?(Hash) ? tenants.keys : []
39
+ known = keys.any? ? "Configured tenants: #{keys.map(&:inspect).join(', ')}." : 'No tenants are configured.'
40
+ "No configuration found for tenant: #{tenant_key.inspect}. #{known}"
41
+ end
42
+
43
+ def lookup_constants
44
+ tenants = ConsoleKit.configuration.tenants
45
+ entry = tenants.is_a?(Hash) ? tenants[tenant_key] : nil
46
+ constants_from(entry) unless entry.nil?
47
+ end
48
+
49
+ def constants_from(entry)
50
+ raise ConfigurationError, "Tenant #{tenant_key.inspect} configuration must be a Hash, got #{entry.class}." \
51
+ unless entry.is_a?(Hash)
52
+
53
+ entry[:constants]
54
+ end
55
+
56
+ def validate!(constants)
57
+ tenant = tenant_key.inspect
58
+ unless constants.is_a?(Hash)
59
+ raise ConfigurationError, "Tenant #{tenant} `:constants` must be a Hash, got #{constants.class}."
60
+ end
61
+
62
+ missing = REQUIRED_KEYS - constants.keys
63
+ return if missing.empty?
64
+
65
+ raise ConfigurationError, "Tenant #{tenant} constants missing keys: #{missing.join(', ')}"
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'instrumentation'
4
+
5
+ module ConsoleKit
6
+ class TenantRollback
7
+ EVENT = 'console_kit.rollback'
8
+ MISSING_HANDLER = 'no connection handler is available to restore it, so it may still be serving the tenant ' \
9
+ 'that was being unwound'
10
+
11
+ class << self
12
+ def unwind(state, previous)
13
+ failures = restore(state, state.context_object || ConsoleKit.configuration.context_class)
14
+ StateStore.current = previous
15
+ raise RollbackError, failures unless failures.empty?
16
+
17
+ previous
18
+ end
19
+
20
+ private
21
+
22
+ def restore(state, ctx)
23
+ wrapper = TenantConfigurator::ContextWrapper.new(ctx, state.undo_context.keys)
24
+ live, missing = resolve_handlers(state, ctx)
25
+ new(state.undo, wrapper).call(live, missing)
26
+ end
27
+
28
+ def resolve_handlers(state, ctx)
29
+ live = Connections::ConnectionManager.available_handlers(ctx)
30
+ .to_h { |handler| [handler.backend_key, handler] }
31
+ present, missing = state.undo_backends.keys.partition { |key| live.key?(key) }
32
+ [live.values_at(*present), missing]
33
+ end
34
+ end
35
+
36
+ def initialize(undo, context_wrapper)
37
+ @undo = undo
38
+ @context_wrapper = context_wrapper
39
+ end
40
+
41
+ def call(handlers, unrestorable = [])
42
+ return [] if undo.nil?
43
+
44
+ Instrumentation.increment(EVENT)
45
+ abandoned(unrestorable) + restore_backends(handlers) + restore_context
46
+ end
47
+
48
+ private
49
+
50
+ attr_reader :undo, :context_wrapper
51
+
52
+ def abandoned(keys)
53
+ keys.map { |key| failure(key, UnsupportedBackendError.new("ConsoleKit: #{key} - #{MISSING_HANDLER}.")) }
54
+ end
55
+
56
+ def restore_backends(handlers)
57
+ handlers.reverse.filter_map do |handler|
58
+ handler.restore(undo[:backends][handler.backend_key])
59
+ nil
60
+ rescue StandardError, NotImplementedError => e
61
+ failure(handler.display_name, e)
62
+ end
63
+ end
64
+
65
+ def restore_context
66
+ context_wrapper.restore(undo[:context])
67
+ []
68
+ rescue StandardError, NotImplementedError => e
69
+ [failure('context', e)]
70
+ end
71
+
72
+ def failure(backend, error)
73
+ Instrumentation.increment('console_kit.rollback_failure')
74
+ { backend: backend, error: error }
75
+ end
76
+ end
77
+ end
@@ -3,7 +3,6 @@
3
3
  require_relative 'output'
4
4
 
5
5
  module ConsoleKit
6
- # For tenant selection
7
6
  module TenantSelector
8
7
  RETRY_LIMIT = 3
9
8
  DEFAULT_SELECTION = '1'
@@ -37,12 +36,10 @@ module ConsoleKit
37
36
  end
38
37
 
39
38
  def menu_items
40
- tenants = ConsoleKit.tenants.keys
41
- items = ['0. Skip (load without tenant configuration)']
42
- tenants.each_with_index.map do |key, index|
43
- items << "#{index + 1}. #{key} (partner: #{tenant_partner(key)})"
39
+ numbered = ConsoleKit.tenants.keys.map.with_index(1) do |key, index|
40
+ "#{index}. #{key} (partner: #{tenant_partner(key)})"
44
41
  end
45
- items
42
+ ['0. Skip (load without tenant configuration)'] + numbered
46
43
  end
47
44
 
48
45
  def tenant_partner(key) = ConsoleKit.tenants.dig(key, :constants, :partner_code) || 'N/A'
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ConsoleKit
4
+ class TenantState
5
+ NO_DROPPED = [].freeze
6
+ EMPTY_UNDO = { context: {}.freeze, backends: {}.freeze, dropped: NO_DROPPED, context_object: nil }.freeze
7
+
8
+ attr_reader :tenant_key, :constants, :undo
9
+
10
+ module OwnCopy
11
+ class << self
12
+ def call(value)
13
+ case value
14
+ when Hash then value.transform_values { |entry| call(entry) }.freeze
15
+ when Array then value.map { |entry| call(entry) }.freeze
16
+ else value
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ class << self
23
+ def empty = new
24
+
25
+ def undo_bundle(context:, backends:, dropped: NO_DROPPED, context_object: nil)
26
+ { context: OwnCopy.call(context), backends: OwnCopy.call(backends),
27
+ dropped: OwnCopy.call(dropped), context_object: context_object }.freeze
28
+ end
29
+ end
30
+
31
+ def initialize(tenant_key: nil, constants: {}, undo: EMPTY_UNDO, configured: false)
32
+ @tenant_key = tenant_key
33
+ @configured = configured
34
+ @constants = OwnCopy.call(constants)
35
+ @undo = undo
36
+ freeze
37
+ end
38
+
39
+ def configured? = @configured
40
+ def undo_context = @undo[:context]
41
+ def undo_backends = @undo[:backends]
42
+
43
+ def context_object = @undo[:context_object]
44
+
45
+ def dropped_backends = @undo[:dropped] || NO_DROPPED
46
+ def snapshot_for(backend) = @undo[:backends][backend]
47
+
48
+ def backends_missing_from(live) = @undo[:backends].keys - live
49
+
50
+ def inspect = "#<ConsoleKit::TenantState #{@tenant_key.inspect} backends=#{@undo[:backends].keys.inspect}>"
51
+ end
52
+
53
+ module StateStore
54
+ STATE_KEY = :console_kit_state
55
+
56
+ class << self
57
+ def current = stored || TenantState.empty
58
+
59
+ def stored = Thread.current.thread_variable_get(STATE_KEY)
60
+
61
+ def current=(state)
62
+ Thread.current.thread_variable_set(STATE_KEY, state)
63
+ end
64
+
65
+ def tenant_key = current.tenant_key
66
+ def configured? = current.configured?
67
+
68
+ def clear!
69
+ Thread.current.thread_variable_set(STATE_KEY, nil)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'errors'
4
+ require_relative 'tenant_state'
5
+ require_relative 'instrumentation'
6
+ require_relative 'tenant_rollback'
7
+ require_relative 'tenant_plan'
8
+ require_relative 'connections/connection_manager'
9
+
10
+ module ConsoleKit
11
+ class TenantSwitch
12
+ EVENT = 'console_kit.tenant_switch'
13
+
14
+ class << self
15
+ def call(tenant_key) = new(tenant_key).call
16
+
17
+ def clear(context: nil) = new(nil, context: context).call
18
+
19
+ def verify_current!(dropped = nil)
20
+ state = StateStore.current
21
+ raise ConfigurationError, 'No tenant is currently configured.' unless state.configured?
22
+
23
+ new(state.tenant_key).send(:verify_committed, state, dropped)
24
+ state
25
+ end
26
+
27
+ def unwind(state, previous) = TenantRollback.unwind(state, previous)
28
+ end
29
+
30
+ def initialize(tenant_key, context: nil)
31
+ @tenant_key = tenant_key
32
+ @context = context || ConsoleKit.configuration.context_class
33
+ @failing_handler = nil
34
+ @dropped_backends = []
35
+ @handlers = []
36
+ end
37
+
38
+ def call
39
+ Instrumentation.instrument(EVENT, tenant: @tenant_key, from: StateStore.tenant_key) { perform }
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :tenant_key, :context
45
+
46
+ def perform
47
+ plan = TenantPlan.new(tenant_key)
48
+ @handlers = Connections::ConnectionManager.available_handlers(context, @dropped_backends)
49
+ targets = plan.targets_for(@handlers)
50
+ prepare_all(targets)
51
+ run_transaction(plan.constants, targets, snapshot_state)
52
+ end
53
+
54
+ def prepare_all(targets)
55
+ @handlers.each { |handler| handler.prepare(targets[handler.backend_key]) }
56
+ end
57
+
58
+ def snapshot_state
59
+ snapshots = @handlers.to_h { |handler| [handler.backend_key, handler.snapshot] }
60
+ TenantState.undo_bundle(context: context_wrapper.current_values, backends: snapshots,
61
+ dropped: @dropped_backends, context_object: context)
62
+ rescue StandardError, NotImplementedError => e
63
+ raise switch_error(e)
64
+ end
65
+
66
+ def run_transaction(constants, targets, undo)
67
+ attempted = []
68
+ apply(constants, targets, attempted)
69
+ commit(constants, undo)
70
+ rescue StandardError, NotImplementedError => e
71
+ raise switch_error(e, TenantRollback.new(undo, context_wrapper).call(attempted))
72
+ end
73
+
74
+ def apply(constants, targets, attempted)
75
+ context_wrapper.assign(constants, TenantConfigurator.context_mapping)
76
+ connect_all(targets, attempted)
77
+ verify_all(attempted, targets)
78
+ end
79
+
80
+ def connect_all(targets, attempted)
81
+ @handlers.each do |handler|
82
+ attempted << handler
83
+ instrument_backend('console_kit.backend_connect', handler) { handler.connect!(targets[handler.backend_key]) }
84
+ end
85
+ end
86
+
87
+ def verify_all(handlers, targets)
88
+ handlers.each { |handler| verify_one(handler, targets[handler.backend_key]) }
89
+ end
90
+
91
+ def verify_one(handler, target)
92
+ instrument_backend('console_kit.backend_verify', handler) { handler.verify!(target) }
93
+ rescue ConnectionVerificationError => e
94
+ Instrumentation.increment('console_kit.verification_failure')
95
+ raise e
96
+ end
97
+
98
+ def instrument_backend(event, handler, &)
99
+ @failing_handler = handler
100
+ Instrumentation.instrument(event, backend: handler.backend_key, tenant: tenant_key, &)
101
+ end
102
+
103
+ def commit(constants, undo)
104
+ StateStore.current = TenantState.new(tenant_key: tenant_key, constants: constants, undo: undo,
105
+ configured: !tenant_key.nil?)
106
+ end
107
+
108
+ def switch_error(error, rollback_failures = [])
109
+ TenantSwitchError.new(
110
+ from_tenant: StateStore.tenant_key, to_tenant: tenant_key, original_error: error,
111
+ backend: error.try(:backend) || @failing_handler&.display_name, rollback_failures: rollback_failures
112
+ )
113
+ end
114
+
115
+ def verify_committed(state, dropped = nil)
116
+ handlers = Connections::ConnectionManager.available_handlers(context, dropped)
117
+ dropped&.concat(state.backends_missing_from(handlers.map(&:backend_key)) - dropped)
118
+ verify_all(handlers, TenantPlan.new(state.tenant_key, constants: state.constants).targets_for(handlers))
119
+ nil
120
+ end
121
+
122
+ def context_wrapper = @context_wrapper ||= TenantConfigurator::ContextWrapper.for_context(context)
123
+ end
124
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ConsoleKit
4
- VERSION = '1.4.0'
4
+ VERSION = '1.5.1'
5
5
  end
data/lib/console_kit.rb CHANGED
@@ -1,20 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'English'
4
+ require 'logger'
5
+ require 'active_support'
3
6
  require 'active_support/core_ext/object/blank'
4
- require 'active_support/core_ext/object/inclusion'
7
+ require 'active_support/core_ext/object/try'
5
8
  require 'active_support/core_ext/string/inflections'
9
+ require 'active_support/core_ext/time/calculations'
6
10
 
7
11
  require_relative 'console_kit/version'
12
+ require_relative 'console_kit/errors'
13
+ require_relative 'console_kit/tenant_state'
14
+ require_relative 'console_kit/instrumentation'
8
15
  require_relative 'console_kit/configuration'
16
+ require_relative 'console_kit/tenant_orchestrator'
9
17
  require_relative 'console_kit/setup'
10
18
  require_relative 'console_kit/console_helpers'
11
19
  require_relative 'console_kit/prompt'
12
20
  require_relative 'console_kit/railtie' if defined?(Rails::Railtie)
13
21
 
14
- # Main module for ConsoleKit
15
22
  module ConsoleKit
16
- # Base error class for ConsoleKit-related exceptions.
17
- class Error < StandardError; end
23
+ INCOMPLETE_VERIFICATION = 'ConsoleKit: tenant %<tenant>p is verified only for the backends ConsoleKit could ' \
24
+ 'drive. Never switched, verified or rolled back, and possibly still serving another ' \
25
+ 'tenant: %<backends>s.'
18
26
 
19
27
  class << self
20
28
  def configure = yield(configuration)
@@ -22,37 +30,56 @@ module ConsoleKit
22
30
 
23
31
  def reset_configuration!
24
32
  @configuration = nil
25
- Setup.current_tenant = nil
26
- TenantConfigurator.configuration_success = false if defined?(TenantConfigurator)
27
- end
28
-
29
- def pretty_output = configuration.pretty_output
30
-
31
- def pretty_output=(val)
32
- configuration.pretty_output = val
33
+ StateStore.clear!
33
34
  end
34
35
 
35
36
  def tenants = configuration.tenants
36
37
 
37
- def tenants=(val)
38
- configuration.tenants = val
39
- end
38
+ def current_tenant = StateStore.tenant_key
39
+ def reset_current_tenant = TenantOrchestrator.reset
40
40
 
41
- def context_class = configuration.context_class
41
+ def switch_tenant(key) = TenantSwitch.call(key)
42
42
 
43
- def context_class=(val)
44
- configuration.context_class = val
43
+ def verify_tenant!
44
+ dropped_now = []
45
+ state = TenantSwitch.verify_current!(dropped_now)
46
+ report_dropped_backends(state, dropped_now)
47
+ state
45
48
  end
46
49
 
47
- def show_dashboard = configuration.show_dashboard
48
-
49
- def show_dashboard=(val)
50
- configuration.show_dashboard = val
50
+ def with_tenant(key, &)
51
+ previous = StateStore.current
52
+ state = TenantSwitch.call(key)
53
+ unwind_after(state, previous, &)
51
54
  end
52
55
 
53
- def current_tenant = Setup.current_tenant
54
- def reset_current_tenant = Setup.reset_current_tenant
55
56
  def enable_pretty_output = configuration.pretty_output = true
56
57
  def disable_pretty_output = configuration.pretty_output = false
58
+
59
+ private
60
+
61
+ def report_dropped_backends(state, dropped_now)
62
+ dropped = state.dropped_backends | dropped_now
63
+ return if dropped.empty?
64
+
65
+ Instrumentation.increment('console_kit.incomplete_verification')
66
+ Output.print_warning(format(INCOMPLETE_VERIFICATION, tenant: state.tenant_key, backends: dropped.join(', ')))
67
+ end
68
+
69
+ def unwind_after(state, previous)
70
+ completed = false
71
+ yield.tap { completed = true }
72
+ ensure
73
+ unwind_scope(state, previous, completed ? nil : $ERROR_INFO)
74
+ end
75
+
76
+ def unwind_scope(state, previous, in_flight)
77
+ TenantSwitch.unwind(state, previous)
78
+ rescue RollbackError => e
79
+ raise e unless in_flight
80
+
81
+ Output.print_error("#{e.message}\nThis rollback failure did not replace the in-flight " \
82
+ "#{in_flight.class}: #{in_flight.message}")
83
+ end
57
84
  end
58
85
  end
@@ -5,22 +5,11 @@ require 'rails/generators/base'
5
5
 
6
6
  module ConsoleKit
7
7
  module Generators
8
- # Generates the required files
9
8
  class InstallGenerator < Rails::Generators::Base
10
9
  source_root File.expand_path('templates', __dir__)
11
10
 
12
- class_option :force, type: :boolean, default: false, desc: 'Overwrite existing files'
13
-
14
11
  def copy_initializer
15
- force = options[:force]
16
- initializer_path = Rails.root.join('config', 'initializers', 'console_kit.rb')
17
-
18
- if File.exist?(initializer_path) && !force
19
- say_status :skipped, "Initializer already exists: #{initializer_path}", :yellow
20
- else
21
- template 'console_kit.rb', 'config/initializers/console_kit.rb', force: force
22
- say_status :created, "Initializer generated at #{initializer_path}", :green
23
- end
12
+ template 'console_kit.rb', 'config/initializers/console_kit.rb'
24
13
  end
25
14
 
26
15
  def remind_about_customization
@@ -6,6 +6,10 @@
6
6
  Rails.application.config.after_initialize do
7
7
  ConsoleKit.configure do |config|
8
8
  # TODO: Set your tenants source, example:
9
+ # `shard` and `partner_code` are required for every tenant; `mongo_db`, `redis_db` and
10
+ # `elasticsearch_prefix` are optional. Extra keys (like `environment` below) are fine -
11
+ # they're just not read by ConsoleKit - but `config.validate!` will warn about them in
12
+ # case they're actually a typo of one of the keys above.
9
13
  # {
10
14
  # tenant_a: {
11
15
  # constants: {