prefab-cloud-ruby 0.11.0 → 0.12.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/Gemfile.lock +5 -5
- data/README.md +9 -0
- data/VERSION +1 -1
- data/lib/prefab/client.rb +29 -37
- data/lib/prefab/config_client.rb +107 -85
- data/lib/prefab/config_helper.rb +8 -0
- data/lib/prefab/config_loader.rb +30 -9
- data/lib/prefab/config_resolver.rb +22 -18
- data/lib/prefab/feature_flag_client.rb +23 -26
- data/lib/prefab/internal_logger.rb +28 -0
- data/lib/prefab/logger_client.rb +1 -1
- data/lib/prefab/options.rb +81 -0
- data/lib/prefab/prefab.rb +32 -0
- data/lib/prefab-cloud-ruby.rb +2 -0
- data/lib/prefab_pb.rb +21 -17
- data/lib/prefab_services_pb.rb +14 -0
- data/prefab-cloud-ruby.gemspec +7 -3
- data/test/.prefab.test.config.yaml +5 -1
- data/test/harness_server.rb +2 -1
- data/test/test_config_client.rb +19 -0
- data/test/test_config_loader.rb +32 -18
- data/test/test_config_resolver.rb +34 -28
- data/test/test_feature_flag_client.rb +37 -44
- data/test/test_helper.rb +4 -4
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2525930a7f8c2e2924db43504b7dfb6d32e60647bbc359b8a85af8ae3c9f2db0
|
4
|
+
data.tar.gz: 583f5dc901866eaf206f7f693a95bec47b7ea04c54f3d56275a04bbf0a7bb75f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6c4ff539c75a2d2cfc5e5d6c3ac7bbab494489386d9a1c66ec97af47ab11d46e723a86df862ab8f86a06b09e6ce93d93425f991656cc473395ccf7e2e5fa4da5
|
7
|
+
data.tar.gz: 1fe296a9c44484d2d8cc4a185929aaef80358fdee8f3bd83d47152933aa6f684fcf995c3f18bae6feb0389370620e9557d2d37f00d657cc72ef43fdd3b55e37e
|
data/Gemfile.lock
CHANGED
@@ -6,7 +6,7 @@ GEM
|
|
6
6
|
i18n (>= 0.7, < 2)
|
7
7
|
minitest (~> 5.1)
|
8
8
|
tzinfo (~> 1.1)
|
9
|
-
addressable (2.
|
9
|
+
addressable (2.8.0)
|
10
10
|
public_suffix (>= 2.0.2, < 5.0)
|
11
11
|
builder (3.2.4)
|
12
12
|
concurrent-ruby (1.1.8)
|
@@ -26,7 +26,7 @@ GEM
|
|
26
26
|
ffi-compiler (1.0.1)
|
27
27
|
ffi (>= 1.0.0)
|
28
28
|
rake
|
29
|
-
git (1.
|
29
|
+
git (1.11.0)
|
30
30
|
rchardet (~> 1.8)
|
31
31
|
github_api (0.19.0)
|
32
32
|
addressable (~> 2.4)
|
@@ -75,13 +75,13 @@ GEM
|
|
75
75
|
llhttp-ffi (0.3.1)
|
76
76
|
ffi-compiler (~> 1.0)
|
77
77
|
rake (~> 13.0)
|
78
|
-
mini_portile2 (2.
|
78
|
+
mini_portile2 (2.8.0)
|
79
79
|
minitest (5.14.4)
|
80
80
|
multi_json (1.15.0)
|
81
81
|
multi_xml (0.6.0)
|
82
82
|
multipart-post (2.1.1)
|
83
|
-
nokogiri (1.13.
|
84
|
-
mini_portile2 (~> 2.
|
83
|
+
nokogiri (1.13.6)
|
84
|
+
mini_portile2 (~> 2.8.0)
|
85
85
|
racc (~> 1.4)
|
86
86
|
oauth2 (1.4.7)
|
87
87
|
faraday (>= 0.8, < 2.0)
|
data/README.md
CHANGED
@@ -43,6 +43,15 @@ on_worker_boot do
|
|
43
43
|
end
|
44
44
|
```
|
45
45
|
|
46
|
+
## Logging & Debugging
|
47
|
+
In classpath or ~/.prefab.overrides.config.yaml set
|
48
|
+
```log_level.prefab: debug```
|
49
|
+
|
50
|
+
To debug issues before this config file has been read, set env var
|
51
|
+
```
|
52
|
+
PREFAB_LOG_CLIENT_BOOTSTRAP_LOG_LEVEL = debug
|
53
|
+
```
|
54
|
+
|
46
55
|
## Contributing to prefab-cloud-ruby
|
47
56
|
|
48
57
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.12.0
|
data/lib/prefab/client.rb
CHANGED
@@ -1,46 +1,38 @@
|
|
1
1
|
module Prefab
|
2
|
-
class Client
|
3
2
|
|
3
|
+
class Client
|
4
4
|
MAX_SLEEP_SEC = 10
|
5
5
|
BASE_SLEEP_SEC = 0.5
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
logdev: nil,
|
15
|
-
stats: nil, # receives increment("prefab.limitcheck", {:tags=>["policy_group:page_view", "pass:true"]})
|
16
|
-
shared_cache: nil, # Something that quacks like Rails.cache ideally memcached
|
17
|
-
local: false,
|
18
|
-
namespace: "",
|
19
|
-
log_formatter: DEFAULT_LOG_FORMATTER
|
20
|
-
)
|
21
|
-
raise "No API key. Set PREFAB_API_KEY env var" if api_key.nil? || api_key.empty?
|
22
|
-
raise "PREFAB_API_KEY format invalid. Expecting 123-development-yourapikey-SDK" unless api_key.count("-") == 3
|
23
|
-
@logdev = (logdev || $stdout)
|
24
|
-
@log_formatter = log_formatter
|
25
|
-
@local = local
|
26
|
-
@stats = (stats || NoopStats.new)
|
27
|
-
@shared_cache = (shared_cache || NoopCache.new)
|
28
|
-
@api_key = api_key
|
29
|
-
@project_id = api_key.split("-")[0].to_i # unvalidated, but that's ok. APIs only listen to the actual passwd
|
30
|
-
@namespace = namespace
|
31
|
-
@interceptor = Prefab::AuthInterceptor.new(api_key)
|
6
|
+
|
7
|
+
attr_reader :project_id, :shared_cache, :stats, :namespace, :interceptor, :api_key, :prefab_api_url, :options
|
8
|
+
|
9
|
+
def initialize(options = Prefab::Options.new)
|
10
|
+
@options = options
|
11
|
+
@shared_cache = @options.shared_cache
|
12
|
+
@stats = @options.stats
|
13
|
+
@namespace = @options.namespace
|
32
14
|
@stubs = {}
|
33
|
-
|
34
|
-
@
|
35
|
-
|
36
|
-
|
37
|
-
|
15
|
+
|
16
|
+
if @options.local_only?
|
17
|
+
@project_id = 0
|
18
|
+
log_internal Logger::INFO, "Prefab Running in Local Mode"
|
19
|
+
else
|
20
|
+
@api_key = @options.api_key
|
21
|
+
raise "No API key. Set PREFAB_API_KEY env var or use PREFAB_DATASOURCES=LOCAL_ONLY" if @api_key.nil? || @api_key.empty?
|
22
|
+
raise "PREFAB_API_KEY format invalid. Expecting 123-development-yourapikey-SDK" unless @api_key.count("-") == 3
|
23
|
+
@project_id = @api_key.split("-")[0].to_i # unvalidated, but that's ok. APIs only listen to the actual passwd
|
24
|
+
@interceptor = Prefab::AuthInterceptor.new(@api_key)
|
25
|
+
@prefab_api_url = @options.prefab_api_url
|
26
|
+
@prefab_grpc_url = @options.prefab_grpc_url
|
27
|
+
log_internal Logger::INFO, "Prefab Connecting to: #{@prefab_api_url} and #{@prefab_grpc_url} Secure: #{http_secure?}"
|
28
|
+
at_exit do
|
29
|
+
channel.destroy
|
30
|
+
end
|
38
31
|
end
|
39
32
|
end
|
40
33
|
|
41
34
|
def channel
|
42
35
|
credentials = http_secure? ? creds : :this_channel_is_insecure
|
43
|
-
log_internal Logger::DEBUG, "GRPC Channel #{@prefab_grpc_url} #{credentials}"
|
44
36
|
@_channel ||= GRPC::Core::Channel.new(@prefab_grpc_url, nil, credentials)
|
45
37
|
end
|
46
38
|
|
@@ -57,11 +49,11 @@ module Prefab
|
|
57
49
|
end
|
58
50
|
|
59
51
|
def log
|
60
|
-
@logger_client ||= Prefab::LoggerClient.new(@logdev, formatter: @log_formatter)
|
52
|
+
@logger_client ||= Prefab::LoggerClient.new(@options.logdev, formatter: @options.log_formatter)
|
61
53
|
end
|
62
54
|
|
63
|
-
def log_internal(level, msg)
|
64
|
-
log.log_internal msg,
|
55
|
+
def log_internal(level, msg, path = "prefab")
|
56
|
+
log.log_internal msg, path, nil, level
|
65
57
|
end
|
66
58
|
|
67
59
|
def request(service, method, req_options: {}, params: {})
|
@@ -122,7 +114,7 @@ module Prefab
|
|
122
114
|
Dir["#{OpenSSL::X509::DEFAULT_CERT_DIR}/*.pem"].each do |cert|
|
123
115
|
ssl_certs << File.open(cert).read
|
124
116
|
end
|
125
|
-
if OpenSSL::X509::DEFAULT_CERT_FILE && File.
|
117
|
+
if OpenSSL::X509::DEFAULT_CERT_FILE && File.exist?(OpenSSL::X509::DEFAULT_CERT_FILE)
|
126
118
|
ssl_certs << File.open(OpenSSL::X509::DEFAULT_CERT_FILE).read
|
127
119
|
end
|
128
120
|
ssl_certs
|
data/lib/prefab/config_client.rb
CHANGED
@@ -1,41 +1,47 @@
|
|
1
1
|
module Prefab
|
2
2
|
class ConfigClient
|
3
|
+
include Prefab::ConfigHelper
|
4
|
+
|
3
5
|
RECONNECT_WAIT = 5
|
4
6
|
DEFAULT_CHECKPOINT_FREQ_SEC = 60
|
5
7
|
DEFAULT_S3CF_BUCKET = 'http://d2j4ed6ti5snnd.cloudfront.net'
|
8
|
+
SSE_READ_TIMEOUT = 300
|
6
9
|
|
7
10
|
def initialize(base_client, timeout)
|
8
11
|
@base_client = base_client
|
12
|
+
@options = base_client.options
|
9
13
|
@base_client.log_internal Logger::DEBUG, "Initialize ConfigClient"
|
10
14
|
@timeout = timeout
|
11
|
-
|
15
|
+
|
16
|
+
@stream_lock = Concurrent::ReadWriteLock.new
|
12
17
|
|
13
18
|
@checkpoint_freq_secs = DEFAULT_CHECKPOINT_FREQ_SEC
|
14
19
|
|
15
20
|
@config_loader = Prefab::ConfigLoader.new(@base_client)
|
16
21
|
@config_resolver = Prefab::ConfigResolver.new(@base_client, @config_loader)
|
17
22
|
|
23
|
+
@initialization_lock = Concurrent::ReadWriteLock.new
|
18
24
|
@base_client.log_internal Logger::DEBUG, "Initialize ConfigClient: AcquireWriteLock"
|
19
25
|
@initialization_lock.acquire_write_lock
|
20
26
|
@base_client.log_internal Logger::DEBUG, "Initialize ConfigClient: AcquiredWriteLock"
|
27
|
+
@initialized_future = Concurrent::Future.execute { @initialization_lock.acquire_read_lock }
|
21
28
|
|
22
29
|
@cancellable_interceptor = Prefab::CancellableInterceptor.new(@base_client)
|
23
30
|
|
24
31
|
@s3_cloud_front = ENV["PREFAB_S3CF_BUCKET"] || DEFAULT_S3CF_BUCKET
|
25
32
|
|
26
|
-
|
27
|
-
|
33
|
+
if @options.local_only?
|
34
|
+
finish_init!(:local_only)
|
35
|
+
else
|
36
|
+
load_checkpoint
|
37
|
+
start_checkpointing_thread
|
38
|
+
start_streaming
|
39
|
+
end
|
28
40
|
end
|
29
41
|
|
30
42
|
def start_streaming
|
31
|
-
@
|
32
|
-
|
33
|
-
start_sse_streaming_connection_thread(@config_loader.highwater_mark)
|
34
|
-
end
|
35
|
-
|
36
|
-
def get(key)
|
37
|
-
@initialization_lock.with_read_lock do
|
38
|
-
@config_resolver.get(key)
|
43
|
+
@stream_lock.with_write_lock do
|
44
|
+
start_sse_streaming_connection_thread(@config_loader.highwater_mark) if @streaming_thread.nil?
|
39
45
|
end
|
40
46
|
end
|
41
47
|
|
@@ -48,7 +54,7 @@ module Prefab
|
|
48
54
|
|
49
55
|
@base_client.request Prefab::ConfigService, :upsert, req_options: { timeout: @timeout }, params: upsert_req
|
50
56
|
@base_client.stats.increment("prefab.config.upsert")
|
51
|
-
@config_loader.set(config_delta)
|
57
|
+
@config_loader.set(config_delta, :upsert)
|
52
58
|
@config_loader.rm(previous_key) if previous_key&.present?
|
53
59
|
@config_resolver.update
|
54
60
|
end
|
@@ -67,14 +73,32 @@ module Prefab
|
|
67
73
|
rows: [Prefab::ConfigRow.new(value: config_value)])
|
68
74
|
end
|
69
75
|
|
76
|
+
def get(key)
|
77
|
+
config = _get(key)
|
78
|
+
config ? value_of(config[:value]) : nil
|
79
|
+
end
|
80
|
+
|
70
81
|
def get_config_obj(key)
|
71
|
-
|
72
|
-
|
73
|
-
end
|
82
|
+
config = _get(key)
|
83
|
+
config ? config[:config] : nil
|
74
84
|
end
|
75
85
|
|
76
86
|
private
|
77
87
|
|
88
|
+
def _get(key)
|
89
|
+
# wait timeout sec for the initalization to be complete
|
90
|
+
@initialized_future.value(@options.initialization_timeout_sec)
|
91
|
+
if @initialized_future.incomplete?
|
92
|
+
if @options.on_init_failure == Prefab::Options::ON_INITIALIZATION_FAILURE::RETURN
|
93
|
+
@base_client.log_internal Logger::WARN, "Couldn't Initialize In #{@options.initialization_timeout_sec}. Key #{key}. Returning what we have"
|
94
|
+
@initialization_lock.release_write_lock
|
95
|
+
else
|
96
|
+
raise "Prefab Couldn't Initialize In #{@options.initialization_timeout_sec} 2 timeout. Key #{key}. "
|
97
|
+
end
|
98
|
+
end
|
99
|
+
@config_resolver._get(key)
|
100
|
+
end
|
101
|
+
|
78
102
|
def stub
|
79
103
|
@_stub = Prefab::ConfigService::Stub.new(nil,
|
80
104
|
nil,
|
@@ -82,56 +106,93 @@ module Prefab
|
|
82
106
|
interceptors: [@base_client.interceptor, @cancellable_interceptor])
|
83
107
|
end
|
84
108
|
|
85
|
-
#
|
86
|
-
# returns the high-watermark of what was in the cache
|
109
|
+
# try API first, if not, fallback to s3
|
87
110
|
def load_checkpoint
|
88
|
-
success =
|
111
|
+
success = load_checkpoint_api_cdn
|
89
112
|
|
90
|
-
if
|
91
|
-
|
92
|
-
|
113
|
+
if success
|
114
|
+
return
|
115
|
+
else
|
116
|
+
@base_client.log_internal Logger::INFO, "LoadCheckpoint: Fallback to GRPC API"
|
93
117
|
end
|
94
118
|
|
95
|
-
|
96
|
-
@base_client.log_internal Logger::WARN, "Unexpected problem loading checkpoint #{e}"
|
97
|
-
end
|
119
|
+
success = load_checkpoint_from_grpc_api
|
98
120
|
|
99
|
-
|
100
|
-
|
121
|
+
if success
|
122
|
+
return
|
123
|
+
else
|
124
|
+
@base_client.log_internal Logger::INFO, "LoadCheckpoint: Fallback to S3"
|
125
|
+
end
|
126
|
+
|
127
|
+
success = load_checkpoint_from_s3
|
101
128
|
|
129
|
+
if !success
|
130
|
+
@base_client.log_internal Logger::WARN, "No success loading checkpoints"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def load_checkpoint_from_grpc_api
|
102
135
|
config_req = Prefab::ConfigServicePointer.new(start_at_id: @config_loader.highwater_mark)
|
103
136
|
|
104
137
|
resp = stub.get_all_config(config_req)
|
105
|
-
|
106
|
-
load_configs(resp, :api)
|
107
|
-
@config_resolver.update
|
108
|
-
finish_init!(:api)
|
138
|
+
load_configs(resp, :remote_api_grpc)
|
109
139
|
true
|
140
|
+
rescue GRPC::Unauthenticated
|
141
|
+
@base_client.log_internal Logger::WARN, "Unauthenticated"
|
110
142
|
rescue => e
|
111
|
-
|
143
|
+
puts e.class
|
144
|
+
@base_client.log_internal Logger::WARN, "Unexpected grpc_api problem loading checkpoint #{e}"
|
112
145
|
false
|
113
146
|
end
|
114
147
|
|
148
|
+
def load_checkpoint_api_cdn
|
149
|
+
key_hash = Murmur3.murmur3_32(@base_client.api_key)
|
150
|
+
url = "#{@options.url_for_api_cdn}/api/v1/config/#{@base_client.project_id}/#{key_hash}/0"
|
151
|
+
conn = if Faraday::VERSION[0].to_i >= 2
|
152
|
+
Faraday.new(url) do |conn|
|
153
|
+
conn.request :authorization, :basic, @base_client.project_id, @base_client.api_key
|
154
|
+
end
|
155
|
+
else
|
156
|
+
Faraday.new(url) do |conn|
|
157
|
+
conn.request :basic_auth, @base_client.project_id, @base_client.api_key
|
158
|
+
end
|
159
|
+
end
|
160
|
+
load_url(conn, :remote_cdn_api)
|
161
|
+
end
|
162
|
+
|
115
163
|
def load_checkpoint_from_s3
|
116
164
|
url = "#{@s3_cloud_front}/#{@base_client.api_key.gsub("|", "/")}"
|
117
|
-
|
165
|
+
load_url(Faraday.new(url), :remote_s3)
|
166
|
+
end
|
167
|
+
|
168
|
+
def load_url(conn, source)
|
169
|
+
resp = conn.get('')
|
118
170
|
if resp.status == 200
|
119
171
|
configs = Prefab::Configs.decode(resp.body)
|
120
|
-
load_configs(configs,
|
172
|
+
load_configs(configs, source)
|
173
|
+
true
|
121
174
|
else
|
122
|
-
@base_client.log_internal Logger::INFO, "
|
175
|
+
@base_client.log_internal Logger::INFO, "Checkpoint #{source} failed to load. Response #{resp.status}"
|
176
|
+
false
|
123
177
|
end
|
178
|
+
rescue => e
|
179
|
+
@base_client.log_internal Logger::WARN, "Unexpected #{source} problem loading checkpoint #{e} #{conn}"
|
180
|
+
false
|
124
181
|
end
|
125
182
|
|
126
183
|
def load_configs(configs, source)
|
127
184
|
project_env_id = configs.config_service_pointer.project_env_id
|
128
185
|
@config_resolver.project_env_id = project_env_id
|
186
|
+
starting_highwater_mark = @config_loader.highwater_mark
|
129
187
|
|
130
|
-
@base_client.log_internal Logger::INFO, "Prefab Initializing in project: #{@base_client.project_id} environment: #{project_env_id} and namespace: '#{@namespace}'"
|
131
188
|
configs.configs.each do |config|
|
132
|
-
@config_loader.set(config)
|
189
|
+
@config_loader.set(config, source)
|
190
|
+
end
|
191
|
+
if @config_loader.highwater_mark > starting_highwater_mark
|
192
|
+
@base_client.log_internal Logger::INFO, "Found new checkpoint with highwater id #{@config_loader.highwater_mark} from #{source} in project #{@base_client.project_id} environment: #{project_env_id} and namespace: '#{@namespace}'"
|
193
|
+
else
|
194
|
+
@base_client.log_internal Logger::DEBUG, "Checkpoint with highwater id #{@config_loader.highwater_mark} from #{source}. No changes.", "prefab.config_client.load_configs"
|
133
195
|
end
|
134
|
-
@base_client.log_internal Logger::INFO, "Found checkpoint with highwater id #{@config_loader.highwater_mark} from #{source}"
|
135
196
|
@base_client.stats.increment("prefab.config.checkpoint.load")
|
136
197
|
@config_resolver.update
|
137
198
|
finish_init!(source)
|
@@ -139,6 +200,7 @@ module Prefab
|
|
139
200
|
|
140
201
|
# A thread that checks for a checkpoint
|
141
202
|
def start_checkpointing_thread
|
203
|
+
|
142
204
|
Thread.new do
|
143
205
|
loop do
|
144
206
|
begin
|
@@ -158,72 +220,32 @@ module Prefab
|
|
158
220
|
|
159
221
|
def finish_init!(source)
|
160
222
|
if @initialization_lock.write_locked?
|
161
|
-
@base_client.log_internal Logger::
|
223
|
+
@base_client.log_internal Logger::INFO, "Unlocked Config via #{source}"
|
162
224
|
@initialization_lock.release_write_lock
|
163
225
|
@base_client.log.set_config_client(self)
|
226
|
+
@base_client.log_internal Logger::INFO, to_s
|
164
227
|
end
|
165
228
|
end
|
166
229
|
|
167
|
-
|
168
230
|
def start_sse_streaming_connection_thread(start_at_id)
|
169
231
|
auth = "#{@base_client.project_id}:#{@base_client.api_key}"
|
170
|
-
|
171
232
|
auth_string = Base64.strict_encode64(auth)
|
172
233
|
headers = {
|
173
234
|
"x-prefab-start-at-id": start_at_id,
|
174
235
|
"Authorization": "Basic #{auth_string}",
|
175
236
|
}
|
176
237
|
url = "#{@base_client.prefab_api_url}/api/v1/sse/config"
|
177
|
-
@base_client.log_internal Logger::INFO, "SSE Streaming Connect to #{url}"
|
178
|
-
SSE::Client.new(url,
|
238
|
+
@base_client.log_internal Logger::INFO, "SSE Streaming Connect to #{url} start_at #{start_at_id}"
|
239
|
+
@streaming_thread = SSE::Client.new(url,
|
240
|
+
headers: headers,
|
241
|
+
read_timeout: SSE_READ_TIMEOUT,
|
242
|
+
logger: Prefab::InternalLogger.new("prefab.config.sse", @base_client.log)) do |client|
|
179
243
|
client.on_event do |event|
|
180
244
|
configs = Prefab::Configs.decode(Base64.decode64(event.data))
|
181
|
-
|
182
|
-
@base_client.log_internal Logger::DEBUG, "SSE received configs: #{configs}"
|
183
|
-
configs.configs.each do |config|
|
184
|
-
@config_loader.set(config)
|
185
|
-
end
|
186
|
-
@config_resolver.update
|
187
|
-
finish_init!(:streaming)
|
245
|
+
load_configs(configs, :sse)
|
188
246
|
end
|
189
247
|
end
|
190
248
|
end
|
191
|
-
|
192
|
-
# Setup a streaming connection to the API
|
193
|
-
# Save new config values into the loader
|
194
|
-
def start_grpc_streaming_connection_thread(start_at_id)
|
195
|
-
config_req = Prefab::ConfigServicePointer.new(start_at_id: start_at_id)
|
196
|
-
@base_client.log_internal Logger::DEBUG, "start api connection thread #{start_at_id}"
|
197
|
-
@base_client.stats.increment("prefab.config.api.start")
|
198
|
-
|
199
|
-
@api_connection_thread = Thread.new do
|
200
|
-
at_exit do
|
201
|
-
@streaming = false
|
202
|
-
@cancellable_interceptor.cancel
|
203
|
-
end
|
204
|
-
|
205
|
-
while @streaming do
|
206
|
-
begin
|
207
|
-
resp = stub.get_config(config_req)
|
208
|
-
resp.each do |r|
|
209
|
-
r.configs.each do |config|
|
210
|
-
@config_loader.set(config)
|
211
|
-
end
|
212
|
-
@config_resolver.update
|
213
|
-
finish_init!(:streaming)
|
214
|
-
end
|
215
|
-
rescue => e
|
216
|
-
if @streaming
|
217
|
-
level = e.code == 1 ? Logger::DEBUG : Logger::INFO
|
218
|
-
@base_client.log_internal level, ("config client encountered #{e.message} pausing #{RECONNECT_WAIT}")
|
219
|
-
reset
|
220
|
-
sleep(RECONNECT_WAIT)
|
221
|
-
end
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
|
-
|
226
|
-
end
|
227
249
|
end
|
228
250
|
end
|
229
251
|
|
data/lib/prefab/config_helper.rb
CHANGED
@@ -16,5 +16,13 @@ module Prefab
|
|
16
16
|
config_value.segment
|
17
17
|
end
|
18
18
|
end
|
19
|
+
|
20
|
+
def value_of_variant(feature_flag_variant)
|
21
|
+
return feature_flag_variant.string if feature_flag_variant.has_string?
|
22
|
+
return feature_flag_variant.int if feature_flag_variant.has_int?
|
23
|
+
return feature_flag_variant.double if feature_flag_variant.has_double?
|
24
|
+
return feature_flag_variant.bool if feature_flag_variant.has_bool?
|
25
|
+
return nil
|
26
|
+
end
|
19
27
|
end
|
20
28
|
end
|
data/lib/prefab/config_loader.rb
CHANGED
@@ -5,6 +5,7 @@ module Prefab
|
|
5
5
|
|
6
6
|
def initialize(base_client)
|
7
7
|
@base_client = base_client
|
8
|
+
@prefab_options = base_client.options
|
8
9
|
@highwater_mark = 0
|
9
10
|
@classpath_config = load_classpath_config
|
10
11
|
@local_overrides = load_local_overrides
|
@@ -20,16 +21,19 @@ module Prefab
|
|
20
21
|
rtn
|
21
22
|
end
|
22
23
|
|
23
|
-
def set(config)
|
24
|
+
def set(config, source)
|
24
25
|
# don't overwrite newer values
|
25
|
-
if @api_config[config.key] && @api_config[config.key].id
|
26
|
+
if @api_config[config.key] && @api_config[config.key][:config].id >= config.id
|
26
27
|
return
|
27
28
|
end
|
28
29
|
|
29
30
|
if config.rows.empty?
|
30
31
|
@api_config.delete(config.key)
|
31
32
|
else
|
32
|
-
@api_config[config.key]
|
33
|
+
if @api_config[config.key]
|
34
|
+
@base_client.log_internal Logger::DEBUG, "Replace #{config.key} with value from #{source} #{ @api_config[config.key][:config].id} -> #{config.id}"
|
35
|
+
end
|
36
|
+
@api_config[config.key] = { source: source, config: config }
|
33
37
|
end
|
34
38
|
@highwater_mark = [config.id, @highwater_mark].max
|
35
39
|
end
|
@@ -41,7 +45,7 @@ module Prefab
|
|
41
45
|
def get_api_deltas
|
42
46
|
configs = Prefab::Configs.new
|
43
47
|
@api_config.each_value do |config_value|
|
44
|
-
configs.configs << config_value
|
48
|
+
configs.configs << config_value[:config]
|
45
49
|
end
|
46
50
|
configs
|
47
51
|
end
|
@@ -49,12 +53,12 @@ module Prefab
|
|
49
53
|
private
|
50
54
|
|
51
55
|
def load_classpath_config
|
52
|
-
classpath_dir =
|
56
|
+
classpath_dir = @prefab_options.prefab_config_classpath_dir
|
53
57
|
load_glob(File.join(classpath_dir, ".prefab*config.yaml"))
|
54
58
|
end
|
55
59
|
|
56
60
|
def load_local_overrides
|
57
|
-
override_dir =
|
61
|
+
override_dir = @prefab_options.prefab_config_override_dir
|
58
62
|
load_glob(File.join(override_dir, ".prefab*config.yaml"))
|
59
63
|
end
|
60
64
|
|
@@ -63,9 +67,26 @@ module Prefab
|
|
63
67
|
Dir.glob(glob).each do |file|
|
64
68
|
yaml = load(file)
|
65
69
|
yaml.each do |k, v|
|
66
|
-
|
67
|
-
|
68
|
-
|
70
|
+
if v.class == Hash
|
71
|
+
v.each do |env_k, env_v|
|
72
|
+
if k == @prefab_options.defaults_env
|
73
|
+
rtn[env_k] = { source: file,
|
74
|
+
match: k,
|
75
|
+
config: Prefab::Config.new(key: env_k, rows: [
|
76
|
+
Prefab::ConfigRow.new(value: Prefab::ConfigValue.new(value_from(env_v)))
|
77
|
+
]) }
|
78
|
+
else
|
79
|
+
next
|
80
|
+
end
|
81
|
+
end
|
82
|
+
else
|
83
|
+
rtn[k] = { source: file,
|
84
|
+
match: "default",
|
85
|
+
config: Prefab::Config.new(key: k, rows: [
|
86
|
+
Prefab::ConfigRow.new(value: Prefab::ConfigValue.new(value_from(v)))
|
87
|
+
]) }
|
88
|
+
|
89
|
+
end
|
69
90
|
end
|
70
91
|
end
|
71
92
|
rtn
|
@@ -8,22 +8,27 @@ module Prefab
|
|
8
8
|
def initialize(base_client, config_loader)
|
9
9
|
@lock = Concurrent::ReadWriteLock.new
|
10
10
|
@local_store = {}
|
11
|
-
@namespace = base_client.namespace
|
11
|
+
@namespace = base_client.options.namespace
|
12
12
|
@config_loader = config_loader
|
13
13
|
@project_env_id = 0
|
14
14
|
make_local
|
15
15
|
end
|
16
16
|
|
17
17
|
def to_s
|
18
|
-
str = ""
|
18
|
+
str = "\n"
|
19
19
|
@lock.with_read_lock do
|
20
20
|
@local_store.each do |k, v|
|
21
|
+
elements = [k.slice(0..49).ljust(50)]
|
21
22
|
if v.nil?
|
22
|
-
|
23
|
+
elements << "tombstone"
|
23
24
|
else
|
24
25
|
value = v[:value]
|
25
|
-
|
26
|
+
elements << value_of(value).to_s.slice(0..34).ljust(35)
|
27
|
+
elements << value_of(value).class.to_s.slice(0..6).ljust(7)
|
28
|
+
elements << "Match: #{v[:match]}".slice(0..29).ljust(30)
|
29
|
+
elements << "Source: #{v[:source]}"
|
26
30
|
end
|
31
|
+
str << elements.join(" | ") << "\n"
|
27
32
|
end
|
28
33
|
end
|
29
34
|
str
|
@@ -39,12 +44,14 @@ module Prefab
|
|
39
44
|
config ? config[:config] : nil
|
40
45
|
end
|
41
46
|
|
42
|
-
def
|
43
|
-
|
47
|
+
def _get(key)
|
48
|
+
@lock.with_read_lock do
|
49
|
+
@local_store[key]
|
50
|
+
end
|
44
51
|
end
|
45
52
|
|
46
|
-
def
|
47
|
-
|
53
|
+
def update
|
54
|
+
make_local
|
48
55
|
end
|
49
56
|
|
50
57
|
private
|
@@ -64,23 +71,26 @@ module Prefab
|
|
64
71
|
|
65
72
|
def make_local
|
66
73
|
store = {}
|
67
|
-
@config_loader.calc_config.each do |key,
|
74
|
+
@config_loader.calc_config.each do |key, config_resolver_obj|
|
75
|
+
config = config_resolver_obj[:config]
|
68
76
|
sortable = config.rows.map do |row|
|
69
77
|
if row.project_env_id != 0
|
70
78
|
if row.project_env_id == @project_env_id
|
71
79
|
if !row.namespace.empty?
|
72
80
|
(starts_with, count) = starts_with_ns?(row.namespace, @namespace)
|
73
81
|
# rubocop:disable BlockNesting
|
74
|
-
{ sortable: 2 + count, match: row.namespace, value: row.value, config: config} if starts_with
|
82
|
+
{ sortable: 2 + count, match: "nm:#{row.namespace}", value: row.value, config: config} if starts_with
|
75
83
|
else
|
76
|
-
{ sortable: 1, match: row.project_env_id, value: row.value, config: config}
|
84
|
+
{ sortable: 1, match: "env:#{row.project_env_id}", value: row.value, config: config}
|
77
85
|
end
|
78
86
|
end
|
79
87
|
else
|
80
|
-
|
88
|
+
match = config_resolver_obj[:match] || "default"
|
89
|
+
{ sortable: 0, match: match, value: row.value, config: config}
|
81
90
|
end
|
82
91
|
end.compact
|
83
92
|
to_store = sortable.sort_by { |h| h[:sortable] }.last
|
93
|
+
to_store[:source] = config_resolver_obj[:source]
|
84
94
|
store[key] = to_store
|
85
95
|
end
|
86
96
|
|
@@ -88,11 +98,5 @@ module Prefab
|
|
88
98
|
@local_store = store
|
89
99
|
end
|
90
100
|
end
|
91
|
-
|
92
|
-
def _get(property)
|
93
|
-
@lock.with_read_lock do
|
94
|
-
@local_store[property]
|
95
|
-
end
|
96
|
-
end
|
97
101
|
end
|
98
102
|
end
|