zuora_connect 2.0.59 → 2.0.60e

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 98cdc050053f90626d6df8e259de4ff0089091d968f94da492b30dadd1e60afb
4
- data.tar.gz: f0517e432e3554c4d751419889c01899fcaf4c6b04febd093b29a7eb02089344
3
+ metadata.gz: 9efee2fa39c05fb2cf07a2e9a49e14b76a75e68c8be87f008018287a45551ceb
4
+ data.tar.gz: f59b6c98c859dc205afc28a2eae57d5037b6d9cc1f41a5dd33e36d3e2b55a307
5
5
  SHA512:
6
- metadata.gz: 5f3f2ead635cb47c3d42aa1415b30d1405553553b9bc3a3b387061974309c7525f1755ae4546084d4406174cd0e4c051c3b596e101f5d3134360f55d328147c5
7
- data.tar.gz: 3a579ea74120c205588cc62af5d8eef28266a0ddf85e4a8ead2b07bbcd4b4ee1ec9c8b39079a58b48473d2f107d72389eea017cbb667aef22e6db9f647087354
6
+ metadata.gz: 73b79ce54de6008219f2f3fd2abce7defccc27454f3328cf57b02ff116ea7faef8d22d5789566bad372e440c38f486a6bc2062ed2c1ff497d0ea1ac3863cbbdd
7
+ data.tar.gz: 940145c4211fa0be98c0e099f69df168a3224d17695c8fe3efa70ad4ed6fcc4788377d4ed74f17fd089f3c8adab481a1b224c33a46f71701403b00dbc1d386b6
@@ -1,10 +1,11 @@
1
1
  module ZuoraConnect
2
2
  class StaticController < ApplicationController
3
- before_action :authenticate_connect_app_request, :except => [:metrics, :health, :initialize_app]
4
- before_action :clear_connect_app_session, :only => [:metrics, :health, :initialize_app]
5
- after_action :persist_connect_app_session, :except => [:metrics, :health, :initialize_app]
6
-
7
- skip_before_action :verify_authenticity_token, :only => [:initialize_app]
3
+ before_action :authenticate_connect_app_request, :except => [:metrics, :health, :initialize_app, :provision, :instance_user]
4
+ before_action :clear_connect_app_session, :only => [:metrics, :health, :initialize_app, :provision, :instance_user]
5
+ after_action :persist_connect_app_session, :except => [:metrics, :health, :initialize_app, :provision, :instance_user]
6
+
7
+ skip_before_action :verify_authenticity_token, :only => [:initialize_app, :provision]
8
+ http_basic_authenticate_with name: ENV['PROVISION_USER'], password: ENV['PROVISION_SECRET'], :only => [:provision, :instance_user]
8
9
 
9
10
  def metrics
10
11
  type = params[:type].present? ? params[:type] : "versions"
@@ -13,11 +14,11 @@ module ZuoraConnect
13
14
 
14
15
  def health
15
16
  if params[:error].present?
16
- begin
17
+ begin
17
18
  raise ZuoraConnect::Exceptions::Error.new('This is an error')
18
19
  rescue => ex
19
20
  case params[:error]
20
- when 'Log'
21
+ when 'Log'
21
22
  Rails.logger.error("Error in Health", ex)
22
23
  when 'Exception'
23
24
  raise
@@ -34,11 +35,13 @@ module ZuoraConnect
34
35
  def initialize_app
35
36
  begin
36
37
  authenticate_connect_app_request
37
- @appinstance.new_session(:session => @appinstance.data_lookup(:session => session))
38
- render json: {
39
- message: "Success",
40
- status: 200
41
- }, status: 200
38
+ unless performed?
39
+ @appinstance.new_session(:session => @appinstance.data_lookup(:session => session))
40
+ render json: {
41
+ message: 'Success',
42
+ status: 200
43
+ }, status: 200
44
+ end
42
45
  rescue => ex
43
46
  Rails.logger.error("Failed to Initialize application", ex)
44
47
  if performed?
@@ -52,6 +55,74 @@ module ZuoraConnect
52
55
  end
53
56
  end
54
57
 
58
+ def provision
59
+ create_new_instance
60
+ unless performed?
61
+ render json: {
62
+ status: 200,
63
+ message: 'Success',
64
+ app_instance_id: @appinstance.id
65
+ }, status: 200
66
+ end
67
+ rescue StandardError => e
68
+ message = 'Failed to provision new instance'
69
+ if performed?
70
+ Rails.logger.error("#{message}: #{performed?}", e)
71
+ else
72
+ Rails.logger.error(message, e)
73
+ render json: {
74
+ status: 500,
75
+ message: message
76
+ }, status: 500
77
+ end
78
+ end
79
+
80
+ def instance_user
81
+ ZuoraConnect::AppInstance.read_master_db do
82
+ ZuoraConnect.logger.with_fields = {} if ZuoraConnect.logger.is_a?(Ougai::Logger)
83
+ Rails.logger.with_fields = {} if Rails.logger.is_a?(Ougai::Logger)
84
+
85
+ if defined?(ElasticAPM) && ElasticAPM.running? && ElasticAPM.respond_to?(:set_label)
86
+ ElasticAPM.set_label(:trace_id, request.uuid)
87
+ end
88
+
89
+ unless params[:id].present?
90
+ render json: {
91
+ status: 400,
92
+ message: 'No app instance id provided'
93
+ }, status: :bad_request
94
+ return
95
+ end
96
+
97
+ @appinstance = ZuoraConnect::AppInstance.find(params[:id]).new_session
98
+ end
99
+
100
+ zuora_client = @appinstance.send(ZuoraConnect::AppInstance::LOGIN_TENANT_DESTINATION).client
101
+ client_describe, = zuora_client.rest_call(
102
+ url: zuora_client.rest_endpoint('genesis/user/info').gsub('v1/', ''),
103
+ session_type: zuora_client.class == ZuoraAPI::Oauth ? :bearer : :basic
104
+ )
105
+
106
+ render json: {
107
+ status: 200,
108
+ message: 'Success',
109
+ user_id: client_describe['coreUserId'],
110
+ username: client_describe['username'],
111
+ email: client_describe['workEmail']
112
+ }, status: 200
113
+ rescue ActiveRecord::RecordNotFound
114
+ render json: {
115
+ status: 400,
116
+ message: 'No app instance found'
117
+ }, status: :bad_request
118
+ rescue StandardError => e
119
+ Rails.logger.error('Error occurred getting user details', e)
120
+ render json: {
121
+ status: 500,
122
+ message: 'Failed to get user details'
123
+ }, status: 500
124
+ end
125
+
55
126
  private
