apisonator 2.100.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 +7 -0
- data/CHANGELOG.md +317 -0
- data/Gemfile +11 -0
- data/Gemfile.base +65 -0
- data/Gemfile.lock +319 -0
- data/Gemfile.on_prem +1 -0
- data/Gemfile.on_prem.lock +297 -0
- data/LICENSE +202 -0
- data/NOTICE +15 -0
- data/README.md +230 -0
- data/Rakefile +287 -0
- data/apisonator.gemspec +47 -0
- data/app/api/api.rb +13 -0
- data/app/api/internal/alert_limits.rb +32 -0
- data/app/api/internal/application_keys.rb +49 -0
- data/app/api/internal/application_referrer_filters.rb +43 -0
- data/app/api/internal/applications.rb +77 -0
- data/app/api/internal/errors.rb +54 -0
- data/app/api/internal/events.rb +42 -0
- data/app/api/internal/internal.rb +104 -0
- data/app/api/internal/metrics.rb +40 -0
- data/app/api/internal/service_tokens.rb +46 -0
- data/app/api/internal/services.rb +58 -0
- data/app/api/internal/stats.rb +42 -0
- data/app/api/internal/usagelimits.rb +62 -0
- data/app/api/internal/utilization.rb +23 -0
- data/bin/3scale_backend +223 -0
- data/bin/3scale_backend_worker +26 -0
- data/config.ru +4 -0
- data/config/puma.rb +192 -0
- data/config/schedule.rb +9 -0
- data/ext/mkrf_conf.rb +64 -0
- data/lib/3scale/backend.rb +67 -0
- data/lib/3scale/backend/alert_limit.rb +56 -0
- data/lib/3scale/backend/alerts.rb +137 -0
- data/lib/3scale/backend/analytics/kinesis.rb +3 -0
- data/lib/3scale/backend/analytics/kinesis/adapter.rb +180 -0
- data/lib/3scale/backend/analytics/kinesis/exporter.rb +86 -0
- data/lib/3scale/backend/analytics/kinesis/job.rb +135 -0
- data/lib/3scale/backend/analytics/redshift.rb +3 -0
- data/lib/3scale/backend/analytics/redshift/adapter.rb +367 -0
- data/lib/3scale/backend/analytics/redshift/importer.rb +83 -0
- data/lib/3scale/backend/analytics/redshift/job.rb +33 -0
- data/lib/3scale/backend/application.rb +330 -0
- data/lib/3scale/backend/application_events.rb +76 -0
- data/lib/3scale/backend/background_job.rb +65 -0
- data/lib/3scale/backend/configurable.rb +20 -0
- data/lib/3scale/backend/configuration.rb +151 -0
- data/lib/3scale/backend/configuration/loader.rb +42 -0
- data/lib/3scale/backend/constants.rb +19 -0
- data/lib/3scale/backend/cors.rb +84 -0
- data/lib/3scale/backend/distributed_lock.rb +67 -0
- data/lib/3scale/backend/environment.rb +21 -0
- data/lib/3scale/backend/error_storage.rb +52 -0
- data/lib/3scale/backend/errors.rb +343 -0
- data/lib/3scale/backend/event_storage.rb +120 -0
- data/lib/3scale/backend/experiment.rb +84 -0
- data/lib/3scale/backend/extensions.rb +5 -0
- data/lib/3scale/backend/extensions/array.rb +19 -0
- data/lib/3scale/backend/extensions/hash.rb +26 -0
- data/lib/3scale/backend/extensions/nil_class.rb +13 -0
- data/lib/3scale/backend/extensions/redis.rb +44 -0
- data/lib/3scale/backend/extensions/string.rb +13 -0
- data/lib/3scale/backend/extensions/time.rb +110 -0
- data/lib/3scale/backend/failed_jobs_scheduler.rb +141 -0
- data/lib/3scale/backend/job_fetcher.rb +122 -0
- data/lib/3scale/backend/listener.rb +728 -0
- data/lib/3scale/backend/listener_metrics.rb +99 -0
- data/lib/3scale/backend/logging.rb +48 -0
- data/lib/3scale/backend/logging/external.rb +44 -0
- data/lib/3scale/backend/logging/external/impl.rb +93 -0
- data/lib/3scale/backend/logging/external/impl/airbrake.rb +66 -0
- data/lib/3scale/backend/logging/external/impl/bugsnag.rb +69 -0
- data/lib/3scale/backend/logging/external/impl/default.rb +18 -0
- data/lib/3scale/backend/logging/external/resque.rb +57 -0
- data/lib/3scale/backend/logging/logger.rb +18 -0
- data/lib/3scale/backend/logging/middleware.rb +62 -0
- data/lib/3scale/backend/logging/middleware/json_writer.rb +21 -0
- data/lib/3scale/backend/logging/middleware/text_writer.rb +60 -0
- data/lib/3scale/backend/logging/middleware/writer.rb +143 -0
- data/lib/3scale/backend/logging/worker.rb +107 -0
- data/lib/3scale/backend/manifest.rb +80 -0
- data/lib/3scale/backend/memoizer.rb +277 -0
- data/lib/3scale/backend/metric.rb +275 -0
- data/lib/3scale/backend/metric/collection.rb +91 -0
- data/lib/3scale/backend/oauth.rb +4 -0
- data/lib/3scale/backend/oauth/token.rb +26 -0
- data/lib/3scale/backend/oauth/token_key.rb +30 -0
- data/lib/3scale/backend/oauth/token_storage.rb +313 -0
- data/lib/3scale/backend/oauth/token_value.rb +25 -0
- data/lib/3scale/backend/period.rb +3 -0
- data/lib/3scale/backend/period/boundary.rb +107 -0
- data/lib/3scale/backend/period/cache.rb +28 -0
- data/lib/3scale/backend/period/period.rb +402 -0
- data/lib/3scale/backend/queue_storage.rb +16 -0
- data/lib/3scale/backend/rack.rb +49 -0
- data/lib/3scale/backend/rack/exception_catcher.rb +136 -0
- data/lib/3scale/backend/rack/internal_error_catcher.rb +23 -0
- data/lib/3scale/backend/rack/prometheus.rb +19 -0
- data/lib/3scale/backend/saas.rb +6 -0
- data/lib/3scale/backend/saas_analytics.rb +4 -0
- data/lib/3scale/backend/server.rb +30 -0
- data/lib/3scale/backend/server/falcon.rb +52 -0
- data/lib/3scale/backend/server/puma.rb +71 -0
- data/lib/3scale/backend/service.rb +317 -0
- data/lib/3scale/backend/service_token.rb +97 -0
- data/lib/3scale/backend/stats.rb +8 -0
- data/lib/3scale/backend/stats/aggregator.rb +170 -0
- data/lib/3scale/backend/stats/aggregators/base.rb +72 -0
- data/lib/3scale/backend/stats/aggregators/response_code.rb +58 -0
- data/lib/3scale/backend/stats/aggregators/usage.rb +34 -0
- data/lib/3scale/backend/stats/bucket_reader.rb +135 -0
- data/lib/3scale/backend/stats/bucket_storage.rb +108 -0
- data/lib/3scale/backend/stats/cleaner.rb +195 -0
- data/lib/3scale/backend/stats/codes_commons.rb +14 -0
- data/lib/3scale/backend/stats/delete_job_def.rb +60 -0
- data/lib/3scale/backend/stats/key_generator.rb +73 -0
- data/lib/3scale/backend/stats/keys.rb +104 -0
- data/lib/3scale/backend/stats/partition_eraser_job.rb +58 -0
- data/lib/3scale/backend/stats/partition_generator_job.rb +46 -0
- data/lib/3scale/backend/stats/period_commons.rb +34 -0
- data/lib/3scale/backend/stats/stats_parser.rb +141 -0
- data/lib/3scale/backend/stats/storage.rb +113 -0
- data/lib/3scale/backend/statsd.rb +14 -0
- data/lib/3scale/backend/storable.rb +35 -0
- data/lib/3scale/backend/storage.rb +40 -0
- data/lib/3scale/backend/storage_async.rb +4 -0
- data/lib/3scale/backend/storage_async/async_redis.rb +21 -0
- data/lib/3scale/backend/storage_async/client.rb +205 -0
- data/lib/3scale/backend/storage_async/pipeline.rb +79 -0
- data/lib/3scale/backend/storage_async/resque_extensions.rb +30 -0
- data/lib/3scale/backend/storage_helpers.rb +278 -0
- data/lib/3scale/backend/storage_key_helpers.rb +9 -0
- data/lib/3scale/backend/storage_sync.rb +43 -0
- data/lib/3scale/backend/transaction.rb +62 -0
- data/lib/3scale/backend/transactor.rb +177 -0
- data/lib/3scale/backend/transactor/limit_headers.rb +54 -0
- data/lib/3scale/backend/transactor/notify_batcher.rb +139 -0
- data/lib/3scale/backend/transactor/notify_job.rb +47 -0
- data/lib/3scale/backend/transactor/process_job.rb +33 -0
- data/lib/3scale/backend/transactor/report_job.rb +84 -0
- data/lib/3scale/backend/transactor/status.rb +236 -0
- data/lib/3scale/backend/transactor/usage_report.rb +182 -0
- data/lib/3scale/backend/usage.rb +63 -0
- data/lib/3scale/backend/usage_limit.rb +115 -0
- data/lib/3scale/backend/use_cases/provider_key_change_use_case.rb +60 -0
- data/lib/3scale/backend/util.rb +17 -0
- data/lib/3scale/backend/validators.rb +26 -0
- data/lib/3scale/backend/validators/base.rb +36 -0
- data/lib/3scale/backend/validators/key.rb +17 -0
- data/lib/3scale/backend/validators/limits.rb +57 -0
- data/lib/3scale/backend/validators/oauth_key.rb +15 -0
- data/lib/3scale/backend/validators/oauth_setting.rb +15 -0
- data/lib/3scale/backend/validators/redirect_uri.rb +33 -0
- data/lib/3scale/backend/validators/referrer.rb +60 -0
- data/lib/3scale/backend/validators/service_state.rb +15 -0
- data/lib/3scale/backend/validators/state.rb +15 -0
- data/lib/3scale/backend/version.rb +5 -0
- data/lib/3scale/backend/views/oauth_access_tokens.builder +14 -0
- data/lib/3scale/backend/views/oauth_app_id_by_token.builder +4 -0
- data/lib/3scale/backend/worker.rb +87 -0
- data/lib/3scale/backend/worker_async.rb +88 -0
- data/lib/3scale/backend/worker_metrics.rb +44 -0
- data/lib/3scale/backend/worker_sync.rb +32 -0
- data/lib/3scale/bundler_shim.rb +17 -0
- data/lib/3scale/prometheus_server.rb +10 -0
- data/lib/3scale/tasks/connectivity.rake +41 -0
- data/lib/3scale/tasks/helpers.rb +3 -0
- data/lib/3scale/tasks/helpers/environment.rb +23 -0
- data/lib/3scale/tasks/stats.rake +131 -0
- data/lib/3scale/tasks/swagger.rake +46 -0
- data/licenses.xml +1215 -0
- metadata +227 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module ThreeScale
|
|
2
|
+
module Backend
|
|
3
|
+
|
|
4
|
+
class BackgroundJob
|
|
5
|
+
include Configurable
|
|
6
|
+
|
|
7
|
+
EMPTY_HOOKS = [].freeze
|
|
8
|
+
Error = Class.new StandardError
|
|
9
|
+
|
|
10
|
+
class << self
|
|
11
|
+
def perform(*args)
|
|
12
|
+
perform_wrapper(args)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def perform_logged(*_args)
|
|
16
|
+
raise "This should be overloaded."
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Disable hooks to improve performance. Profiling tests show that some
|
|
20
|
+
# significant CPU resources were consumed sorting and filtering these.
|
|
21
|
+
def hooks
|
|
22
|
+
EMPTY_HOOKS
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
def enqueue_time(args)
|
|
28
|
+
args.last or raise('Enqueue time not specified')
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def perform_wrapper(args)
|
|
32
|
+
start_time = Time.now.getutc
|
|
33
|
+
status_ok, message = perform_logged(*args)
|
|
34
|
+
stats_mem = Memoizer.stats
|
|
35
|
+
end_time = Time.now.getutc
|
|
36
|
+
|
|
37
|
+
raise Error, 'No job message given' unless message
|
|
38
|
+
prefix = log_class_name + ' ' + message
|
|
39
|
+
|
|
40
|
+
if status_ok
|
|
41
|
+
Worker.logger.info(prefix +
|
|
42
|
+
" #{(end_time - start_time).round(5)}" +
|
|
43
|
+
" #{(end_time.to_f - enqueue_time(args)).round(5)}"+
|
|
44
|
+
" #{stats_mem[:size]} #{stats_mem[:count]} #{stats_mem[:hits]}")
|
|
45
|
+
|
|
46
|
+
if configuration.worker_prometheus_metrics.enabled
|
|
47
|
+
update_prometheus_metrics(end_time - start_time)
|
|
48
|
+
end
|
|
49
|
+
else
|
|
50
|
+
Worker.logger.error("#{log_class_name} " + message)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def log_class_name
|
|
55
|
+
self.name.split('::').last
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def update_prometheus_metrics(runtime)
|
|
59
|
+
WorkerMetrics.increase_job_count(log_class_name)
|
|
60
|
+
WorkerMetrics.report_runtime(log_class_name, runtime)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require '3scale/backend/configuration'
|
|
2
|
+
|
|
3
|
+
module ThreeScale
|
|
4
|
+
module Backend
|
|
5
|
+
# Include this into any class to provide convenient access to the configuration.
|
|
6
|
+
module Configurable
|
|
7
|
+
def self.included(base)
|
|
8
|
+
base.extend(self)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def configuration
|
|
12
|
+
ThreeScale::Backend.configuration
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def configuration=(cfg)
|
|
16
|
+
ThreeScale::Backend.configuration=(cfg)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
require '3scale/backend/configuration/loader'
|
|
2
|
+
require '3scale/backend/environment'
|
|
3
|
+
require '3scale/backend/configurable'
|
|
4
|
+
|
|
5
|
+
module ThreeScale
|
|
6
|
+
module Backend
|
|
7
|
+
class << self
|
|
8
|
+
attr_accessor :configuration
|
|
9
|
+
|
|
10
|
+
def configure
|
|
11
|
+
yield configuration
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def parse_int(value, default)
|
|
17
|
+
case value
|
|
18
|
+
when "", nil, false then default
|
|
19
|
+
else Integer(value)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
NOTIFICATION_BATCH_DEFAULT = 10000
|
|
25
|
+
private_constant :NOTIFICATION_BATCH_DEFAULT
|
|
26
|
+
|
|
27
|
+
CONFIG_MASTER_METRICS_TRANSACTIONS_DEFAULT = "transactions".freeze
|
|
28
|
+
private_constant :CONFIG_MASTER_METRICS_TRANSACTIONS_DEFAULT
|
|
29
|
+
CONFIG_MASTER_METRICS_TRANSACTIONS_AUTHORIZE_DEFAULT = "transactions/authorize".freeze
|
|
30
|
+
private_constant :CONFIG_MASTER_METRICS_TRANSACTIONS_AUTHORIZE_DEFAULT
|
|
31
|
+
|
|
32
|
+
CONFIG_DELETE_STATS_BATCH_SIZE = 50
|
|
33
|
+
private_constant :CONFIG_DELETE_STATS_BATCH_SIZE
|
|
34
|
+
CONFIG_DELETE_STATS_PARTITION_BATCH_SIZE = 1000
|
|
35
|
+
private_constant :CONFIG_DELETE_STATS_PARTITION_BATCH_SIZE
|
|
36
|
+
|
|
37
|
+
@configuration = Configuration::Loader.new
|
|
38
|
+
|
|
39
|
+
# assign @configuration first, since code can depend on the attr_reader
|
|
40
|
+
@configuration.tap do |config|
|
|
41
|
+
# To distinguish between SaaS and on-premises mode.
|
|
42
|
+
config.saas = true
|
|
43
|
+
|
|
44
|
+
config.request_loggers = [:text]
|
|
45
|
+
config.workers_logger_formatter = :text
|
|
46
|
+
|
|
47
|
+
# Add configuration sections
|
|
48
|
+
config.add_section(:queues, :master_name, :sentinels, :role,
|
|
49
|
+
:connect_timeout, :read_timeout, :write_timeout)
|
|
50
|
+
config.add_section(:redis, :url, :proxy, :sentinels, :role,
|
|
51
|
+
:connect_timeout, :read_timeout, :write_timeout,
|
|
52
|
+
:async)
|
|
53
|
+
config.add_section(:analytics_redis, :server,
|
|
54
|
+
:connect_timeout, :read_timeout, :write_timeout)
|
|
55
|
+
config.add_section(:hoptoad, :service, :api_key)
|
|
56
|
+
config.add_section(:stats, :bucket_size, :delete_batch_size, :delete_partition_batch_size)
|
|
57
|
+
config.add_section(:redshift, :host, :port, :dbname, :user, :password)
|
|
58
|
+
config.add_section(:statsd, :host, :port)
|
|
59
|
+
config.add_section(:internal_api, :user, :password)
|
|
60
|
+
config.add_section(:oauth, :max_token_size)
|
|
61
|
+
config.add_section(:master, :metrics)
|
|
62
|
+
config.add_section(:worker_prometheus_metrics, :enabled, :port)
|
|
63
|
+
config.add_section(:listener_prometheus_metrics, :enabled, :port)
|
|
64
|
+
|
|
65
|
+
config.add_section(
|
|
66
|
+
:async_worker,
|
|
67
|
+
|
|
68
|
+
# Max number of jobs in the reactor
|
|
69
|
+
:max_concurrent_jobs,
|
|
70
|
+
# Max number of jobs in memory pending to be added to the reactor
|
|
71
|
+
:max_pending_jobs,
|
|
72
|
+
# Seconds to wait before fetching more jobs when the number of jobs
|
|
73
|
+
# in memory has reached max_pending_jobs.
|
|
74
|
+
:seconds_before_fetching_more
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Configure nested fields
|
|
78
|
+
master_metrics = [:transactions, :transactions_authorize]
|
|
79
|
+
config.master.metrics = Struct.new(*master_metrics).new
|
|
80
|
+
|
|
81
|
+
# Default config
|
|
82
|
+
config.master_service_id = 1
|
|
83
|
+
|
|
84
|
+
# This setting controls whether the listener can create event buckets in
|
|
85
|
+
# Redis. We do not want all the listeners creating buckets yet, as we do
|
|
86
|
+
# not know exactly the rate at which we can send events to Kinesis
|
|
87
|
+
# without problems.
|
|
88
|
+
# By default, we will allow creating buckets in any environment that is
|
|
89
|
+
# not 'production'.
|
|
90
|
+
# Notice that in order to create buckets, you also need to execute this
|
|
91
|
+
# rake task: stats:buckets:enable
|
|
92
|
+
config.can_create_event_buckets = !production?
|
|
93
|
+
|
|
94
|
+
config.legacy_referrer_filters = false
|
|
95
|
+
|
|
96
|
+
# Load configuration from a file.
|
|
97
|
+
config.load!([
|
|
98
|
+
'/etc/3scale_backend.conf',
|
|
99
|
+
'~/.3scale_backend.conf',
|
|
100
|
+
ENV['CONFIG_FILE']
|
|
101
|
+
].compact)
|
|
102
|
+
|
|
103
|
+
## this means that there will be a NotifyJob for every X notifications (this is
|
|
104
|
+
## the call to master)
|
|
105
|
+
config.notification_batch = parse_int(config.notification_batch,
|
|
106
|
+
NOTIFICATION_BATCH_DEFAULT)
|
|
107
|
+
|
|
108
|
+
# Assign default values to some configuration values
|
|
109
|
+
# that might been set in the config file but their
|
|
110
|
+
# environment variables not, or not have been set
|
|
111
|
+
# but should always have at least a default value.
|
|
112
|
+
# Also make sure that what we have is a String, just in case
|
|
113
|
+
# a type of data different than a String has been
|
|
114
|
+
# assigned to this configuration parameter
|
|
115
|
+
config.master.metrics.transactions = config.master.metrics.transactions.to_s
|
|
116
|
+
if config.master.metrics.transactions.empty?
|
|
117
|
+
config.master.metrics.transactions = CONFIG_MASTER_METRICS_TRANSACTIONS_DEFAULT
|
|
118
|
+
end
|
|
119
|
+
config.master.metrics.transactions_authorize = config.master.metrics.transactions_authorize.to_s
|
|
120
|
+
if config.master.metrics.transactions_authorize.empty?
|
|
121
|
+
config.master.metrics.transactions_authorize = CONFIG_MASTER_METRICS_TRANSACTIONS_AUTHORIZE_DEFAULT
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# can_create_event_buckets is just for our SaaS analytics system.
|
|
125
|
+
# If SaaS has been set to false, we need to disable buckets too.
|
|
126
|
+
config.can_create_event_buckets = false unless config.saas
|
|
127
|
+
|
|
128
|
+
config.stats.delete_batch_size = parse_int(config.stats.delete_batch_size,
|
|
129
|
+
CONFIG_DELETE_STATS_BATCH_SIZE)
|
|
130
|
+
|
|
131
|
+
config.stats.delete_partition_batch_size = parse_int(config.stats.delete_partition_batch_size,
|
|
132
|
+
CONFIG_DELETE_STATS_PARTITION_BATCH_SIZE)
|
|
133
|
+
|
|
134
|
+
# often we don't have a log_file setting - generate it here from
|
|
135
|
+
# the log_path setting.
|
|
136
|
+
log_file = config.log_file
|
|
137
|
+
if !log_file || log_file.empty?
|
|
138
|
+
log_path = config.log_path
|
|
139
|
+
config.log_file = if log_path && !log_path.empty?
|
|
140
|
+
if File.stat(log_path).ftype == 'directory'
|
|
141
|
+
"#{log_path}/backend_logger.log"
|
|
142
|
+
else
|
|
143
|
+
log_path
|
|
144
|
+
end
|
|
145
|
+
else
|
|
146
|
+
ENV['CONFIG_LOG_FILE'] || STDOUT
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'ostruct'
|
|
2
|
+
|
|
3
|
+
module ThreeScale
|
|
4
|
+
module Backend
|
|
5
|
+
module Configuration
|
|
6
|
+
class Loader < OpenStruct
|
|
7
|
+
Error = Class.new StandardError
|
|
8
|
+
NoConfigFiles = Class.new Error
|
|
9
|
+
|
|
10
|
+
# Add configuration section with the given fields.
|
|
11
|
+
#
|
|
12
|
+
# == Example
|
|
13
|
+
#
|
|
14
|
+
# # Define section like this
|
|
15
|
+
# loader = Loader.new
|
|
16
|
+
# loader.add_section(:bacons, :type, :amount)
|
|
17
|
+
#
|
|
18
|
+
# # Configure default values like this
|
|
19
|
+
# loader.bacons.type = :chunky
|
|
20
|
+
# loader.bacons.amount = 'a lot'
|
|
21
|
+
#
|
|
22
|
+
# # Load the configuration from an array of files
|
|
23
|
+
# loader.load!(files)
|
|
24
|
+
#
|
|
25
|
+
# # Use like this
|
|
26
|
+
# loader.bacons.type # :chunky
|
|
27
|
+
#
|
|
28
|
+
def add_section(name, *fields)
|
|
29
|
+
send("#{name}=", Struct.new(*fields).new)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Load configuration from a set of files
|
|
33
|
+
def load!(files)
|
|
34
|
+
raise NoConfigFiles if !files || files.empty?
|
|
35
|
+
files.each do |path|
|
|
36
|
+
load path if File.readable?(File.expand_path(path))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module ThreeScale
|
|
2
|
+
module Backend
|
|
3
|
+
module Constants
|
|
4
|
+
module All
|
|
5
|
+
TIME_FORMAT = '%Y-%m-%d %H:%M:%S %z'.freeze
|
|
6
|
+
PIPELINED_SLICE_SIZE = 400
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.included(base)
|
|
10
|
+
All.constants.each do |k|
|
|
11
|
+
base.const_set(k, All.const_get(k))
|
|
12
|
+
base.private_constant k
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
include Constants
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# CORS support
|
|
2
|
+
#
|
|
3
|
+
# Please see references:
|
|
4
|
+
#
|
|
5
|
+
# https://www.w3.org/TR/cors/
|
|
6
|
+
# https://code.google.com/archive/p/html5security/wikis/CrossOriginRequestSecurity.wiki
|
|
7
|
+
#
|
|
8
|
+
module ThreeScale
|
|
9
|
+
module Backend
|
|
10
|
+
module CORS
|
|
11
|
+
def self.stringify_consts(*consts)
|
|
12
|
+
consts.each do |k|
|
|
13
|
+
val = const_get k
|
|
14
|
+
val = val.respond_to?(:join) ? val.join(', ') : val.to_s
|
|
15
|
+
k_s = "#{k}_S".to_sym
|
|
16
|
+
const_set(k_s, val.freeze)
|
|
17
|
+
private_constant k_s
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
private_class_method :stringify_consts
|
|
21
|
+
|
|
22
|
+
MAX_AGE = 86400
|
|
23
|
+
private_constant :MAX_AGE
|
|
24
|
+
|
|
25
|
+
ALLOW_ORIGIN = '*'.freeze
|
|
26
|
+
private_constant :ALLOW_ORIGIN
|
|
27
|
+
|
|
28
|
+
ALLOW_METHODS = [
|
|
29
|
+
'GET'.freeze,
|
|
30
|
+
'POST'.freeze,
|
|
31
|
+
'PATCH'.freeze,
|
|
32
|
+
'PUT'.freeze,
|
|
33
|
+
'DELETE'.freeze
|
|
34
|
+
].freeze
|
|
35
|
+
private_constant :ALLOW_METHODS
|
|
36
|
+
|
|
37
|
+
ALLOW_HEADERS = [
|
|
38
|
+
'Authorization'.freeze,
|
|
39
|
+
'Accept-Encoding'.freeze,
|
|
40
|
+
'Content-Type'.freeze,
|
|
41
|
+
'Cache-Control'.freeze,
|
|
42
|
+
'Accept'.freeze,
|
|
43
|
+
'If-Match'.freeze,
|
|
44
|
+
'If-Modified-Since'.freeze,
|
|
45
|
+
'If-None-Match'.freeze,
|
|
46
|
+
'If-Unmodified-Since'.freeze,
|
|
47
|
+
'X-Requested-With'.freeze,
|
|
48
|
+
'X-HTTP-Method-Override'.freeze,
|
|
49
|
+
'3scale-options'.freeze,
|
|
50
|
+
].freeze
|
|
51
|
+
private_constant :ALLOW_HEADERS
|
|
52
|
+
|
|
53
|
+
EXPOSE_HEADERS = [
|
|
54
|
+
'ETag'.freeze,
|
|
55
|
+
'Link'.freeze,
|
|
56
|
+
'3scale-rejection-reason'.freeze,
|
|
57
|
+
].freeze
|
|
58
|
+
private_constant :EXPOSE_HEADERS
|
|
59
|
+
|
|
60
|
+
stringify_consts :MAX_AGE, :ALLOW_METHODS, :ALLOW_HEADERS, :EXPOSE_HEADERS
|
|
61
|
+
|
|
62
|
+
HEADERS = {
|
|
63
|
+
'Access-Control-Allow-Origin'.freeze => ALLOW_ORIGIN,
|
|
64
|
+
'Access-Control-Expose-Headers'.freeze => EXPOSE_HEADERS_S,
|
|
65
|
+
}.freeze
|
|
66
|
+
private_constant :HEADERS
|
|
67
|
+
|
|
68
|
+
OPTIONS_HEADERS = {
|
|
69
|
+
'Access-Control-Max-Age'.freeze => MAX_AGE_S,
|
|
70
|
+
'Access-Control-Allow-Methods'.freeze => ALLOW_METHODS_S,
|
|
71
|
+
'Access-Control-Allow-Headers'.freeze => ALLOW_HEADERS_S,
|
|
72
|
+
}.merge(HEADERS).freeze
|
|
73
|
+
private_constant :OPTIONS_HEADERS
|
|
74
|
+
|
|
75
|
+
def self.headers
|
|
76
|
+
HEADERS
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.options_headers
|
|
80
|
+
OPTIONS_HEADERS
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module ThreeScale
|
|
2
|
+
module Backend
|
|
3
|
+
|
|
4
|
+
# This class uses Redis to implement a distributed lock.
|
|
5
|
+
#
|
|
6
|
+
# To implement the distributed lock, we use the Redis operation 'set nx'.
|
|
7
|
+
# The locking algorithm is detailed here: http://redis.io/topics/distlock
|
|
8
|
+
# Basically, every time that we want to use the lock, we generate a random
|
|
9
|
+
# number and set a key in Redis with that random number if its current
|
|
10
|
+
# value is null. If we could set the value, it means that we could get the
|
|
11
|
+
# lock. To release it, we just need to set to delete the same key.
|
|
12
|
+
#
|
|
13
|
+
# The implementation used in this class has some limitations.
|
|
14
|
+
# This is limited to a single Redis instance (through Twemproxy), and if
|
|
15
|
+
# the master goes off the mutual exclusion basically does not exist
|
|
16
|
+
# anymore. But there is another thing that breaks the mutual exclusion:
|
|
17
|
+
# whatever we do within the lock critical section is racing against the
|
|
18
|
+
# TTL, and we cannot guarantee that the section will be finished within the
|
|
19
|
+
# limit of the TTL. It can be the case that even if we actually locked
|
|
20
|
+
# Redis, the TTL would have expired before we got the response (think about
|
|
21
|
+
# really bad network conditions or scheduling issues in the computer that
|
|
22
|
+
# is running the critical section). So the lock acts more as an "advisory"
|
|
23
|
+
# lock than a real lock: whatever we execute inside the critical section is
|
|
24
|
+
# "probably going to be with mutual exclusion, but no guarantees".
|
|
25
|
+
#
|
|
26
|
+
# Possible ways to minimize the window of this race condition:
|
|
27
|
+
# 1) Do all the work and just lock for committing.
|
|
28
|
+
# 2) Use large values as TTLs as much as possible.
|
|
29
|
+
class DistributedLock
|
|
30
|
+
MAX_RANDOM = 1_000_000_000
|
|
31
|
+
private_constant :MAX_RANDOM
|
|
32
|
+
|
|
33
|
+
def initialize(resource, ttl, storage)
|
|
34
|
+
@resource = resource
|
|
35
|
+
@ttl = ttl
|
|
36
|
+
@storage = storage
|
|
37
|
+
@random = Random.new
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Returns key to unlock if the lock is acquired. Nil otherwise.
|
|
41
|
+
def lock
|
|
42
|
+
key = lock_key
|
|
43
|
+
storage.set(lock_storage_key, key, nx: true, ex: ttl) ? key : nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def unlock
|
|
47
|
+
storage.del(lock_storage_key)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def current_lock_key
|
|
51
|
+
storage.get(lock_storage_key)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
attr_reader :resource, :ttl, :storage, :random
|
|
57
|
+
|
|
58
|
+
def lock_key
|
|
59
|
+
random.rand(MAX_RANDOM).to_s
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def lock_storage_key
|
|
63
|
+
"#{resource.downcase}:lock".freeze
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|