prefab-cloud-ruby 0.20.0 → 0.21.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/.envrc.sample +3 -0
- data/.github/workflows/ruby.yml +4 -0
- data/.gitmodules +3 -0
- data/Gemfile +12 -12
- data/Gemfile.lock +16 -14
- data/README.md +1 -1
- data/Rakefile +13 -14
- data/VERSION +1 -1
- data/lib/prefab/auth_interceptor.rb +2 -1
- data/lib/prefab/cancellable_interceptor.rb +8 -7
- data/lib/prefab/client.rb +33 -24
- data/lib/prefab/config_client.rb +55 -66
- data/lib/prefab/config_loader.rb +7 -114
- data/lib/prefab/config_resolver.rb +27 -57
- data/lib/prefab/config_value_unwrapper.rb +23 -0
- data/lib/prefab/criteria_evaluator.rb +96 -0
- data/lib/prefab/errors/invalid_api_key_error.rb +1 -1
- data/lib/prefab/feature_flag_client.rb +13 -145
- data/lib/prefab/internal_logger.rb +6 -5
- data/lib/prefab/local_config_parser.rb +110 -0
- data/lib/prefab/logger_client.rb +26 -31
- data/lib/prefab/murmer3.rb +3 -4
- data/lib/prefab/noop_cache.rb +5 -7
- data/lib/prefab/noop_stats.rb +2 -3
- data/lib/prefab/options.rb +11 -9
- data/lib/prefab/ratelimit_client.rb +11 -13
- data/lib/prefab/sse_logger.rb +3 -2
- data/lib/prefab/weighted_value_resolver.rb +42 -0
- data/lib/prefab/yaml_config_parser.rb +32 -0
- data/lib/prefab-cloud-ruby.rb +7 -2
- data/lib/prefab_pb.rb +49 -43
- data/lib/prefab_services_pb.rb +0 -1
- data/prefab-cloud-ruby.gemspec +28 -19
- data/test/.prefab.unit_tests.config.yaml +3 -2
- data/test/integration_test.rb +98 -0
- data/test/integration_test_helpers.rb +37 -0
- data/test/test_client.rb +32 -31
- data/test/test_config_client.rb +21 -20
- data/test/test_config_loader.rb +48 -37
- data/test/test_config_resolver.rb +312 -135
- data/test/test_config_value_unwrapper.rb +83 -0
- data/test/test_criteria_evaluator.rb +533 -0
- data/test/test_feature_flag_client.rb +35 -347
- data/test/test_helper.rb +18 -14
- data/test/test_integration.rb +33 -0
- data/test/test_local_config_parser.rb +78 -0
- data/test/test_logger.rb +47 -46
- data/test/test_weighted_value_resolver.rb +65 -0
- metadata +24 -27
- data/lib/prefab/config_helper.rb +0 -31
- data/run_test_harness_server.sh +0 -8
- data/test/harness_server.rb +0 -64
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a34b06515d401b7e73fd8b6adf77294187d94ea3a1d1909d159ebfd63b4046ad
|
4
|
+
data.tar.gz: 6ba449742b1a75517b35a47151452dbdf94c0a8aef0e58a639ae5b2481262d43
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a32e0ac965c54d634c579de66927d2982cca26d7098daaf3cdfd2dda88bf04bd33265c1e9f85e303d75fc9360f6df202cee256e59a91012b94f0adcf29252aa
|
7
|
+
data.tar.gz: 2c37b2cfeef43f44d2dcbe8835ff8ea130393ad98dd9dd61328371972bf1818ab12d932f6bec319a52ec2423f9204762c6305ab3643023a3aa5ea4bfd18974bf
|
data/.envrc.sample
ADDED
data/.github/workflows/ruby.yml
CHANGED
@@ -26,6 +26,8 @@ jobs:
|
|
26
26
|
|
27
27
|
steps:
|
28
28
|
- uses: actions/checkout@v3
|
29
|
+
with:
|
30
|
+
submodules: recursive
|
29
31
|
- name: Set up Ruby
|
30
32
|
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
31
33
|
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
@@ -37,3 +39,5 @@ jobs:
|
|
37
39
|
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
38
40
|
- name: Run tests
|
39
41
|
run: bundle exec rake
|
42
|
+
env:
|
43
|
+
PREFAB_INTEGRATION_TEST_API_KEY: ${{ secrets.PREFAB_INTEGRATION_TEST_API_KEY }}
|
data/.gitmodules
ADDED
data/Gemfile
CHANGED
@@ -1,23 +1,23 @@
|
|
1
|
-
source
|
1
|
+
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
gem 'concurrent-ruby', '~> 1.0', '>= 1.0.5'
|
4
4
|
gem 'faraday'
|
5
|
+
gem 'googleapis-common-protos-types', platforms: :ruby
|
6
|
+
gem 'google-protobuf', platforms: :ruby
|
7
|
+
gem 'grpc', platforms: :ruby
|
5
8
|
gem 'ld-eventsource'
|
6
|
-
gem 'grpc', :platforms => :ruby
|
7
|
-
gem 'google-protobuf', :platforms => :ruby
|
8
|
-
gem 'googleapis-common-protos-types', :platforms => :ruby
|
9
9
|
|
10
10
|
group :development do
|
11
11
|
gem 'benchmark-ips'
|
12
|
-
gem '
|
13
|
-
gem
|
14
|
-
gem
|
15
|
-
gem
|
16
|
-
gem
|
17
|
-
gem 'thin'
|
12
|
+
gem 'bundler'
|
13
|
+
gem 'grpc-tools', platforms: :ruby
|
14
|
+
gem 'juwelier', '~> 2.4.9'
|
15
|
+
gem 'rdoc'
|
16
|
+
gem 'simplecov', '>= 0'
|
18
17
|
end
|
19
18
|
|
20
19
|
group :test do
|
21
|
-
gem
|
22
|
-
gem
|
20
|
+
gem 'minitest'
|
21
|
+
gem 'minitest-focus'
|
22
|
+
gem 'minitest-reporters'
|
23
23
|
end
|
data/Gemfile.lock
CHANGED
@@ -3,16 +3,15 @@ GEM
|
|
3
3
|
specs:
|
4
4
|
addressable (2.8.0)
|
5
5
|
public_suffix (>= 2.0.2, < 5.0)
|
6
|
+
ansi (1.5.0)
|
6
7
|
benchmark-ips (2.10.0)
|
7
8
|
builder (3.2.4)
|
8
9
|
concurrent-ruby (1.1.10)
|
9
|
-
daemons (1.4.1)
|
10
10
|
descendants_tracker (0.0.4)
|
11
11
|
thread_safe (~> 0.3, >= 0.3.1)
|
12
12
|
docile (1.3.5)
|
13
13
|
domain_name (0.5.20190701)
|
14
14
|
unf (>= 0.0.5, < 1.0.0)
|
15
|
-
eventmachine (1.2.7)
|
16
15
|
faraday (1.3.0)
|
17
16
|
faraday-net_http (~> 1.0)
|
18
17
|
multipart-post (>= 1.2, < 3)
|
@@ -22,7 +21,8 @@ GEM
|
|
22
21
|
ffi-compiler (1.0.1)
|
23
22
|
ffi (>= 1.0.0)
|
24
23
|
rake
|
25
|
-
git (1.
|
24
|
+
git (1.13.0)
|
25
|
+
addressable (~> 2.8)
|
26
26
|
rchardet (~> 1.8)
|
27
27
|
github_api (0.19.0)
|
28
28
|
addressable (~> 2.4)
|
@@ -72,35 +72,37 @@ GEM
|
|
72
72
|
minitest (5.16.2)
|
73
73
|
minitest-focus (1.3.1)
|
74
74
|
minitest (>= 4, < 6)
|
75
|
+
minitest-reporters (1.5.0)
|
76
|
+
ansi
|
77
|
+
builder
|
78
|
+
minitest (>= 5.0)
|
79
|
+
ruby-progressbar
|
75
80
|
multi_json (1.15.0)
|
76
81
|
multi_xml (0.6.0)
|
77
82
|
multipart-post (2.1.1)
|
78
|
-
nokogiri (1.13.
|
83
|
+
nokogiri (1.13.10)
|
79
84
|
mini_portile2 (~> 2.8.0)
|
80
85
|
racc (~> 1.4)
|
81
|
-
oauth2 (1.4.
|
82
|
-
faraday (>= 0.
|
86
|
+
oauth2 (1.4.11)
|
87
|
+
faraday (>= 0.17.3, < 3.0)
|
83
88
|
jwt (>= 1.0, < 3.0)
|
84
89
|
multi_json (~> 1.3)
|
85
90
|
multi_xml (~> 0.5)
|
86
|
-
rack (>= 1.2, <
|
91
|
+
rack (>= 1.2, < 4)
|
87
92
|
psych (3.3.1)
|
88
93
|
public_suffix (4.0.6)
|
89
|
-
racc (1.6.
|
90
|
-
rack (
|
94
|
+
racc (1.6.1)
|
95
|
+
rack (3.0.4.1)
|
91
96
|
rake (13.0.6)
|
92
97
|
rchardet (1.8.0)
|
93
98
|
rdoc (6.3.3)
|
99
|
+
ruby-progressbar (1.11.0)
|
94
100
|
ruby2_keywords (0.0.4)
|
95
101
|
semver2 (3.4.2)
|
96
102
|
simplecov (0.18.5)
|
97
103
|
docile (~> 1.1)
|
98
104
|
simplecov-html (~> 0.11)
|
99
105
|
simplecov-html (0.12.3)
|
100
|
-
thin (1.8.1)
|
101
|
-
daemons (~> 1.0, >= 1.0.9)
|
102
|
-
eventmachine (~> 1.0, >= 1.0.4)
|
103
|
-
rack (>= 1, < 3)
|
104
106
|
thread_safe (0.3.6)
|
105
107
|
unf (0.1.4)
|
106
108
|
unf_ext
|
@@ -122,9 +124,9 @@ DEPENDENCIES
|
|
122
124
|
ld-eventsource
|
123
125
|
minitest
|
124
126
|
minitest-focus
|
127
|
+
minitest-reporters
|
125
128
|
rdoc
|
126
129
|
simplecov
|
127
|
-
thin
|
128
130
|
|
129
131
|
BUNDLED WITH
|
130
132
|
2.3.5
|
data/README.md
CHANGED
data/Rakefile
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
# frozen_string_literal: true
|
3
2
|
|
4
3
|
require 'rubygems'
|
@@ -6,21 +5,21 @@ require 'bundler'
|
|
6
5
|
begin
|
7
6
|
Bundler.setup(:default, :development)
|
8
7
|
rescue Bundler::BundlerError => e
|
9
|
-
|
10
|
-
|
8
|
+
warn e.message
|
9
|
+
warn 'Run `bundle install` to install missing gems'
|
11
10
|
exit e.status_code
|
12
11
|
end
|
13
12
|
require 'rake'
|
14
13
|
require 'juwelier'
|
15
14
|
Juwelier::Tasks.new do |gem|
|
16
15
|
# gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
|
17
|
-
gem.name =
|
18
|
-
gem.homepage =
|
19
|
-
gem.license =
|
20
|
-
gem.summary = %
|
21
|
-
gem.description = %
|
22
|
-
gem.email =
|
23
|
-
gem.authors = [
|
16
|
+
gem.name = 'prefab-cloud-ruby'
|
17
|
+
gem.homepage = 'http://github.com/prefab-cloud/prefab-cloud-ruby'
|
18
|
+
gem.license = 'MIT'
|
19
|
+
gem.summary = %(Prefab Ruby Infrastructure)
|
20
|
+
gem.description = %(RateLimits & Config as a service)
|
21
|
+
gem.email = 'jdwyer@prefab.cloud'
|
22
|
+
gem.authors = ['Jeff Dwyer']
|
24
23
|
|
25
24
|
# dependencies defined in Gemfile
|
26
25
|
end
|
@@ -32,17 +31,17 @@ Rake::TestTask.new(:test) do |test|
|
|
32
31
|
test.verbose = true
|
33
32
|
end
|
34
33
|
|
35
|
-
desc
|
34
|
+
desc 'Code coverage detail'
|
36
35
|
task :simplecov do
|
37
|
-
ENV['COVERAGE'] =
|
36
|
+
ENV['COVERAGE'] = 'true'
|
38
37
|
Rake::Task['test'].execute
|
39
38
|
end
|
40
39
|
|
41
|
-
task :
|
40
|
+
task default: :test
|
42
41
|
|
43
42
|
require 'rdoc/task'
|
44
43
|
Rake::RDocTask.new do |rdoc|
|
45
|
-
version = File.exist?('VERSION') ? File.read('VERSION') :
|
44
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ''
|
46
45
|
|
47
46
|
rdoc.rdoc_dir = 'rdoc'
|
48
47
|
rdoc.title = "prefab-cloud-ruby #{version}"
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.21.0
|
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Prefab
|
3
4
|
class AuthInterceptor < GRPC::ClientInterceptor
|
4
|
-
VERSION = File.exist?('VERSION') ? File.read('VERSION').chomp :
|
5
|
+
VERSION = File.exist?('VERSION') ? File.read('VERSION').chomp : ''
|
5
6
|
CLIENT = "prefab-cloud-ruby.#{VERSION}".freeze
|
6
7
|
|
7
8
|
def initialize(api_key)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Prefab
|
3
4
|
class CancellableInterceptor < GRPC::ClientInterceptor
|
4
5
|
WAIT_SEC = 3
|
@@ -8,20 +9,20 @@ module Prefab
|
|
8
9
|
end
|
9
10
|
|
10
11
|
def cancel
|
11
|
-
@call.instance_variable_get(
|
12
|
+
@call.instance_variable_get('@wrapped').instance_variable_get('@call').cancel
|
12
13
|
i = 0
|
13
|
-
while
|
14
|
-
if @call.instance_variable_get(
|
15
|
-
@base_client.log_internal Logger::DEBUG,
|
14
|
+
while i < WAIT_SEC
|
15
|
+
if @call.instance_variable_get('@wrapped').cancelled?
|
16
|
+
@base_client.log_internal Logger::DEBUG, 'Cancelled streaming.'
|
16
17
|
return
|
17
18
|
else
|
18
|
-
@base_client.log_internal Logger::DEBUG,
|
19
|
-
@call.instance_variable_get(
|
19
|
+
@base_client.log_internal Logger::DEBUG, 'Unable to cancel streaming. Trying again'
|
20
|
+
@call.instance_variable_get('@wrapped').instance_variable_get('@call').cancel
|
20
21
|
i += 1
|
21
22
|
sleep(1)
|
22
23
|
end
|
23
24
|
end
|
24
|
-
@base_client.log_internal Logger::INFO,
|
25
|
+
@base_client.log_internal Logger::INFO, 'Unable to cancel streaming.'
|
25
26
|
end
|
26
27
|
|
27
28
|
def request_response(request:, call:, method:, metadata:, &block)
|
data/lib/prefab/client.rb
CHANGED
@@ -1,11 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Prefab
|
3
4
|
class Client
|
4
5
|
MAX_SLEEP_SEC = 10
|
5
6
|
BASE_SLEEP_SEC = 0.5
|
6
7
|
NO_DEFAULT_PROVIDED = :no_default_provided
|
7
8
|
|
8
|
-
attr_reader :shared_cache
|
9
|
+
attr_reader :shared_cache
|
10
|
+
attr_reader :stats
|
11
|
+
attr_reader :namespace
|
12
|
+
attr_reader :interceptor
|
13
|
+
attr_reader :api_key
|
14
|
+
attr_reader :prefab_api_url
|
15
|
+
attr_reader :options
|
9
16
|
|
10
17
|
def initialize(options = Prefab::Options.new)
|
11
18
|
@options = options
|
@@ -15,14 +22,16 @@ module Prefab
|
|
15
22
|
@stubs = {}
|
16
23
|
|
17
24
|
if @options.local_only?
|
18
|
-
log_internal Logger::INFO,
|
25
|
+
log_internal Logger::INFO, 'Prefab Running in Local Mode'
|
19
26
|
else
|
20
27
|
@api_key = @options.api_key
|
21
|
-
raise Prefab::Errors::InvalidApiKeyError
|
28
|
+
raise Prefab::Errors::InvalidApiKeyError, @api_key if @api_key.nil? || @api_key.empty? || api_key.count('-') < 1
|
29
|
+
|
22
30
|
@interceptor = Prefab::AuthInterceptor.new(@api_key)
|
23
31
|
@prefab_api_url = @options.prefab_api_url
|
24
32
|
@prefab_grpc_url = @options.prefab_grpc_url
|
25
|
-
log_internal Logger::INFO,
|
33
|
+
log_internal Logger::INFO,
|
34
|
+
"Prefab Connecting to: #{@prefab_api_url} and #{@prefab_grpc_url} Secure: #{http_secure?}"
|
26
35
|
at_exit do
|
27
36
|
channel.destroy
|
28
37
|
end
|
@@ -63,16 +72,14 @@ module Prefab
|
|
63
72
|
|
64
73
|
begin
|
65
74
|
attempts += 1
|
66
|
-
|
67
|
-
rescue =>
|
75
|
+
stub_for(service, opts[:timeout]).send(method, *params)
|
76
|
+
rescue StandardError => e
|
77
|
+
log_internal Logger::WARN, e
|
68
78
|
|
69
|
-
|
79
|
+
raise e if Time.now - start_time > opts[:timeout]
|
70
80
|
|
71
|
-
|
72
|
-
|
73
|
-
end
|
74
|
-
sleep_seconds = [BASE_SLEEP_SEC * (2 ** (attempts - 1)), MAX_SLEEP_SEC].min
|
75
|
-
sleep_seconds = sleep_seconds * (0.5 * (1 + rand()))
|
81
|
+
sleep_seconds = [BASE_SLEEP_SEC * (2**(attempts - 1)), MAX_SLEEP_SEC].min
|
82
|
+
sleep_seconds *= (0.5 * (1 + rand))
|
76
83
|
sleep_seconds = [BASE_SLEEP_SEC, sleep_seconds].max
|
77
84
|
log_internal Logger::INFO, "Sleep #{sleep_seconds} and Reset #{service} #{method}"
|
78
85
|
sleep sleep_seconds
|
@@ -86,24 +93,28 @@ module Prefab
|
|
86
93
|
@_channel = nil
|
87
94
|
end
|
88
95
|
|
89
|
-
def enabled?(feature_name, lookup_key=nil, attributes={})
|
96
|
+
def enabled?(feature_name, lookup_key = nil, attributes = {})
|
90
97
|
feature_flag_client.feature_is_on_for?(feature_name, lookup_key, attributes: attributes)
|
91
98
|
end
|
92
99
|
|
93
|
-
def get(key, default_or_lookup_key=NO_DEFAULT_PROVIDED,
|
94
|
-
|
95
|
-
|
96
|
-
if result.is_a?(Prefab::FeatureFlag)
|
97
|
-
feature_flag_client.get(key, default_or_lookup_key, attributes, default: ff_default)
|
100
|
+
def get(key, default_or_lookup_key = NO_DEFAULT_PROVIDED, properties = {}, ff_default = nil)
|
101
|
+
if is_ff?(key)
|
102
|
+
feature_flag_client.get(key, default_or_lookup_key, properties, default: ff_default)
|
98
103
|
else
|
99
|
-
|
104
|
+
config_client.get(key, default_or_lookup_key, properties)
|
100
105
|
end
|
101
106
|
end
|
102
107
|
|
103
108
|
private
|
104
109
|
|
110
|
+
def is_ff?(key)
|
111
|
+
raw = config_client.send(:raw, key)
|
112
|
+
|
113
|
+
raw && raw.allowable_values.any?
|
114
|
+
end
|
115
|
+
|
105
116
|
def http_secure?
|
106
|
-
ENV[
|
117
|
+
ENV['PREFAB_CLOUD_HTTP'] != 'true'
|
107
118
|
end
|
108
119
|
|
109
120
|
def stub_for(service, timeout)
|
@@ -119,7 +130,7 @@ module Prefab
|
|
119
130
|
end
|
120
131
|
|
121
132
|
def ssl_certs
|
122
|
-
ssl_certs =
|
133
|
+
ssl_certs = ''
|
123
134
|
Dir["#{OpenSSL::X509::DEFAULT_CERT_DIR}/*.pem"].each do |cert|
|
124
135
|
ssl_certs += File.open(cert).read
|
125
136
|
end
|
@@ -127,11 +138,9 @@ module Prefab
|
|
127
138
|
ssl_certs += File.open(OpenSSL::X509::DEFAULT_CERT_FILE).read
|
128
139
|
end
|
129
140
|
ssl_certs
|
130
|
-
rescue => e
|
141
|
+
rescue StandardError => e
|
131
142
|
log.warn("Issue loading SSL certs #{e.message}")
|
132
143
|
ssl_certs
|
133
144
|
end
|
134
|
-
|
135
145
|
end
|
136
146
|
end
|
137
|
-
|
data/lib/prefab/config_client.rb
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Prefab
|
3
4
|
class ConfigClient
|
4
|
-
include Prefab::ConfigHelper
|
5
|
-
|
6
5
|
RECONNECT_WAIT = 5
|
7
6
|
DEFAULT_CHECKPOINT_FREQ_SEC = 60
|
8
7
|
SSE_READ_TIMEOUT = 300
|
9
|
-
AUTH_USER =
|
8
|
+
AUTH_USER = 'authuser'
|
10
9
|
|
11
10
|
def initialize(base_client, timeout)
|
12
11
|
@base_client = base_client
|
13
12
|
@options = base_client.options
|
14
|
-
@base_client.log_internal Logger::DEBUG,
|
13
|
+
@base_client.log_internal Logger::DEBUG, 'Initialize ConfigClient'
|
15
14
|
@timeout = timeout
|
16
15
|
|
17
16
|
@stream_lock = Concurrent::ReadWriteLock.new
|
@@ -22,9 +21,9 @@ module Prefab
|
|
22
21
|
@config_resolver = Prefab::ConfigResolver.new(@base_client, @config_loader)
|
23
22
|
|
24
23
|
@initialization_lock = Concurrent::ReadWriteLock.new
|
25
|
-
@base_client.log_internal Logger::DEBUG,
|
24
|
+
@base_client.log_internal Logger::DEBUG, 'Initialize ConfigClient: AcquireWriteLock'
|
26
25
|
@initialization_lock.acquire_write_lock
|
27
|
-
@base_client.log_internal Logger::DEBUG,
|
26
|
+
@base_client.log_internal Logger::DEBUG, 'Initialize ConfigClient: AcquiredWriteLock'
|
28
27
|
@initialized_future = Concurrent::Future.execute { @initialization_lock.acquire_read_lock }
|
29
28
|
|
30
29
|
@cancellable_interceptor = Prefab::CancellableInterceptor.new(@base_client)
|
@@ -45,14 +44,15 @@ module Prefab
|
|
45
44
|
end
|
46
45
|
|
47
46
|
def upsert(key, config_value, namespace = nil, previous_key = nil)
|
48
|
-
raise "Key must not contain ':' set namespaces separately" if key.include?
|
49
|
-
raise "Namespace must not contain ':'" if namespace&.include?(
|
47
|
+
raise "Key must not contain ':' set namespaces separately" if key.include? ':'
|
48
|
+
raise "Namespace must not contain ':'" if namespace&.include?(':')
|
49
|
+
|
50
50
|
config_delta = Prefab::ConfigClient.value_to_delta(key, config_value, namespace)
|
51
51
|
upsert_req = Prefab::UpsertRequest.new(config_delta: config_delta)
|
52
52
|
upsert_req.previous_key = previous_key if previous_key&.present?
|
53
53
|
|
54
54
|
@base_client.request Prefab::ConfigService, :upsert, req_options: { timeout: @timeout }, params: upsert_req
|
55
|
-
@base_client.stats.increment(
|
55
|
+
@base_client.stats.increment('prefab.config.upsert')
|
56
56
|
@config_loader.set(config_delta, :upsert)
|
57
57
|
@config_loader.rm(previous_key) if previous_key&.present?
|
58
58
|
@config_resolver.update
|
@@ -68,46 +68,43 @@ module Prefab
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def self.value_to_delta(key, config_value, namespace = nil)
|
71
|
-
Prefab::Config.new(key: [namespace, key].compact.join(
|
71
|
+
Prefab::Config.new(key: [namespace, key].compact.join(':'),
|
72
72
|
rows: [Prefab::ConfigRow.new(value: config_value)])
|
73
73
|
end
|
74
74
|
|
75
|
-
def get(key, default=Prefab::Client::NO_DEFAULT_PROVIDED)
|
76
|
-
|
77
|
-
|
78
|
-
end
|
79
|
-
|
80
|
-
def get_config_obj(key)
|
81
|
-
config = _get(key)
|
82
|
-
config ? config[:config] : nil
|
75
|
+
def get(key, default = Prefab::Client::NO_DEFAULT_PROVIDED, properties = {}, lookup_key = nil)
|
76
|
+
value = _get(key, lookup_key, properties)
|
77
|
+
value ? Prefab::ConfigValueUnwrapper.unwrap(value, key, properties) : handle_default(key, default)
|
83
78
|
end
|
84
79
|
|
85
80
|
private
|
86
81
|
|
82
|
+
def raw(key)
|
83
|
+
@config_resolver.raw(key)
|
84
|
+
end
|
85
|
+
|
87
86
|
def handle_default(key, default)
|
88
|
-
if default != Prefab::Client::NO_DEFAULT_PROVIDED
|
89
|
-
return default
|
90
|
-
end
|
87
|
+
return default if default != Prefab::Client::NO_DEFAULT_PROVIDED
|
91
88
|
|
92
|
-
if @options.on_no_default == Prefab::Options::ON_NO_DEFAULT::RAISE
|
93
|
-
raise Prefab::Errors::MissingDefaultError.new(key)
|
94
|
-
end
|
89
|
+
raise Prefab::Errors::MissingDefaultError, key if @options.on_no_default == Prefab::Options::ON_NO_DEFAULT::RAISE
|
95
90
|
|
96
91
|
nil
|
97
92
|
end
|
98
93
|
|
99
|
-
def _get(key)
|
94
|
+
def _get(key, lookup_key, properties)
|
100
95
|
# wait timeout sec for the initalization to be complete
|
101
96
|
@initialized_future.value(@options.initialization_timeout_sec)
|
102
97
|
if @initialized_future.incomplete?
|
103
|
-
|
104
|
-
@base_client.log_internal Logger::WARN, "Couldn't Initialize In #{@options.initialization_timeout_sec}. Key #{key}. Returning what we have"
|
105
|
-
@initialization_lock.release_write_lock
|
106
|
-
else
|
98
|
+
unless @options.on_init_failure == Prefab::Options::ON_INITIALIZATION_FAILURE::RETURN
|
107
99
|
raise Prefab::Errors::InitializationTimeoutError.new(@options.initialization_timeout_sec, key)
|
108
100
|
end
|
101
|
+
|
102
|
+
@base_client.log_internal Logger::WARN,
|
103
|
+
"Couldn't Initialize In #{@options.initialization_timeout_sec}. Key #{key}. Returning what we have"
|
104
|
+
@initialization_lock.release_write_lock
|
105
|
+
|
109
106
|
end
|
110
|
-
@config_resolver.
|
107
|
+
@config_resolver.get(key, lookup_key, properties)
|
111
108
|
end
|
112
109
|
|
113
110
|
def stub
|
@@ -120,19 +117,15 @@ module Prefab
|
|
120
117
|
def load_checkpoint
|
121
118
|
success = load_checkpoint_api_cdn
|
122
119
|
|
123
|
-
if success
|
124
|
-
|
125
|
-
|
126
|
-
@base_client.log_internal Logger::INFO, "LoadCheckpoint: Fallback to GRPC API"
|
127
|
-
end
|
120
|
+
return if success
|
121
|
+
|
122
|
+
@base_client.log_internal Logger::INFO, 'LoadCheckpoint: Fallback to GRPC API'
|
128
123
|
|
129
124
|
success = load_checkpoint_from_grpc_api
|
130
125
|
|
131
|
-
if success
|
132
|
-
|
133
|
-
|
134
|
-
@base_client.log_internal Logger::WARN, "No success loading checkpoints"
|
135
|
-
end
|
126
|
+
return if success
|
127
|
+
|
128
|
+
@base_client.log_internal Logger::WARN, 'No success loading checkpoints'
|
136
129
|
end
|
137
130
|
|
138
131
|
def load_checkpoint_from_grpc_api
|
@@ -142,8 +135,8 @@ module Prefab
|
|
142
135
|
load_configs(resp, :remote_api_grpc)
|
143
136
|
true
|
144
137
|
rescue GRPC::Unauthenticated
|
145
|
-
@base_client.log_internal Logger::WARN,
|
146
|
-
rescue => e
|
138
|
+
@base_client.log_internal Logger::WARN, 'Unauthenticated'
|
139
|
+
rescue StandardError => e
|
147
140
|
@base_client.log_internal Logger::WARN, "Unexpected grpc_api problem loading checkpoint #{e}"
|
148
141
|
false
|
149
142
|
end
|
@@ -172,7 +165,7 @@ module Prefab
|
|
172
165
|
@base_client.log_internal Logger::INFO, "Checkpoint #{source} failed to load. Response #{resp.status}"
|
173
166
|
false
|
174
167
|
end
|
175
|
-
rescue => e
|
168
|
+
rescue StandardError => e
|
176
169
|
@base_client.log_internal Logger::WARN, "Unexpected #{source} problem loading checkpoint #{e} #{conn}"
|
177
170
|
false
|
178
171
|
end
|
@@ -187,42 +180,39 @@ module Prefab
|
|
187
180
|
@config_loader.set(config, source)
|
188
181
|
end
|
189
182
|
if @config_loader.highwater_mark > starting_highwater_mark
|
190
|
-
@base_client.log_internal Logger::INFO,
|
183
|
+
@base_client.log_internal Logger::INFO,
|
184
|
+
"Found new checkpoint with highwater id #{@config_loader.highwater_mark} from #{source} in project #{project_id} environment: #{project_env_id} and namespace: '#{@namespace}'"
|
191
185
|
else
|
192
|
-
@base_client.log_internal Logger::DEBUG,
|
186
|
+
@base_client.log_internal Logger::DEBUG,
|
187
|
+
"Checkpoint with highwater id #{@config_loader.highwater_mark} from #{source}. No changes.", 'load_configs'
|
193
188
|
end
|
194
|
-
@base_client.stats.increment(
|
189
|
+
@base_client.stats.increment('prefab.config.checkpoint.load')
|
195
190
|
@config_resolver.update
|
196
191
|
finish_init!(source)
|
197
192
|
end
|
198
193
|
|
199
194
|
# A thread that checks for a checkpoint
|
200
195
|
def start_checkpointing_thread
|
201
|
-
|
202
196
|
Thread.new do
|
203
197
|
loop do
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
end
|
212
|
-
rescue StandardError => exn
|
213
|
-
@base_client.log_internal Logger::INFO, "Issue Checkpointing #{exn.message}"
|
214
|
-
end
|
198
|
+
load_checkpoint
|
199
|
+
|
200
|
+
started_at = Time.now
|
201
|
+
delta = @checkpoint_freq_secs - (Time.now - started_at)
|
202
|
+
sleep(delta) if delta > 0
|
203
|
+
rescue StandardError => e
|
204
|
+
@base_client.log_internal Logger::INFO, "Issue Checkpointing #{e.message}"
|
215
205
|
end
|
216
206
|
end
|
217
207
|
end
|
218
208
|
|
219
209
|
def finish_init!(source)
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
210
|
+
return unless @initialization_lock.write_locked?
|
211
|
+
|
212
|
+
@base_client.log_internal Logger::INFO, "Unlocked Config via #{source}"
|
213
|
+
@initialization_lock.release_write_lock
|
214
|
+
@base_client.log.set_config_client(self)
|
215
|
+
@base_client.log_internal Logger::INFO, to_s
|
226
216
|
end
|
227
217
|
|
228
218
|
def start_sse_streaming_connection_thread(start_at_id)
|
@@ -230,7 +220,7 @@ module Prefab
|
|
230
220
|
auth_string = Base64.strict_encode64(auth)
|
231
221
|
headers = {
|
232
222
|
"x-prefab-start-at-id": start_at_id,
|
233
|
-
"Authorization": "Basic #{auth_string}"
|
223
|
+
"Authorization": "Basic #{auth_string}"
|
234
224
|
}
|
235
225
|
url = "#{@base_client.prefab_api_url}/api/v1/sse/config"
|
236
226
|
@base_client.log_internal Logger::INFO, "SSE Streaming Connect to #{url} start_at #{start_at_id}"
|
@@ -246,4 +236,3 @@ module Prefab
|
|
246
236
|
end
|
247
237
|
end
|
248
238
|
end
|
249
|
-
|