56
127
 
57
128
  def clear_connect_app_session
@@ -402,7 +402,7 @@ module ZuoraConnect
402
402
  end
403
403
 
404
404
  def self.write_to_telegraf(*args)
405
- if ZuoraConnect.configuration.enable_metrics
405
+ if ZuoraConnect.configuration.enable_metrics && !defined?(Prometheus)
406
406
  @@telegraf_host = ZuoraConnect::Telegraf.new() if @@telegraf_host == nil
407
407
  unicorn_stats = self.unicorn_listener_stats() if defined?(Unicorn) && Unicorn.respond_to?(:listener_names)
408
408
  @@telegraf_host.write(direction: 'Raindrops', tags: {}, values: unicorn_stats) unless unicorn_stats.blank?
@@ -3,38 +3,93 @@ if defined? Prometheus
3
3
  require "zuora_connect/version"
4
4
  require "zuora_api/version"
5
5
 
6
+ resque_path = "#{ENV['RESQUE_EXPORTER_PATH'] || Rails.root.join('tmp/resque_exporter')}/*.prom"
7
+ prometheus_path = Rails.root.join("tmp/prometheus")
8
+
9
+ Dir[resque_path, "#{prometheus_path}/*.bin"].each do |file_path|
10
+ File.unlink(file_path)
11
+ end
12
+
13
+ require 'prometheus/client/data_stores/direct_file_store'
14
+ Prometheus::Client.config.data_store = Prometheus::Client::DataStores::DirectFileStore.new(
15
+ dir: prometheus_path
16
+ )
17
+
18
+ class ResqueExporter
19
+ require 'prometheus/client/formats/text'
20
+ require 'fileutils'
21
+
22
+ def initialize
23
+ @lock = Monitor.new
24
+ @registry = Prometheus::Client.registry
25
+ @path = ENV['RESQUE_EXPORTER_PATH'] || Rails.root.join('tmp/resque_exporter')
26
+ FileUtils.mkdir_p(@path)
27
+ end
28
+
29
+ def export
30
+ filename = File.join(@path, 'resque_export.prom')
31
+ @lock.synchronize do
32
+ File.open(filename, 'w+') do |file|
33
+ file.write(Prometheus::Client::Formats::Text.marshal(@registry))
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ most_recent_aggregation = {}
40
+ sum_aggregation = {}
41
+ if defined?(Unicorn)
42
+ most_recent_aggregation[:aggregation] = :most_recent
43
+ sum_aggregation[:aggregation] = :sum
44
+ end
45
+
6
46
  # Create a default Prometheus registry for our metrics.
7
47
  prometheus = Prometheus::Client.registry
8
48
 
9
49
  # Create your metrics.
10
- ZUORA_VERSION = Prometheus::Client::Gauge.new(:zuora_version, 'The current Zuora Gem version.')
11
- CONNECT_VERSION = Prometheus::Client::Gauge.new(:gem_version, 'The current Connect Gem version.')
12
- RAILS_VERSION = Prometheus::Client::Gauge.new(:rails_version, 'The current Rails version.')
13
- RUBY_V = Prometheus::Client::Gauge.new(:ruby_version, 'The current Ruby version.')
50
+ ZUORA_VERSION = prometheus.gauge(:zuora_version, docstring: 'The current Zuora Gem version.', labels: %i(version name), preset_labels: { version: ZuoraAPI::VERSION, name: ZuoraConnect::Telegraf.app_name }, store_settings: most_recent_aggregation)
51
+ CONNECT_VERSION = prometheus.gauge(:gem_version, docstring: 'The current Connect Gem version.', labels: %i(version name), preset_labels: { version: ZuoraConnect::VERSION, name: ZuoraConnect::Telegraf.app_name }, store_settings: most_recent_aggregation)
52
+ RAILS_VERSION = prometheus.gauge(:rails_version, docstring: 'The current Rails version.', labels: %i(version name), preset_labels: { version: Rails.version, name: ZuoraConnect::Telegraf.app_name }, store_settings: most_recent_aggregation)
53
+ RUBY_V = prometheus.gauge(:ruby_version, docstring: 'The current Ruby version.', labels: %i(version name), preset_labels: { version: RUBY_VERSION, name: ZuoraConnect::Telegraf.app_name }, store_settings: most_recent_aggregation)
14
54
 
15
- # Register your metrics with the registry we previously created.
16
- prometheus.register(ZUORA_VERSION);ZUORA_VERSION.set({version: ZuoraAPI::VERSION, name: ZuoraConnect::Telegraf.app_name},0)
17
- prometheus.register(CONNECT_VERSION);CONNECT_VERSION.set({version: ZuoraConnect::VERSION, name: ZuoraConnect::Telegraf.app_name},0)
18
- prometheus.register(RAILS_VERSION);RAILS_VERSION.set({version: Rails.version, name: ZuoraConnect::Telegraf.app_name},0)
19
- prometheus.register(RUBY_V);RUBY_V.set({version: RUBY_VERSION, name: ZuoraConnect::Telegraf.app_name},0)
55
+ ZUORA_VERSION.set(0)
56
+ CONNECT_VERSION.set(0)
57
+ RAILS_VERSION.set(0)
58
+ RUBY_V.set(0)
20
59
 
