cloud-config 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/Gemfile.lock +1 -1
- data/README.md +87 -4
- data/cloud-config.gemspec +8 -5
- data/examples/004_handling_configuration_changes.rb +45 -0
- data/lib/cloud-config/cache/in_memory.rb +1 -1
- data/lib/cloud-config/cache.rb +1 -1
- data/lib/cloud-config/providers/aws_parameter_store.rb +0 -4
- data/lib/cloud-config/providers.rb +16 -0
- data/lib/cloud-config/version.rb +1 -1
- data/lib/cloud-config.rb +12 -7
- metadata +10 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5d99c6f38f07031a7da456fa285c345a87ed6f124a534d2b5fe004540743e85
|
4
|
+
data.tar.gz: cba8c49a6fcc40d3cfac3f451826a3fddaaf6a0d8a7271033fbc7e833dc47cbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b883d58ca786602fe85135a77a31959c00dcd7d3af7534e63889320fb7d1d739285bb048fa6f2c72fb309c8ecf9ed4bc95da513be39a9d073a14ec768c4cb7c7
|
7
|
+
data.tar.gz: 0d61a08ecbba70c9ae743cf8fa84c68772da1a6a3566716fb9f17ed564472150ef5c6dcbc38e0f6b9722437327feef6cac85c6aca02e1bcd2c49ab0812c003fb
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
-
## [0.
|
3
|
+
## [0.2.0] - 2023-05-20
|
4
|
+
|
5
|
+
Bug fixes:
|
6
|
+
|
7
|
+
- Fix in memory cache expiry
|
8
|
+
|
9
|
+
Enhancements:
|
10
|
+
|
11
|
+
- Reset cache for single key
|
12
|
+
- Lookup providers by configuration key
|
13
|
+
|
14
|
+
## [Released]
|
15
|
+
|
16
|
+
## [0.1.0] - 2023-05-17
|
4
17
|
|
5
18
|
- Initial release
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,9 @@
|
|
2
2
|
|
3
3
|
A library to modernise the way applications fetch configuration. Typically an application will use environment variables and settings files to store and retrieve configurations, but there are many issues with this approach. Often environment variables create a security risk and settings files hard code infrastructure dependencies into the code. Changing configuration settings will usually require redeploying large parts of the infrastructure and perhaps even need to go through the application deployment lifecycle.
|
4
4
|
|
5
|
-
A modern approach stores configuration remotely, often using key/value databases. Storing configurations in a single database, separately from the codebase reduces infrastructure dependency. Configuration updates can automatically sync with any application, without requiring redeployments. Security risk is greatly reduced, since
|
5
|
+
A modern approach stores configuration remotely, often using key/value databases. Storing configurations in a single database, separately from the codebase reduces infrastructure dependency. Configuration updates can automatically sync with any application, without requiring redeployments. Security risk is greatly reduced, since configurations can be securely stored. Another goal with this approach is creating an environmentless codebase. The application no longer needs to know which environment it's running in, since all the configuration is handled by the infrastucture.
|
6
|
+
|
7
|
+
Another common problem is password rotation. A typical application will need to be restarted or even redeployed when the configuration settings change, such as rotating passwords. CloudConfig can handle this elegantly, improving the uptime and resilience of the application.
|
6
8
|
|
7
9
|
## Installation
|
8
10
|
|
@@ -16,7 +18,7 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
16
18
|
|
17
19
|
## Usage
|
18
20
|
|
19
|
-
CloudConfig can be configured to fetch keys from multiple providers. Since CloudConfig will need to know which provider has the correct key, all the keys will need to be preconfigured. An example configuration may look like
|
21
|
+
CloudConfig can be configured to fetch keys from multiple providers. Since CloudConfig will need to know which provider has the correct key, all the keys will need to be preconfigured. Duplicate keys will be overriden with the latest provider. An example configuration may look like
|
20
22
|
|
21
23
|
```ruby
|
22
24
|
CloudConfig.configure do
|
@@ -27,6 +29,18 @@ CloudConfig.configure do
|
|
27
29
|
end
|
28
30
|
```
|
29
31
|
|
32
|
+
Fetch a setting with
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
url = CloudConfig.get(:api_url)
|
36
|
+
```
|
37
|
+
|
38
|
+
If caching has been configured, the cache can be reset using
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
url = CloudConfig.get(:api_url, reset_cache: true)
|
42
|
+
```
|
43
|
+
|
30
44
|
### Configuration Options
|
31
45
|
|
32
46
|
```ruby
|
@@ -66,7 +80,7 @@ end
|
|
66
80
|
|
67
81
|
### Preloading
|
68
82
|
|
69
|
-
CloudConfig can preload all the keys configured for preload enabled providers. A cache client must be configured, otherwise
|
83
|
+
CloudConfig can preload all the keys configured for preload enabled providers. A cache client must also be configured, otherwise
|
70
84
|
preloading won't do anything.
|
71
85
|
|
72
86
|
```ruby
|
@@ -74,6 +88,7 @@ CloudConfig.configure do
|
|
74
88
|
cache_client CloudConfig::Cache::InMemory.new
|
75
89
|
|
76
90
|
provider :in_memory, preload: true do
|
91
|
+
setting :key1
|
77
92
|
end
|
78
93
|
end
|
79
94
|
```
|
@@ -84,9 +99,77 @@ Call preload to cache all the keys.
|
|
84
99
|
CloudConfig.preload
|
85
100
|
```
|
86
101
|
|
102
|
+
### Custom Providers
|
103
|
+
|
104
|
+
CloudConfig comes equipped with a set of providers for fetching configuration from remote datastores. The limit set of providers will not be sufficient for most real world applications, so creating custom providers will be necessary. There is a very simple interface consisting of 3 methods necessary for creating a custom provider.
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
class CustomProvider
|
108
|
+
# Define initialize with a params argument.
|
109
|
+
def initialize(params = {}); end
|
110
|
+
|
111
|
+
# Define `get` for fetching keys from the remote datastore
|
112
|
+
def get(key); end
|
113
|
+
|
114
|
+
# Define `set` for storing keys in the remote datastore
|
115
|
+
def set(key, value); end
|
116
|
+
end
|
117
|
+
```
|
118
|
+
|
119
|
+
Specify the class when configuring the custom provider.
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
CloudConfig.configure do
|
123
|
+
provider :custom_provider, provider_class: CustomProvider do
|
124
|
+
end
|
125
|
+
end
|
126
|
+
```
|
127
|
+
|
128
|
+
### Custom Cache
|
129
|
+
|
130
|
+
Defining a custom cache client will require an interface of 3 methods.
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
class CustomCache
|
134
|
+
# Define `key?` for checking whether the key exists in the cache client.
|
135
|
+
# This check allows nil values to be cached.
|
136
|
+
def key?(key); end
|
137
|
+
|
138
|
+
# Define `get` for fetching keys from the cache
|
139
|
+
def get(key); end
|
140
|
+
|
141
|
+
# Define `set` for storing keys in the cache
|
142
|
+
def set(key, value); end
|
143
|
+
end
|
144
|
+
```
|
145
|
+
|
146
|
+
Configure CloudConfig to use the cache in the usual way
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
CloudConfig.configure do
|
150
|
+
cache_client CustomCache.new
|
151
|
+
end
|
152
|
+
```
|
153
|
+
|
154
|
+
## Connection Pooling
|
155
|
+
|
156
|
+
CloudConfig does not do any connection pooling. It is the responsibility of the application to handle connection pooling. For example, Rails handles its own connection pools, so CloudConfig will not attempt to interfere with the pool.
|
157
|
+
|
158
|
+
## Examples
|
159
|
+
|
160
|
+
There are some example configurations in the [examples](examples) folder. In the terminal, execute
|
161
|
+
|
162
|
+
$ examples/001_parameter_store.rb
|
163
|
+
|
164
|
+
These examples make use of AWS infrastructure, so make sure the AWS enviroment variables have been correctly configured.
|
165
|
+
|
166
|
+
$ export AWS_ACCESS_KEY_ID=
|
167
|
+
$ export AWS_SECRET_ACCESS_KEY=
|
168
|
+
$ export AWS_REGION=
|
169
|
+
|
87
170
|
## Development
|
88
171
|
|
89
|
-
It's recommended to use Docker when working with
|
172
|
+
It's recommended to use Docker when working with this library. Assuming Docker is installed, run
|
90
173
|
|
91
174
|
$ docker-compose run config /bin/bash
|
92
175
|
|
data/cloud-config.gemspec
CHANGED
@@ -9,16 +9,19 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.email = ['hypernova2002@gmail.com']
|
10
10
|
|
11
11
|
spec.summary = 'A library for dynamically reloading settings from cloud services'
|
12
|
-
|
13
|
-
|
12
|
+
spec.description = <<~DOC
|
13
|
+
Modernise applications by fetching and caching remote settings. No longer store settings
|
14
|
+
in the codebase or as environment variables.
|
15
|
+
DOC
|
16
|
+
spec.homepage = 'http://github.com/hypernova2002/cloud-config'
|
14
17
|
spec.license = 'MIT'
|
15
18
|
spec.required_ruby_version = '>= 3.1.0'
|
16
19
|
|
17
20
|
# spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
|
18
21
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
23
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
24
|
+
spec.metadata['changelog_uri'] = "#{spec.homepage}/CHANGELOG.md"
|
22
25
|
|
23
26
|
# Specify which files should be added to the gem when it is released.
|
24
27
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/inline'
|
4
|
+
|
5
|
+
gemfile do
|
6
|
+
source 'https://rubygems.org'
|
7
|
+
|
8
|
+
gem 'faraday'
|
9
|
+
|
10
|
+
gem 'cloud-config', path: '..'
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'faraday'
|
14
|
+
require 'cloud-config/cache/in_memory'
|
15
|
+
require 'cloud-config/providers/in_memory'
|
16
|
+
|
17
|
+
CloudConfig.configure do
|
18
|
+
cache_client CloudConfig::Cache::InMemory.new
|
19
|
+
|
20
|
+
provider :in_memory, preload: true do
|
21
|
+
setting :url, cache: 60
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
CloudConfig.set(:url, 'https://httpstat.us/503')
|
26
|
+
|
27
|
+
url = CloudConfig.get(:url)
|
28
|
+
|
29
|
+
# Mimick a configuration change
|
30
|
+
CloudConfig.providers_by_key[:url].provider.set(:url, 'https://httpstat.us/200')
|
31
|
+
|
32
|
+
req = Faraday.new do |f|
|
33
|
+
f.response :raise_error
|
34
|
+
f.response :logger
|
35
|
+
end
|
36
|
+
|
37
|
+
begin
|
38
|
+
req.get(url)
|
39
|
+
rescue Faraday::ServerError
|
40
|
+
url = CloudConfig.get(:url, reset_cache: true)
|
41
|
+
|
42
|
+
sleep 1
|
43
|
+
|
44
|
+
retry
|
45
|
+
end
|
@@ -55,7 +55,7 @@ module CloudConfig
|
|
55
55
|
# @param value [Object] Value of the key
|
56
56
|
# @option options [Hash] :expire_in Time in seconds until key expires
|
57
57
|
def set(key, value, options = {})
|
58
|
-
expire_in = options
|
58
|
+
expire_in = options[:expire_in] || DEFAULT_EXPIRE
|
59
59
|
|
60
60
|
expire_at = Time.now + expire_in
|
61
61
|
|
data/lib/cloud-config/cache.rb
CHANGED
@@ -14,6 +14,10 @@ module CloudConfig
|
|
14
14
|
# @return [Hash<Symbol,ProviderConfig>]
|
15
15
|
attr_reader :providers
|
16
16
|
|
17
|
+
# @!attribute [r] Fetch provider configuration by setting key
|
18
|
+
# @return [Hash<Symbol,ProviderConfig>]
|
19
|
+
attr_reader :providers_by_key
|
20
|
+
|
17
21
|
# Add a provider to the list of provider configurations.
|
18
22
|
#
|
19
23
|
# @param provider_name [Symbol] Name of the provider
|
@@ -25,6 +29,18 @@ module CloudConfig
|
|
25
29
|
provider_config.instance_eval(&blk) if blk
|
26
30
|
@providers ||= {}
|
27
31
|
@providers[provider_name] = provider_config
|
32
|
+
|
33
|
+
update_provider_keys(provider_config)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Update {#providers_by_key} with settings from the provider config.
|
37
|
+
#
|
38
|
+
# @param provider_config [ProviderConfig] Provider config containing the keys
|
39
|
+
def update_provider_keys(provider_config)
|
40
|
+
@providers_by_key ||= {}
|
41
|
+
provider_config.settings.each_key do |key|
|
42
|
+
@providers_by_key[key] = provider_config
|
43
|
+
end
|
28
44
|
end
|
29
45
|
end
|
30
46
|
end
|
data/lib/cloud-config/version.rb
CHANGED
data/lib/cloud-config.rb
CHANGED
@@ -35,14 +35,15 @@ module CloudConfig
|
|
35
35
|
# Fetch the value of a key using the appropriate provider.
|
36
36
|
#
|
37
37
|
# @param key [String,Symbol] Key to lookup
|
38
|
+
# @param reset_cache [Boolean] Whether the cache for the key should be reset
|
38
39
|
#
|
39
40
|
# @return [Object] Value of the key
|
40
|
-
def get(key)
|
41
|
-
provider_config =
|
41
|
+
def get(key, reset_cache: false)
|
42
|
+
provider_config = providers_by_key[key]
|
42
43
|
|
43
44
|
raise MissingKey, 'Key not found' if provider_config.nil?
|
44
45
|
|
45
|
-
load_key(provider_config, key)
|
46
|
+
load_key(provider_config, key, reset_cache:)
|
46
47
|
end
|
47
48
|
|
48
49
|
# Set the value of a key with the configured provider.
|
@@ -50,7 +51,7 @@ module CloudConfig
|
|
50
51
|
# @param key [String,Symobl] Key to update
|
51
52
|
# @param value [Object] Value of key
|
52
53
|
def set(key, value)
|
53
|
-
provider_config =
|
54
|
+
provider_config = providers_by_key[key]
|
54
55
|
|
55
56
|
raise MissingKey, 'Key not found' if provider_config.nil?
|
56
57
|
|
@@ -84,10 +85,11 @@ module CloudConfig
|
|
84
85
|
#
|
85
86
|
# @param provider_config [CloudConfig::ProviderConfig] provider configuration
|
86
87
|
# @param key [String,Symbol] Key to fetch
|
88
|
+
# @param reset_cache [Boolean] Whether the cache for the key should be reset
|
87
89
|
#
|
88
90
|
# @return [Object] Value of the key
|
89
|
-
def load_key(provider_config, key)
|
90
|
-
with_cache(key, expire_in: provider_config.settings[key][:cache]) do
|
91
|
+
def load_key(provider_config, key, reset_cache: false)
|
92
|
+
with_cache(key, reset_cache:, expire_in: provider_config.settings[key][:cache]) do
|
91
93
|
provider_config.provider.get(key)
|
92
94
|
end
|
93
95
|
end
|
@@ -95,6 +97,9 @@ module CloudConfig
|
|
95
97
|
# Reset the {CloudConfig} configuration
|
96
98
|
def reset!
|
97
99
|
@cache = nil
|
98
|
-
@
|
100
|
+
@providers = {}
|
101
|
+
@providers_by_key = {}
|
99
102
|
end
|
100
103
|
end
|
104
|
+
|
105
|
+
CloudConfig.reset!
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cloud-config
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- hypernova2002
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-05-
|
11
|
+
date: 2023-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: parallel
|
@@ -206,7 +206,9 @@ dependencies:
|
|
206
206
|
- - ">="
|
207
207
|
- !ruby/object:Gem::Version
|
208
208
|
version: '0'
|
209
|
-
description:
|
209
|
+
description: |2
|
210
|
+
Modernise applications by fetching and caching remote settings. No longer store settings
|
211
|
+
in the codebase or as environment variables.
|
210
212
|
email:
|
211
213
|
- hypernova2002@gmail.com
|
212
214
|
executables: []
|
@@ -228,6 +230,7 @@ files:
|
|
228
230
|
- examples/001_parameter_store.rb
|
229
231
|
- examples/002_parameter_store_with_cache.rb
|
230
232
|
- examples/003_parameter_store_with_preload.rb
|
233
|
+
- examples/004_handling_configuration_changes.rb
|
231
234
|
- lib/cloud-config.rb
|
232
235
|
- lib/cloud-config/cache.rb
|
233
236
|
- lib/cloud-config/cache/in_memory.rb
|
@@ -241,10 +244,13 @@ files:
|
|
241
244
|
- lib/cloud-config/providers/yaml_file.rb
|
242
245
|
- lib/cloud-config/version.rb
|
243
246
|
- sig/cloud-config.rbs
|
244
|
-
homepage:
|
247
|
+
homepage: http://github.com/hypernova2002/cloud-config
|
245
248
|
licenses:
|
246
249
|
- MIT
|
247
250
|
metadata:
|
251
|
+
homepage_uri: http://github.com/hypernova2002/cloud-config
|
252
|
+
source_code_uri: http://github.com/hypernova2002/cloud-config
|
253
|
+
changelog_uri: http://github.com/hypernova2002/cloud-config/CHANGELOG.md
|
248
254
|
rubygems_mfa_required: 'true'
|
249
255
|
post_install_message:
|
250
256
|
rdoc_options: []
|