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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 22d564bcc76424b3308623b4d2e27a4acaf4217c285bcca02542291d8cc15827
4
- data.tar.gz: c097169d379d9f477a66d20fc378f693173de044baecad08059263fd239257e5
3
+ metadata.gz: 2525930a7f8c2e2924db43504b7dfb6d32e60647bbc359b8a85af8ae3c9f2db0
4
+ data.tar.gz: 583f5dc901866eaf206f7f693a95bec47b7ea04c54f3d56275a04bbf0a7bb75f
5
5
  SHA512:
6
- metadata.gz: cdf6ca3b1b28bf83caa629193b21e2d26e4d22d64ee4cd30b7a88fdce623adc7f3feb3605df7107cdca00e7823709f9eeac2a68176db1d10b6616196d2bcb580
7
- data.tar.gz: c61b171a2be217c016fad1cc0a1ce9faed159c6f894065db4202ec200ec1f8ac22caabb880fae0f358fcced0930ce49d4e273f800a0fb72c39c6d6f19cc59d07
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.7.0)
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.8.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.7.1)
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.1)
84
- mini_portile2 (~> 2.7.0)
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.11.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
- DEFAULT_LOG_FORMATTER = proc {|severity, datetime, progname, msg|
7
- "#{severity.ljust(5)} #{datetime}: #{progname} #{msg}\n"
8
- }
9
-
10
-
11
- attr_reader :project_id, :shared_cache, :stats, :namespace, :interceptor, :api_key, :prefab_api_url
12
-
13
- def initialize(api_key: ENV['PREFAB_API_KEY'],
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
- @prefab_api_url = ENV["PREFAB_API_URL"] || 'https://api.prefab.cloud'
34
- @prefab_grpc_url = ENV["PREFAB_GRPC_URL"] || 'grpc.prefab.cloud:443'
35
- log_internal Logger::INFO, "Prefab Connecting to: #{@prefab_api_url} and #{@prefab_grpc_url} Secure: #{http_secure?}"
36
- at_exit do
37
- channel.destroy
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, "prefab", nil, level
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.exists?(OpenSSL::X509::DEFAULT_CERT_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
@@ -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
- @initialization_lock = Concurrent::ReadWriteLock.new
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
- load_checkpoint
27
- start_checkpointing_thread
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
- @streaming = true
32
- # start_grpc_streaming_connection_thread(@config_loader.highwater_mark)
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
- @initialization_lock.with_read_lock do
72
- @config_resolver.get_config(key)
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
- # Bootstrap out of the cache
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 = load_checkpoint_from_config
111
+ success = load_checkpoint_api_cdn
89
112
 
90
- if !success
91
- @base_client.log_internal Logger::INFO, "Fallback to S3"
92
- load_checkpoint_from_s3
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
- rescue => e
96
- @base_client.log_internal Logger::WARN, "Unexpected problem loading checkpoint #{e}"
97
- end
119
+ success = load_checkpoint_from_grpc_api
98
120
 
99
- def load_checkpoint_from_config
100
- @base_client.log_internal Logger::DEBUG, "Load Checkpoint From Config"
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
- @base_client.log_internal Logger::DEBUG, "Got Response #{resp}"
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
- @base_client.log_internal Logger::WARN, "Unexpected problem loading checkpoint #{e}"
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
- resp = Faraday.get url
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, :s3)
172
+ load_configs(configs, source)
173
+ true
121
174
  else
122
- @base_client.log_internal Logger::INFO, "No S3 checkpoint. Response #{resp.status} Plan may not support this."
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::DEBUG, "Unlocked Config via #{source}"
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, headers: headers) do |client|
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
- @base_client.log_internal Logger::INFO, "SSE received configs."
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
 
@@ -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
@@ -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 > config.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] = config
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 = ENV['PREFAB_CONFIG_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 = ENV['PREFAB_CONFIG_OVERRIDE_DIR'] || Dir.home
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
- rtn[k] = Prefab::Config.new(key: k, rows: [
67
- Prefab::ConfigRow.new(value: Prefab::ConfigValue.new(value_from(v)))
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
- str<< "|#{k}| tombstone\n"
23
+ elements << "tombstone"
23
24
  else
24
25
  value = v[:value]
25
- str << "|#{k}| from #{v[:match]} |#{value_of(value)}|#{value_of(value).class}\n"
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 update
43
- make_local
47
+ def _get(key)
48
+ @lock.with_read_lock do
49
+ @local_store[key]
50
+ end
44
51
  end
45
52
 
46
- def export_api_deltas
47
- @config_loader.get_api_deltas
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, config|
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
- { sortable: 0, match: "default", value: row.value, config: config}
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