21
60
  # Do they have resque jobs?
22
61
  if defined? Resque.redis
23
- REDIS_CONNECTION = Prometheus::Client::Gauge.new(:redis_connection, 'The status of the redis connection, 0 or 1')
24
- FINISHED_JOBS = Prometheus::Client::Gauge.new(:finished_jobs, 'Done resque jobs')
25
- WORKERS = Prometheus::Client::Gauge.new(:workers, 'Total resque workers')
26
- ACTIVE_WORKERS = Prometheus::Client::Gauge.new(:active_workers, 'Active resque workers')
27
- FAILED_JOBS = Prometheus::Client::Gauge.new(:failed_jobs, 'Failed resque jobs')
28
- PENDING_JOBS = Prometheus::Client::Gauge.new(:pending_jobs, 'Pending resque jobs')
29
-
30
- prometheus.register(REDIS_CONNECTION)
31
- prometheus.register(FINISHED_JOBS)
32
- prometheus.register(ACTIVE_WORKERS)
33
- prometheus.register(WORKERS)
34
- prometheus.register(FAILED_JOBS)
35
- prometheus.register(PENDING_JOBS)
36
-
62
+ REDIS_CONNECTION = prometheus.gauge(:redis_connection, docstring: 'The status of the redis connection, 0 or 1', labels: %i(connection name), preset_labels: {connection:'redis', name: ZuoraConnect::Telegraf.app_name}, store_settings: most_recent_aggregation)
63
+ JOBS_FINISHED = prometheus.gauge(:jobs_finished, docstring: 'Done resque jobs', labels: %i(type name), preset_labels: {type:'resque', name: ZuoraConnect::Telegraf.app_name}, store_settings: most_recent_aggregation)
64
+ WORKERS_TOTAL = prometheus.gauge(:workers_total, docstring: 'Total resque workers', labels: %i(type name), preset_labels: {type:'resque', name: ZuoraConnect::Telegraf.app_name}, store_settings: most_recent_aggregation)
65
+ WORKERS_ACTIVE = prometheus.gauge(:workers_active, docstring: 'Active resque workers', labels: %i(type name), preset_labels: {type:'resque', name: ZuoraConnect::Telegraf.app_name}, store_settings: most_recent_aggregation)
66
+ JOBS_FAILED = prometheus.gauge(:jobs_failed, docstring: 'Failed resque jobs', labels: %i(type name), preset_labels: {type:'resque', name: ZuoraConnect::Telegraf.app_name}, store_settings: most_recent_aggregation)
67
+ JOBS_PENDING = prometheus.gauge(:jobs_pending, docstring: 'Pending resque jobs', labels: %i(type name), preset_labels: {type:'resque', name: ZuoraConnect::Telegraf.app_name}, store_settings: most_recent_aggregation)
37
68
  end
38
69
 
70
+ if defined?(Unicorn) && Unicorn.respond_to?(:listener_names)
71
+ UNICORN_KILLS = prometheus.gauge(
72
+ :unicorn_kills,
73
+ docstring: 'Unicorn Kills',
74
+ labels: %i(type name),
75
+ preset_labels: {type:'Unicorn-Killer', name: ZuoraConnect::Telegraf.app_name},
76
+ store_settings: sum_aggregation
77
+ )
78
+
79
+ ZuoraConnect::AppInstanceBase.unicorn_listener_stats.each do |key, _|
80
+ gauge_name = "unicorn_#{key}".gsub(/[^a-zA-Z0-9_]/, '_')
81
+ gauge = prometheus.gauge(
82
+ gauge_name.to_sym,
83
+ docstring: 'Unicorn Stats',
84
+ labels: %i(type name),
85
+ preset_labels: { type: 'unicorn', name: ZuoraConnect::Telegraf.app_name },
86
+ store_settings: most_recent_aggregation
87
+ )
88
+ Prometheus.const_set(
89
+ gauge_name.upcase,
90
+ gauge
91
+ )
92
+ end
93
+ end
39
94
  end
40
95
  end
@@ -5,6 +5,20 @@ if defined?(Resque::Worker)
5
5
  Resque::Job.send(:include, Resque::SelfLookup)
6
6
  end
7
7
 
8
+ if defined?(Resque::Job) && defined?(Prometheus)
9
+ module ResquePrometheusExtensions
10
+ EXPORTER = Prometheus::ResqueExporter.new
11
+ def perform
12
+ super
13
+ EXPORTER.export
14
+ end
15
+ end
16
+
17
+ class Resque::Job
18
+ prepend ResquePrometheusExtensions
19
+ end
20
+ end
21
+
8
22
  Resque.module_eval do
9
23
  # Returns a hash, mapping queue names to queue sizes
10
24
  def queue_sizes
@@ -3,7 +3,35 @@ if defined?(Unicorn::WorkerKiller)
3
3
  self.singleton_class.send(:alias_method, :kill_self_old, :kill_self)
4
4
  def self.kill_self(logger, start_time)
5
5
  self.kill_self_old(logger, start_time)
6
- ZuoraConnect::AppInstance.write_to_telegraf(direction: 'Unicorn-Killer', tags: {app_instance: 0}, values: {kill: 1})
6
+ if defined?(Prometheus)
7
+ Prometheus::UNICORN_KILLS.set(1)
8
+ else
9
+ ZuoraConnect::AppInstance.write_to_telegraf(direction: 'Unicorn-Killer', tags: {app_instance: 0}, values: {kill: 1})
10
+ end
7
11
  end
8
12
  end
13
+ end
14
+
15
+ if defined?(Unicorn::HttpServer) && defined?(Prometheus)
16
+ module HttpServerExtensions
17
+ def kill_worker(signal, wpid)
18
+ Prometheus::UNICORN_KILLS.increment
19
+ super
20
+ end
21
+ end
22
+
23
+ module WorkerExtensions
24
+ def soft_kill(sig)
25
+ Prometheus::UNICORN_KILLS.increment
26
+ super
27
+ end
28
+ end
29
+
30
+ class Unicorn::HttpServer
31
+ prepend HttpServerExtensions
32
+ end
33
+
34
+ class Unicorn::Worker
35
+ prepend WorkerExtensions
36
+ end
9
37
  end
@@ -3,6 +3,11 @@ ZuoraConnect::Engine.routes.draw do
3
3
  get '/internal/data' => 'static#metrics'
4
4
  post '/initialize_app' => 'static#initialize_app'
5
5
 
6
+ if ENV['PROVISION_USER'].present? && ENV['PROVISION_SECRET'].present?
7
+ post '/provision' => 'static#provision'
8
+ get '/instance/:id/user' => 'static#instance_user'
9
+ end
10
+
6
11
  namespace :api do
7
12
  namespace :v1 do
8
13
  resources :app_instance, :only => [:index], defaults: {format: :json} do
@@ -7,7 +7,7 @@ module ZuoraConnect
7
7
  def call(env)
8
8
  begin
9
9
  @app.call(env)
10
- rescue ActionDispatch::ParamsParser::ParseError => error
10
+ rescue DynamicRailsError => error
11
11
  if env['HTTP_ACCEPT'] =~ /application\/json/ || env['CONTENT_TYPE'] =~ /application\/json/
12
12
  return [
13
13
  400, { "Content-Type" => "application/json" },
@@ -18,5 +18,16 @@ module ZuoraConnect
18
18
  end
19
19
  end
20
20
  end
21
+
22
+ # Note(hartley): remove once the minimum supported version of Rails is 5.2
23
+ class DynamicRailsError < StandardError
24
+ def self.===(exception)
25
+ if Rails.version >= "5.2"
26
+ exception.is_a?(ActionDispatch::Http::Parameters::ParseError)
27
+ else
28
+ exception.is_a?(ActionDispatch::ParamsParser::ParseError)
29
+ end
30
+ end
31
+ end
21
32
  end
22
- end
33
+ end
@@ -64,6 +64,22 @@ module ZuoraConnect
64
64
  #Remove bad headers
65
65
  @bad_headers.each { |header| env.delete(header) }
66
66
 
67
+ if defined?(Prometheus) && env['PATH_INFO'] == '/connect/internal/metrics'
68
+ # Prometheus Stuff
69
+ metrics = ZuoraConnect::AppInstance.get_metrics('stats')
70
+ redis_up = metrics.present? && metrics.dig(:Resque, :Workers_Total).present? ? 1 : 0
71
+ Prometheus::REDIS_CONNECTION.set(redis_up)
72
+
73
+ process_prometheus_metric(metrics: metrics)
74
+
75
+ if defined?(Unicorn) && Unicorn.respond_to?(:listener_names)
76
+ ZuoraConnect::AppInstanceBase.unicorn_listener_stats.each do |key, value|
77
+ gauge = Prometheus.const_get("unicorn_#{key}".gsub(/[^a-zA-Z0-9_]/, '_').upcase)
78
+ gauge.set(value) if gauge.present?
79
+ end
80
+ end
81
+ end
82
+
67
83
  #Thread.current[:appinstance] = nil
68
84
  start_time = Time.now
69
85
  begin
@@ -77,35 +93,6 @@ module ZuoraConnect
77
93
  ZuoraConnect::AppInstanceBase.write_to_telegraf(direction: 'request-inbound-assets', tags: tags, values: values)
78
94
  end
79
95
 
80
- if defined? Prometheus
81
- #Prometheus Stuff
82
- if env['PATH_INFO'] == '/connect/internal/metrics'
83
-
84
- #Do something before each scrape
85
- if defined? Resque.redis
86
- begin
87
-
88
- Resque.redis.ping
89
-
90
- Prometheus::REDIS_CONNECTION.set({connection:'redis',name: ZuoraConnect::Telegraf.app_name},1)
91
- Prometheus::FINISHED_JOBS.set({type:'resque',name: ZuoraConnect::Telegraf.app_name},Resque.info[:processed])
92
- Prometheus::PENDING_JOBS.set({type:'resque',name: ZuoraConnect::Telegraf.app_name},Resque.info[:pending])
93
- Prometheus::ACTIVE_WORKERS.set({type:'resque',name: ZuoraConnect::Telegraf.app_name},Resque.info[:working])
94
- Prometheus::WORKERS.set({type:'resque',name: ZuoraConnect::Telegraf.app_name},Resque.info[:workers])
95
- Prometheus::FAILED_JOBS.set({type:'resque',name: ZuoraConnect::Telegraf.app_name},Resque.info[:failed])
96
-
97
- rescue Redis::CannotConnectError
98
- Prometheus::REDIS_CONNECTION.set({connection:'redis',name: ZuoraConnect::Telegraf.app_name},0)
99
- end
100
-
101
- if ZuoraConnect.configuration.custom_prometheus_update_block != nil
102
- ZuoraConnect.configuration.custom_prometheus_update_block.call()
103
- end
104
- end
105
-
106
- end
107
- end
108
-
109
96
  # Uncomment following block of code for handling engine requests/requests without controller
110
97
  # else
111
98
  # # Handling requests which do not have controllers (engines)
@@ -119,10 +106,10 @@ module ZuoraConnect
119
106
  content_type = @headers['Content-Type'].split(';')[0] if @headers['Content-Type']
120
107
  content_type = content_type.gsub('text/javascript', 'application/javascript')
121
108
  tags = {status: @status, content_type: content_type}
122
-
109
+
123
110
  tags = tags.merge({controller: 'ActionController'})
124
111
  tags = tags.merge({action: 'RoutingError' }) if @status == 404
125
-
112
+
126
113
  values = {response_time: ((Time.now - start_time)*1000).round(2) }
127
114
 
128
115
  ZuoraConnect::AppInstanceBase.write_to_telegraf(direction: :inbound, tags: tags, values: values)
@@ -133,5 +120,32 @@ module ZuoraConnect
133
120
  [@status, @headers, @response]
134
121
  end
135
122
  end
123
+
124
+ def process_prometheus_metric(type: 'none', metrics: {})
125
+ return if metrics.blank?
126
+
127
+ prometheus = Prometheus::Client.registry
128
+ most_recent_aggregation = {}
129
+ if Prometheus::Client.config.data_store.is_a?(Prometheus::Client::DataStores::DirectFileStore)
130
+ most_recent_aggregation[:aggregation] = :most_recent
131
+ end
132
+ metrics.each do |key, value|
133
+ next if %w[app_name url].include?(key.to_s)
134
+
135
+ if value.is_a?(Hash)
136
+ process_prometheus_metric(type: key.to_s, metrics: value)
137
+ else
138
+ gauge_name = key.to_s.downcase.gsub(/[^a-z0-9_]/, '_')
139
+ gauge = prometheus.get(gauge_name.to_sym) || prometheus.gauge(
140
+ gauge_name.to_sym,
141
+ docstring: "#{key} metric",
142
+ labels: %i(type name),
143
+ preset_labels: { type: type, name: ZuoraConnect::Telegraf.app_name },
144
+ store_settings: most_recent_aggregation
145
+ )
146
+ gauge.set(value)
147
+ end
148
+ end
149
+ end
136
150
  end
137
151
  end
@@ -47,7 +47,7 @@ module Resque
47
47
  raise
48
48
  end
49
49
  rescue PG::ConnectionBad => exception
50
- Rails.logger.info("Bad Connection Restart")
50
+ Rails.logger.warn("Bad Connection Restart", exception)
51
51
  Resque.enqueue_to(self.job.queue, self.job.payload['class'], args)
52
52
  return
53
53
  rescue ZuoraConnect::Exceptions::ConnectCommunicationError => exception
@@ -219,6 +219,80 @@ module ZuoraConnect
219
219
  return (request.headers['ZuoraCurrentEntity'].present? || cookies['ZuoraCurrentEntity'].present?)
220
220
  end
221
221
 
222
+ def create_new_instance
223
+ ZuoraConnect::AppInstance.read_master_db do
224
+ Thread.current[:appinstance] = nil
225
+ ZuoraConnect.logger.with_fields = {} if ZuoraConnect.logger.is_a?(Ougai::Logger)
226
+ Rails.logger.with_fields = {} if Rails.logger.is_a?(Ougai::Logger)
227
+
228
+ if defined?(ElasticAPM) && ElasticAPM.running? && ElasticAPM.respond_to?(:set_label)
229
+ ElasticAPM.set_label(:trace_id, request.uuid)
230
+ end
231
+
232
+ zuora_host = request.headers['zuora-host']
233
+ zuora_entity_id = (request.headers['zuora-entity-ids'] || '').gsub(
234
+ '-',
235
+ ''
236
+ ).split(',').first
237
+
238
+ # Validate host present
239
+ if zuora_host.blank?
240
+ render json: {
241
+ status: 401,
242
+ message: 'zuora-host header was not supplied.'
243
+ }, status: :unauthorized
244
+ return
245
+ end
246
+
247
+ # Validate entity-ids present
248
+ if zuora_entity_id.blank?
249
+ render json: {
250
+ status: 401,
251
+ message: 'zuora-entity-ids header was not supplied.'
252
+ }, status: :unauthorized
253
+ return
254
+ end
255
+
256
+ rest_domain = ZuoraAPI::Login.new(url: "https://#{zuora_host}").rest_domain
257
+ app_instance_id = ZuoraConnect::AppInstance.where(
258
+ 'zuora_entity_ids ?& array[:entities] AND zuora_domain = :host',
259
+ entities: [zuora_entity_id],
260
+ host: rest_domain
261
+ ).pluck(:id).first
262
+
263
+ if app_instance_id.present?
264
+ render json: {
265
+ status: 409,
266
+ message: 'Instance already exists.',
267
+ app_instance_id: app_instance_id
268
+ }, status: 409
269
+ else
270
+ Apartment::Tenant.switch!("public")
271
+ retry_count = 3
272
+ begin
273
+ @appinstance = new_instance(
274
+ next_instance_id,
275
+ zuora_entity_id,
276
+ rest_domain,
277
+ retry_count: retry_count
278
+ )
279
+ rescue ActiveRecord::RecordNotUnique
280
+ retry if (retry_count -= 1).positive?
281
+ return
282
+ end
283
+
284
+ app_instance_id = @appinstance.id
285
+ end
286
+
287
+ begin
288
+ Apartment::Tenant.switch!('public')
289
+ Apartment::Tenant.create(app_instance_id.to_s)
290
+ rescue Apartment::TenantExists
291
+ ZuoraConnect.logger.debug('Tenant Already Exists')
292
+ end
293
+ end
294
+ end
295
+
222
296
  private
223
297
  def setup_instance_via_prod_mode
224
298
  zuora_entity_id = request.headers['ZuoraCurrentEntity'] || cookies['ZuoraCurrentEntity']
@@ -226,7 +300,7 @@ module ZuoraConnect
226
300
  if zuora_entity_id.present?
227
301
  zuora_tenant_id = cookies['Zuora-Tenant-Id']
228
302
  zuora_user_id = cookies['Zuora-User-Id']
229
- zuora_host = request.headers["HTTP_X_FORWARDED_HOST"] || "apisandbox.zuora.com"
303
+ zuora_host = request.headers['HTTP_X_FORWARDED_HOST'] || request.headers['Zuora-Host'] || 'apisandbox.zuora.com'
230
304
 
231
305
  zuora_details = {'host' => zuora_host, 'user_id' => zuora_user_id, 'tenant_id' => zuora_tenant_id, 'entity_id' => zuora_entity_id}
232
306
  auth_headers = {}
@@ -328,13 +402,16 @@ module ZuoraConnect
328
402
 
329
403
  zuora_user_id = cookies['Zuora-User-Id'] || session["ZuoraCurrentIdentity"]['userId']
330
404
 
331
- #One deployed instance
332
405
  if appinstances.size == 1
333
406
  ZuoraConnect.logger.debug("Instance is #{appinstances.to_h.keys.first}")
334
407
  @appinstance = ZuoraConnect::AppInstance.find(appinstances.to_h.keys.first)
408
+ end
335
409
 
410
+ # One deployed instance with credentials
411
+ if defined?(@appinstance) && !@appinstance['zuora_logins'].nil?
336
412
  #Add user/update
337
413
  begin
414
+ ZuoraConnect::ZuoraUser.reset_table_name
338
415
  @zuora_user = ZuoraConnect::ZuoraUser.where(:zuora_user_id => zuora_user_id).first
339
416
  rescue ActiveRecord::StatementInvalid => ex
340
417
  if ex.message.include?("PG::UndefinedTable") && ex.message.include?("zuora_users")
@@ -380,79 +457,85 @@ module ZuoraConnect
380
457
  return
381
458
  end
382
459
  Apartment::Tenant.switch!("public")
383
- ActiveRecord::Base.transaction do
384
- ActiveRecord::Base.connection.execute('LOCK public.zuora_users IN ACCESS EXCLUSIVE MODE')
385
- appinstances = ZuoraConnect::AppInstance.where("zuora_entity_ids ?& array[:entities] = true AND zuora_domain = :host", entities: [zuora_entity_id], host: zuora_client.rest_domain).pluck(:id, :name)
460
+ retry_count = 3
461
+ task_data = {}
462
+ begin
463
+ ActiveRecord::Base.transaction do
464
+ ActiveRecord::Base.connection.execute('LOCK public.zuora_users IN ACCESS EXCLUSIVE MODE')
386
465
 
387
- if appinstances.size > 0
388
- redirect_to "https://#{zuora_host}/apps/newlogin.do?retURL=#{request.fullpath}"
389
- return
390
- end
466
+ unless defined?(@appinstance)
467
+ appinstances = ZuoraConnect::AppInstance.where("zuora_entity_ids ?& array[:entities] = true AND zuora_domain = :host", entities: [zuora_entity_id], host: zuora_client.rest_domain).pluck(:id, :name)
391
468
 
392
- next_id = (ZuoraConnect::AppInstance.all.where('id > 24999999').order(id: :desc).limit(1).pluck(:id).first || 24999999) + 1
393
- user = (ENV['DEIS_APP'] || "Application").split('-').map(&:capitalize).join(' ')
394
- body = {
395
- 'userId' => zuora_user_id,
396
- 'entityIds' => [zuora_entity_id.unpack("a8a4a4a4a12").join('-')],
397
- 'customAuthorities' => [],
398
- 'additionalInformation' => {
399
- 'description' => "This user is for #{user} application.",
400
- 'name' => "#{user} API User #{next_id}"
401
- }
402
- }
403
-
404
- oauth_response, response = zuora_client.rest_call(
405
- method: :post,
406
- body: body.to_json,
407
- url: zuora_client.rest_endpoint("genesis/clients").gsub('v1/', ''),
408
- session_type: zuora_client.class == ZuoraAPI::Oauth ? :bearer : :basic,
409
- headers: auth_headers
410
- )
469
+ if appinstances.size > 0
470
+ redirect_to "https://#{zuora_host}/apps/newlogin.do?retURL=#{request.fullpath}"
471
+ return
472
+ end
473
+ end
411
474
 
412
- new_zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", oauth_client_id: oauth_response["clientId"], oauth_secret: oauth_response["clientSecret"] )
413
- if session["ZuoraCurrentUserInfo"].blank?
414
- client_describe, response = new_zuora_client.rest_call(url: zuora_client.rest_endpoint("genesis/user/info").gsub('v1/', ''), session_type: :bearer)
415
- else
416
- client_describe = session["ZuoraCurrentUserInfo"]
417
- end
475
+ next_id = defined?(@appinstance) ? @appinstance.id : next_instance_id
476
+ if task_data.blank?
477
+ user = (ENV['DEIS_APP'] || "Application").split('-').map(&:capitalize).join(' ')
478
+ body = {
479
+ 'userId' => zuora_user_id,
480
+ 'entityIds' => [zuora_entity_id.unpack("a8a4a4a4a12").join('-')],
481
+ 'customAuthorities' => [],
482
+ 'additionalInformation' => {
483
+ 'description' => "This user is for #{user} application.",
484
+ 'name' => "#{user} API User #{next_id}"
485
+ }
486
+ }
487
+
488
+ oauth_response, response = zuora_client.rest_call(
489
+ method: :post,
490
+ body: body.to_json,
491
+ url: zuora_client.rest_endpoint("genesis/clients").gsub('v1/', ''),
492
+ session_type: zuora_client.class == ZuoraAPI::Oauth ? :bearer : :basic,
493
+ headers: auth_headers
494
+ )
495
+
496
+ new_zuora_client = ZuoraAPI::Oauth.new(url: "https://#{zuora_host}", oauth_client_id: oauth_response["clientId"], oauth_secret: oauth_response["clientSecret"] )
497
+ if session["ZuoraCurrentUserInfo"].blank?
498
+ client_describe, response = new_zuora_client.rest_call(url: zuora_client.rest_endpoint("genesis/user/info").gsub('v1/', ''), session_type: :bearer)
499
+ else
500
+ client_describe = session["ZuoraCurrentUserInfo"]
501
+ end
418
502
 
419
- available_entities = client_describe["accessibleEntities"].select {|entity| entity['id'] == zuora_entity_id}
420
- task_data = {
421
- "id": next_id,
422
- "name": client_describe["tenantName"],
423
- "mode": "Collections",
424
- "status": "Running",
425
- ZuoraConnect::AppInstance::LOGIN_TENANT_DESTINATION => {
426
- "tenant_type": "Zuora",
427
- "username": session["ZuoraCurrentIdentity"]["username"],
428
- "url": new_zuora_client.url,
429
- "status": "Active",
430
- "oauth_client_id": oauth_response['clientId'],
431
- "oauth_secret": oauth_response['clientSecret'],
432
- "authentication_type": "OAUTH",
433
- "entities": available_entities.map {|e| e.merge({'displayName' => client_describe["tenantName"]})}
434
- },
435
- "tenant_ids": available_entities.map{|e| e['entityId']}.uniq,
436
- }
437
- mapped_values = {:id => next_id, :api_token => rand(36**64).to_s(36), :token => rand(36**64).to_s(36), :zuora_logins => task_data, :oauth_expires_at => Time.now + 1000.years, :zuora_domain => zuora_client.rest_domain, :zuora_entity_ids => [zuora_entity_id]}
438
- @appinstance = ZuoraConnect::AppInstance.new(mapped_values)
439
- retry_count = 0
440
- begin
441
- @appinstance.save(:validate => false)
442
- rescue ActiveRecord::RecordNotUnique => ex
443
- if (retry_count += 1) < 3
444
- @appinstance.assign_attributes({:api_token => rand(36**64).to_s(36), :token => rand(36**64).to_s(36)})
445
- retry
503
+ available_entities = client_describe["accessibleEntities"].select {|entity| entity['id'] == zuora_entity_id}
504
+ task_data = {
505
+ "id": next_id,
506
+ "name": client_describe["tenantName"],
507
+ "mode": "Collections",
508
+ "status": "Running",
509
+ ZuoraConnect::AppInstance::LOGIN_TENANT_DESTINATION => {
510
+ "tenant_type": "Zuora",
511
+ "username": session["ZuoraCurrentIdentity"]["username"],
512
+ "url": new_zuora_client.url,
513
+ "status": "Active",
514
+ "oauth_client_id": oauth_response['clientId'],
515
+ "oauth_secret": oauth_response['clientSecret'],
516
+ "authentication_type": "OAUTH",
517
+ "entities": available_entities.map {|e| e.merge({'displayName' => client_describe["tenantName"]})}
518
+ },
519
+ "tenant_ids": available_entities.map{|e| e['entityId']}.uniq,
520
+ }
521
+ end
522
+
523
+ if defined?(@appinstance)
524
+ @appinstance.zuora_logins = task_data
525
+ @appinstance.save(:validate => false)
446
526
  else
447
- Thread.current[:appinstance] = nil
448
- session["appInstance"] = nil
449
- render "zuora_connect/static/error_handled", :locals => {
450
- :title => "Application could not create unique tokens.",
451
- :message => "Please contact support or retry launching application."
452
- }, :layout => false
453
- return
527
+ @appinstance = new_instance(
528
+ next_id,
529
+ zuora_entity_id,
530
+ zuora_client.rest_domain,
531
+ task_data: task_data,
532
+ retry_count: retry_count
533
+ )
454
534
  end
455
535
  end
536
+ rescue ActiveRecord::RecordNotUnique
537
+ retry if (retry_count -= 1).positive?
538
+ return
456
539
  end
457
540
 
458
541
  Apartment::Tenant.switch!("public")
@@ -540,6 +623,50 @@ module ZuoraConnect
540
623
  end
541
624
  end
542
625
 
626
+ def next_instance_id
627
+ min_instance_id = 24_999_999
628
+ (ZuoraConnect::AppInstance.all.where("id > #{min_instance_id}").order(id: :desc).limit(1).pluck(:id).first || min_instance_id) + 1
629
+ end
630
+
631
+ def new_instance(id, zuora_entity_id, rest_domain, task_data: nil, retry_count: 0)
632
+ app_instance = ZuoraConnect::AppInstance.new(
633
+ :id => id,
634
+ :api_token => generate_token,
635
+ :token => generate_token,
636
+ :oauth_expires_at => Time.now + 1000.years,
637
+ :zuora_domain => rest_domain,
638
+ :zuora_entity_ids => [zuora_entity_id]
639
+ )
640
+
641
+ if task_data.nil?
642
+ # no encryption
643
+ app_instance['zuora_logins'] = task_data
644
+ else
645
+ # kms encrypt
646
+ app_instance.zuora_logins = task_data
647
+ end
648
+
649
+ begin
650
+ app_instance.save(:validate => false)
651
+ rescue ActiveRecord::RecordNotUnique
652
+ raise if retry_count > 1
653
+
654
+ Thread.current[:appinstance] = nil
655
+ session['appInstance'] = nil
656
+ render 'zuora_connect/static/error_handled', :locals => {
657
+ :title => 'Application could not create unique tokens.',
658
+ :message => 'Please contact support or retry launching application.'
659
+ }, :layout => false
660
+ return
661
+ end
662
+
663
+ app_instance
664
+ end
665
+
666
+ def generate_token
667
+ rand(36**64).to_s(36)
668
+ end
669
+
543
670
  def setup_instance_via_dev_mode
544
671
  session["appInstance"] = ZuoraConnect.configuration.dev_mode_appinstance
545
672
  user = ZuoraConnect.configuration.dev_mode_user
@@ -28,11 +28,6 @@ module ZuoraConnect
28
28
  ::Rails.configuration.action_dispatch.x_sendfile_header = nil
29
29
  end
30
30
 
31
- if defined? Prometheus
32
- initializer "prometheus.configure_rails_initialization" do |app|
33
- app.middleware.use Prometheus::Middleware::Exporter,(options ={:path => '/connect/internal/metrics'})
34
- end
35
- end
36
31
  initializer "zuora_connect.configure_rails_initialization" do |app|
37
32
  app.middleware.insert_after Rack::Sendfile, ZuoraConnect::MetricsMiddleware
38
33
  app.middleware.insert_after ActionDispatch::RequestId, ZuoraConnect::RequestIdMiddleware
@@ -40,6 +35,15 @@ module ZuoraConnect
40
35
  app.config.middleware.use ZuoraConnect::JsonParseErrors
41
36
  end
42
37
 
38
+ if defined? Prometheus
39
+ require 'rack'
40
+ require 'prometheus/middleware/exporter'
41
+ initializer "prometheus.configure_rails_initialization" do |app|
42
+ app.middleware.insert_after ZuoraConnect::MetricsMiddleware, Prometheus::Middleware::Exporter, path: '/connect/internal/metrics'
43
+ app.config.middleware.use Rack::Deflater, if: ->(env, *) { env['PATH_INFO'] == '/connect/internal/metrics' }
44
+ end
45
+ end
46
+
43
47
  # hook to process_action
44
48
  ActiveSupport::Notifications.subscribe('process_action.action_controller', ZuoraConnect::PageRequest.new)
45
49
 
@@ -1,3 +1,3 @@
1
1
  module ZuoraConnect
2
- VERSION = "2.0.59"
2
+ VERSION = "2.0.60e"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zuora_connect
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.59
4
+ version: 2.0.60e
5
5
  platform: ruby
6
6
  authors:
7
7
  - Connect Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-26 00:00:00.000000000 Z
11
+ date: 2020-08-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: apartment
@@ -429,53 +429,53 @@ required_ruby_version: !ruby/object:Gem::Requirement
429
429
  version: '0'
430
430
  required_rubygems_version: !ruby/object:Gem::Requirement
431
431
  requirements:
432
- - - ">="
432
+ - - ">"
433
433
  - !ruby/object:Gem::Version
434
- version: '0'
434
+ version: 1.3.1
435
435
  requirements: []
436
436
  rubygems_version: 3.0.3
437
437
  signing_key:
438
438
  specification_version: 4
439
439
  summary: Summary of Connect.
440
440
  test_files:
441
- - test/integration/navigation_test.rb
442
- - test/controllers/zuora_connect/api/v1/app_instance_controller_test.rb
443
441
  - test/fixtures/zuora_connect/app_instances.yml
444
- - test/lib/generators/zuora_connect/datatable_generator_test.rb
445
442
  - test/models/zuora_connect/app_instance_test.rb
443
+ - test/integration/navigation_test.rb
444
+ - test/controllers/zuora_connect/api/v1/app_instance_controller_test.rb
446
445
  - test/zuora_connect_test.rb
447
- - test/dummy/Rakefile
448
- - test/dummy/config.ru
449
- - test/dummy/public/422.html
450
- - test/dummy/public/404.html
446
+ - test/lib/generators/zuora_connect/datatable_generator_test.rb
451
447
  - test/dummy/public/500.html
448
+ - test/dummy/public/404.html
452
449
  - test/dummy/public/favicon.ico
453
- - test/dummy/app/controllers/application_controller.rb
454
- - test/dummy/app/helpers/application_helper.rb
455
- - test/dummy/app/views/layouts/application.html.erb
456
- - test/dummy/app/assets/javascripts/application.js
457
- - test/dummy/app/assets/stylesheets/application.css
458
- - test/dummy/README.rdoc
459
- - test/dummy/bin/rails
460
- - test/dummy/bin/rake
461
- - test/dummy/bin/bundle
462
- - test/dummy/bin/setup
450
+ - test/dummy/public/422.html
451
+ - test/dummy/Rakefile
452
+ - test/dummy/config.ru
463
453
  - test/dummy/config/secrets.yml
464
454
  - test/dummy/config/boot.rb
465
- - test/dummy/config/initializers/mime_types.rb
466
- - test/dummy/config/initializers/filter_parameter_logging.rb
455
+ - test/dummy/config/application.rb
467
456
  - test/dummy/config/initializers/session_store.rb
468
- - test/dummy/config/initializers/inflections.rb
469
- - test/dummy/config/initializers/cookies_serializer.rb
470
457
  - test/dummy/config/initializers/assets.rb
471
458
  - test/dummy/config/initializers/wrap_parameters.rb
459
+ - test/dummy/config/initializers/filter_parameter_logging.rb
472
460
  - test/dummy/config/initializers/backtrace_silencers.rb
473
- - test/dummy/config/database.yml
474
- - test/dummy/config/environment.rb
475
- - test/dummy/config/application.rb
461
+ - test/dummy/config/initializers/mime_types.rb
462
+ - test/dummy/config/initializers/inflections.rb
463
+ - test/dummy/config/initializers/cookies_serializer.rb
476
464
  - test/dummy/config/routes.rb
465
+ - test/dummy/config/database.yml
466
+ - test/dummy/config/environments/test.rb
477
467
  - test/dummy/config/environments/production.rb
478
468
  - test/dummy/config/environments/development.rb
479
- - test/dummy/config/environments/test.rb
469
+ - test/dummy/config/environment.rb
480
470
  - test/dummy/config/locales/en.yml
471
+ - test/dummy/README.rdoc
472
+ - test/dummy/bin/rake
473
+ - test/dummy/bin/setup
474
+ - test/dummy/bin/rails
475
+ - test/dummy/bin/bundle
476
+ - test/dummy/app/views/layouts/application.html.erb
477
+ - test/dummy/app/assets/javascripts/application.js
478
+ - test/dummy/app/assets/stylesheets/application.css
479
+ - test/dummy/app/helpers/application_helper.rb
480
+ - test/dummy/app/controllers/application_controller.rb
481
481
  - test/test_helper.rb