constancy 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +26 -9
- data/lib/constancy.rb +2 -0
- data/lib/constancy/cli/config_command.rb +26 -18
- data/lib/constancy/config.rb +56 -103
- data/lib/constancy/sync_target.rb +11 -3
- data/lib/constancy/token_source.rb +94 -0
- data/lib/constancy/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4f4ece9e3ea596821b57e8e4e2f4e11d70833df49f3480082a4438a1a018a69b
|
4
|
+
data.tar.gz: 5cead608d291729245435375774093004032877b5e4e447aa7ebf0ff8a7b0e2a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b40eb6bc0491fce5719d10c44cd11b154f71ea6415a5f5564b116ed7814a8d796deb046cf18e6d74cf17b8da15a5726a094141ae491506d1e5a3739c192f6b6
|
7
|
+
data.tar.gz: c45737ecc9a1c1aa92b0bfb46594dbe5ce8be6dac55f4e9efb3e30655b65bec9ec1cccec1895a0805da86c9b8bd6193f5116b11798c3e2a1a24d2f6d3b159d24
|
data/README.md
CHANGED
@@ -20,14 +20,14 @@ synchronize the changes from the filesystem to Consul.
|
|
20
20
|
local:consul/config => consul:dc1:config/myapp
|
21
21
|
Keys scanned: 80
|
22
22
|
|
23
|
-
UPDATE config/myapp/prod/ip-
|
23
|
+
UPDATE config/myapp/prod/ip-allowlist.json
|
24
24
|
-------------------------------------------------------------------------------------
|
25
25
|
-["10.8.0.0/16"]
|
26
26
|
+["10.8.0.0/16","10.9.10.0/24"]
|
27
27
|
-------------------------------------------------------------------------------------
|
28
28
|
|
29
29
|
Keys to update: 1
|
30
|
-
~ config/myapp/prod/ip-
|
30
|
+
~ config/myapp/prod/ip-allowlist.json
|
31
31
|
|
32
32
|
You can also limit your command to specific synchronization targets by using
|
33
33
|
the `--target` flag:
|
@@ -38,19 +38,19 @@ the `--target` flag:
|
|
38
38
|
local:consul/config => consul:dc1:config/myapp
|
39
39
|
Keys scanned: 80
|
40
40
|
|
41
|
-
UPDATE config/myapp/prod/ip-
|
41
|
+
UPDATE config/myapp/prod/ip-allowlist.json
|
42
42
|
-------------------------------------------------------------------------------------
|
43
43
|
-["10.8.0.0/16"]
|
44
44
|
+["10.8.0.0/16","10.9.10.0/24"]
|
45
45
|
-------------------------------------------------------------------------------------
|
46
46
|
|
47
47
|
Keys to update: 1
|
48
|
-
~ config/myapp/prod/ip-
|
48
|
+
~ config/myapp/prod/ip-allowlist.json
|
49
49
|
|
50
50
|
Do you want to push these changes?
|
51
51
|
Enter 'yes' to continue: yes
|
52
52
|
|
53
|
-
UPDATE config/myapp/prod/ip-
|
53
|
+
UPDATE config/myapp/prod/ip-allowlist.json OK
|
54
54
|
|
55
55
|
Run `constancy --help` for additional options and commands.
|
56
56
|
|
@@ -127,6 +127,9 @@ required. An example `constancy.yml` is below including explanatory comments:
|
|
127
127
|
# 'none': expect no Consul token (although env vars will be used if they are set)
|
128
128
|
# 'env': expect Consul token to be set in CONSUL_TOKEN or CONSUL_HTTP_TOKEN
|
129
129
|
# 'vault': read Consul token from Vault based on settings in the 'vault' section
|
130
|
+
# 'vault.<label>': a named Vault token source, eg `vault.us-east-1` or `vault.dev`
|
131
|
+
# NOTE: labels must begin with a letter and may contain only (ASCII) letters,
|
132
|
+
# numbers, hyphens, and underscores
|
130
133
|
|
131
134
|
# the vault section is only necessary if consul.token_source is set to 'vault'
|
132
135
|
vault:
|
@@ -146,6 +149,15 @@ required. An example `constancy.yml` is below including explanatory comments:
|
|
146
149
|
# but can be set to something else for static values.
|
147
150
|
consul_token_field: token
|
148
151
|
|
152
|
+
# You can define one or more 'vault.<label>' sections to define alternative Vault
|
153
|
+
# token sources for use in individual sync targets.
|
154
|
+
vault.other:
|
155
|
+
url: https://your.vault.example
|
156
|
+
consul_token_path: consul/creds/my-other-role
|
157
|
+
vault.dev:
|
158
|
+
url: https://dev.vault.example
|
159
|
+
consul_token_path: consul/creds/my-dev-role
|
160
|
+
|
149
161
|
sync:
|
150
162
|
# sync is an array of hashes of sync target configurations
|
151
163
|
# Fields:
|
@@ -167,6 +179,10 @@ required. An example `constancy.yml` is below including explanatory comments:
|
|
167
179
|
# containing a hash of remote keys if this sync target has
|
168
180
|
# type=file. This path is calculated relative to the directory
|
169
181
|
# containing the configuration file.
|
182
|
+
# token_source - An alternative token source other than the
|
183
|
+
# default. Potential values are the same as for the
|
184
|
+
# consul.token_source config value: 'none', 'env', 'vault',
|
185
|
+
# or 'vault.<label>'.
|
170
186
|
# delete - Whether or not to delete remote keys that do not exist
|
171
187
|
# in the local filesystem. This inherits the setting from the
|
172
188
|
# `constancy` section, or if not specified, defaults to `false`.
|
@@ -195,6 +211,7 @@ required. An example `constancy.yml` is below including explanatory comments:
|
|
195
211
|
type: dir
|
196
212
|
datacenter: dc1
|
197
213
|
path: consul/private
|
214
|
+
token_source: vault.dev
|
198
215
|
delete: true
|
199
216
|
- name: yourapp-config
|
200
217
|
prefix: config/yourapp
|
@@ -354,10 +371,10 @@ Constancy may be partially configured using environment variables:
|
|
354
371
|
interacting with the API. Otherwise, by default the agent's `acl_token`
|
355
372
|
setting is used implicitly.
|
356
373
|
* `VAULT_ADDR` and `VAULT_TOKEN` - if `consul.token_source` is set to `vault`
|
357
|
-
these variables are used to authenticate to Vault. If
|
358
|
-
set, Constancy will attempt to read a token from
|
359
|
-
`url` field is set, it will take priority over the
|
360
|
-
variable, but one or the other must be set.
|
374
|
+
or `vault.<label>`, these variables are used to authenticate to Vault. If
|
375
|
+
`VAULT_TOKEN` is not set, Constancy will attempt to read a token from
|
376
|
+
`~/.vault-token`. If the `url` field is set, it will take priority over the
|
377
|
+
`VAULT_ADDR` environment variable, but one or the other must be set.
|
361
378
|
|
362
379
|
|
363
380
|
## Roadmap
|
data/lib/constancy.rb
CHANGED
@@ -4,10 +4,12 @@ require 'erb'
|
|
4
4
|
require 'imperium'
|
5
5
|
require 'fileutils'
|
6
6
|
require 'ostruct'
|
7
|
+
require 'vault'
|
7
8
|
require 'yaml'
|
8
9
|
|
9
10
|
require_relative 'constancy/version'
|
10
11
|
require_relative 'constancy/config'
|
12
|
+
require_relative 'constancy/token_source'
|
11
13
|
require_relative 'constancy/diff'
|
12
14
|
require_relative 'constancy/sync_target'
|
13
15
|
|
@@ -8,18 +8,25 @@ class Constancy
|
|
8
8
|
Constancy::CLI.configure(call_external_apis: false)
|
9
9
|
|
10
10
|
puts " Config file: #{Constancy.config.config_file}"
|
11
|
-
puts " Consul URL: #{Constancy.config.
|
11
|
+
puts " Consul URL: #{Constancy.config.consul_url}"
|
12
12
|
puts " Verbose: #{Constancy.config.verbose?.to_s.bold}"
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
puts
|
14
|
+
puts " Defined Consul Token Sources:"
|
15
|
+
default_src_name = Constancy.config.default_consul_token_source.name
|
16
|
+
srcs = Constancy.config.consul_token_sources
|
17
|
+
( %w( none env ) + ( srcs.keys.sort - %w( none env ) ) ).each do |name|
|
18
18
|
puts
|
19
|
-
puts "
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
puts " #{name}:#{ default_src_name == name ? " (DEFAULT)".bold : ""}"
|
20
|
+
case name
|
21
|
+
when "none"
|
22
|
+
puts " uses CONSUL_HTTP_TOKEN or CONSUL_TOKEN env var if available"
|
23
|
+
when "env"
|
24
|
+
puts " requires CONSUL_HTTP_TOKEN or CONSUL_TOKEN env var"
|
25
|
+
when /^vault/
|
26
|
+
puts " address: #{srcs[name].vault_addr}"
|
27
|
+
puts " path: #{srcs[name].consul_token_path}"
|
28
|
+
puts " field: #{srcs[name].consul_token_field}"
|
29
|
+
end
|
23
30
|
end
|
24
31
|
puts
|
25
32
|
puts "Sync target defaults:"
|
@@ -35,16 +42,17 @@ class Constancy
|
|
35
42
|
else
|
36
43
|
print '*'
|
37
44
|
end
|
38
|
-
puts "
|
39
|
-
puts "
|
40
|
-
puts "
|
41
|
-
puts "
|
42
|
-
puts "
|
43
|
-
puts "
|
45
|
+
puts " Datacenter: #{target.datacenter}"
|
46
|
+
puts " Local type: #{target.type == :dir ? 'Directory' : 'Single file'}"
|
47
|
+
puts " #{target.type == :dir ? " Dir" : "File"} path: #{target.path}"
|
48
|
+
puts " Prefix: #{target.prefix}"
|
49
|
+
puts " Token Source: #{target.token_source.name}"
|
50
|
+
puts " Autochomp? #{target.chomp?}"
|
51
|
+
puts " Delete? #{target.delete?}"
|
44
52
|
if not target.exclude.empty?
|
45
|
-
puts "
|
53
|
+
puts " Exclusions:"
|
46
54
|
target.exclude.each do |exclusion|
|
47
|
-
puts "
|
55
|
+
puts " - #{exclusion}"
|
48
56
|
end
|
49
57
|
end
|
50
58
|
puts
|
data/lib/constancy/config.rb
CHANGED
@@ -11,6 +11,8 @@ class Constancy
|
|
11
11
|
class Config
|
12
12
|
CONFIG_FILENAMES = %w( constancy.yml )
|
13
13
|
VALID_CONFIG_KEYS = %w( sync consul vault constancy )
|
14
|
+
VALID_VAULT_KEY_PATTERNS = [ %r{^vault\.[A-Za-z][A-Za-z0-9_-]*$}, %r{^vault$} ]
|
15
|
+
VALID_CONFIG_KEY_PATTERNS = VALID_VAULT_KEY_PATTERNS
|
14
16
|
VALID_CONSUL_CONFIG_KEYS = %w( url datacenter token_source )
|
15
17
|
VALID_VAULT_CONFIG_KEYS = %w( url consul_token_path consul_token_field )
|
16
18
|
VALID_CONSTANCY_CONFIG_KEYS = %w( verbose chomp delete color )
|
@@ -18,7 +20,8 @@ class Constancy
|
|
18
20
|
DEFAULT_CONSUL_TOKEN_SOURCE = "none"
|
19
21
|
DEFAULT_VAULT_CONSUL_TOKEN_FIELD = "token"
|
20
22
|
|
21
|
-
attr_accessor :config_file, :base_dir, :
|
23
|
+
attr_accessor :config_file, :base_dir, :consul_url, :default_consul_token_source,
|
24
|
+
:sync_targets, :target_allowlist, :call_external_apis, :consul_token_sources
|
22
25
|
|
23
26
|
class << self
|
24
27
|
# discover the nearest config file
|
@@ -34,6 +37,15 @@ class Constancy
|
|
34
37
|
|
35
38
|
dir == "/" ? nil : self.discover(dir: File.dirname(dir))
|
36
39
|
end
|
40
|
+
|
41
|
+
def only_valid_config_keys!(keylist)
|
42
|
+
(keylist - VALID_CONFIG_KEYS).each do |key|
|
43
|
+
if not VALID_CONFIG_KEY_PATTERNS.find { |pattern| key =~ pattern }
|
44
|
+
raise Constancy::ConfigFileInvalid.new("'#{key}' is not a valid configuration key")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
true
|
48
|
+
end
|
37
49
|
end
|
38
50
|
|
39
51
|
def initialize(path: nil, targets: nil, call_external_apis: true)
|
@@ -51,7 +63,7 @@ class Constancy
|
|
51
63
|
|
52
64
|
self.config_file = File.expand_path(self.config_file)
|
53
65
|
self.base_dir = File.dirname(self.config_file)
|
54
|
-
self.
|
66
|
+
self.target_allowlist = targets
|
55
67
|
self.call_external_apis = call_external_apis
|
56
68
|
parse!
|
57
69
|
end
|
@@ -72,7 +84,11 @@ class Constancy
|
|
72
84
|
@use_color
|
73
85
|
end
|
74
86
|
|
75
|
-
|
87
|
+
def parse_vault_token_sources!(raw)
|
88
|
+
raw.keys.select { |key| VALID_VAULT_KEY_PATTERNS.find { |pattern| key =~ pattern } }.collect do |key|
|
89
|
+
[key, Constancy::VaultTokenSource.new(name: key, config: raw[key])]
|
90
|
+
end.to_h
|
91
|
+
end
|
76
92
|
|
77
93
|
def parse!
|
78
94
|
raw = {}
|
@@ -91,116 +107,40 @@ class Constancy
|
|
91
107
|
raise Constancy::ConfigFileInvalid.new("Config file must form a hash")
|
92
108
|
end
|
93
109
|
|
94
|
-
|
95
|
-
|
96
|
-
|
110
|
+
Constancy::Config.only_valid_config_keys!(raw.keys)
|
111
|
+
|
112
|
+
self.consul_token_sources = {
|
113
|
+
"none" => Constancy::PassiveTokenSource.new,
|
114
|
+
"env" => Constancy::EnvTokenSource.new,
|
115
|
+
}.merge(
|
116
|
+
self.parse_vault_token_sources!(raw),
|
117
|
+
)
|
97
118
|
|
98
119
|
raw['consul'] ||= {}
|
99
120
|
if not raw['consul'].is_a? Hash
|
100
121
|
raise Constancy::ConfigFileInvalid.new("'consul' must be a hash")
|
101
122
|
end
|
102
123
|
|
103
|
-
if (raw['consul'].keys -
|
104
|
-
raise Constancy::ConfigFileInvalid.new("Only the following keys are valid in the consul config: #{
|
124
|
+
if (raw['consul'].keys - VALID_CONSUL_CONFIG_KEYS) != []
|
125
|
+
raise Constancy::ConfigFileInvalid.new("Only the following keys are valid in the consul config: #{VALID_CONSUL_CONFIG_KEYS.join(", ")}")
|
105
126
|
end
|
106
127
|
|
107
|
-
consul_url = raw['consul']['url'] ||
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
case self.consul_token_source
|
114
|
-
when "none"
|
115
|
-
# nothing to do
|
116
|
-
|
117
|
-
when "env"
|
118
|
-
if consul_token.nil? or consul_token == ""
|
119
|
-
raise Constancy::ConsulTokenRequired.new("Consul token_source is set to 'env' but neither CONSUL_TOKEN nor CONSUL_HTTP_TOKEN is set")
|
120
|
-
end
|
121
|
-
|
122
|
-
when "vault"
|
123
|
-
require 'vault'
|
124
|
-
|
125
|
-
raw['vault'] ||= {}
|
126
|
-
if not raw['vault'].is_a? Hash
|
127
|
-
raise Constancy::ConfigFileInvalid.new("'vault' must be a hash")
|
128
|
-
end
|
129
|
-
|
130
|
-
if (raw['vault'].keys - Constancy::Config::VALID_VAULT_CONFIG_KEYS) != []
|
131
|
-
raise Constancy::ConfigFileInvalid.new("Only the following keys are valid in the vault config: #{Constancy::Config::VALID_VAULT_CONFIG_KEYS.join(", ")}")
|
132
|
-
end
|
133
|
-
|
134
|
-
vault_path = raw['vault']['consul_token_path']
|
135
|
-
if vault_path.nil? or vault_path == ""
|
136
|
-
raise Constancy::ConfigFileInvalid.new("vault.consul_token_path must be specified to use vault as a token source")
|
137
|
-
end
|
138
|
-
|
139
|
-
# prioritize the config file over environment variables for vault address
|
140
|
-
vault_addr = raw['vault']['url'] || ENV['VAULT_ADDR']
|
141
|
-
if vault_addr.nil? or vault_addr == ""
|
142
|
-
raise Constancy::VaultConfigInvalid.new("Vault address must be set in vault.url or VAULT_ADDR")
|
143
|
-
end
|
144
|
-
|
145
|
-
vault_token = ENV['VAULT_TOKEN']
|
146
|
-
if vault_token.nil? or vault_token == ""
|
147
|
-
vault_token_file = File.expand_path("~/.vault-token")
|
148
|
-
if File.exist?(vault_token_file)
|
149
|
-
vault_token = File.read(vault_token_file)
|
150
|
-
else
|
151
|
-
raise Constancy::VaultConfigInvalid.new("Vault token must be set in ~/.vault-token or VAULT_TOKEN")
|
128
|
+
self.consul_url = raw['consul']['url'] || DEFAULT_CONSUL_URL
|
129
|
+
srcname = raw['consul']['token_source'] || DEFAULT_CONSUL_TOKEN_SOURCE
|
130
|
+
self.default_consul_token_source =
|
131
|
+
self.consul_token_sources[srcname].tap do |src|
|
132
|
+
if src.nil?
|
133
|
+
raise Constancy::ConfigFileInvalid.new("Consul token source '#{consul_token_source}' is not defined")
|
152
134
|
end
|
153
135
|
end
|
154
136
|
|
155
|
-
vault_field = raw['vault']['consul_token_field'] || Constancy::Config::DEFAULT_VAULT_CONSUL_TOKEN_FIELD
|
156
|
-
|
157
|
-
self.vault_config = OpenStruct.new(
|
158
|
-
url: vault_addr,
|
159
|
-
consul_token_path: vault_path,
|
160
|
-
consul_token_field: vault_field,
|
161
|
-
)
|
162
|
-
|
163
|
-
# don't waste time talking to Vault if this is just a config parsing run
|
164
|
-
if self.call_external_apis
|
165
|
-
ENV['VAULT_ADDR'] = vault_addr
|
166
|
-
ENV['VAULT_TOKEN'] = vault_token
|
167
|
-
|
168
|
-
begin
|
169
|
-
response = Vault.logical.read(vault_path)
|
170
|
-
consul_token = response.data[vault_field.to_sym]
|
171
|
-
|
172
|
-
if response.lease_id
|
173
|
-
at_exit {
|
174
|
-
begin
|
175
|
-
Vault.sys.revoke(response.lease_id)
|
176
|
-
rescue => e
|
177
|
-
# this is fine
|
178
|
-
end
|
179
|
-
}
|
180
|
-
end
|
181
|
-
|
182
|
-
rescue => e
|
183
|
-
raise Constancy::VaultConfigInvalid.new("Are you logged in to Vault?\n\n#{e}")
|
184
|
-
end
|
185
|
-
|
186
|
-
if consul_token.nil? or consul_token == ""
|
187
|
-
raise Constancy::VaultConfigInvalid.new("Could not acquire a Consul token from Vault")
|
188
|
-
end
|
189
|
-
end
|
190
|
-
|
191
|
-
else
|
192
|
-
raise Constancy::ConfigFileInvalid.new("Only the following values are valid for token_source: none, env, vault")
|
193
|
-
end
|
194
|
-
|
195
|
-
self.consul = Imperium::Configuration.new(url: consul_url, token: consul_token)
|
196
|
-
|
197
137
|
raw['constancy'] ||= {}
|
198
138
|
if not raw['constancy'].is_a? Hash
|
199
139
|
raise Constancy::ConfigFileInvalid.new("'constancy' must be a hash")
|
200
140
|
end
|
201
141
|
|
202
|
-
if (raw['constancy'].keys -
|
203
|
-
raise Constancy::ConfigFileInvalid.new("Only the following keys are valid in the 'constancy' config block: #{
|
142
|
+
if (raw['constancy'].keys - VALID_CONSTANCY_CONFIG_KEYS) != []
|
143
|
+
raise Constancy::ConfigFileInvalid.new("Only the following keys are valid in the 'constancy' config block: #{VALID_CONSTANCY_CONFIG_KEYS.join(", ")}")
|
204
144
|
end
|
205
145
|
|
206
146
|
# verbose: default false
|
@@ -233,6 +173,7 @@ class Constancy
|
|
233
173
|
|
234
174
|
self.sync_targets = []
|
235
175
|
raw['sync'].each do |target|
|
176
|
+
token_source = self.default_consul_token_source
|
236
177
|
if target.is_a? Hash
|
237
178
|
target['datacenter'] ||= raw['consul']['datacenter']
|
238
179
|
if target['chomp'].nil?
|
@@ -241,19 +182,31 @@ class Constancy
|
|
241
182
|
if target['delete'].nil?
|
242
183
|
target['delete'] = self.delete?
|
243
184
|
end
|
185
|
+
if not target['token_source'].nil?
|
186
|
+
token_source = self.consul_token_sources[target['token_source']]
|
187
|
+
if token_source.nil?
|
188
|
+
raise Constancy::ConfigFileInvalid.new("Consul token source '#{target['token_source']}' is not defined")
|
189
|
+
end
|
190
|
+
target.delete('token_source')
|
191
|
+
end
|
244
192
|
end
|
245
193
|
|
246
|
-
if not self.
|
247
|
-
# unnamed targets cannot be
|
194
|
+
if not self.target_allowlist.nil?
|
195
|
+
# unnamed targets cannot be allowlisted
|
248
196
|
next if target['name'].nil?
|
249
197
|
|
250
|
-
# named targets must be on the
|
251
|
-
next if not self.
|
198
|
+
# named targets must be on the allowlist
|
199
|
+
next if not self.target_allowlist.include?(target['name'])
|
252
200
|
end
|
253
201
|
|
254
|
-
|
202
|
+
# only try to fetch consul tokens if we are actually going to do work
|
203
|
+
consul_token = if self.call_external_apis
|
204
|
+
token_source.consul_token
|
205
|
+
else
|
206
|
+
""
|
207
|
+
end
|
208
|
+
self.sync_targets << Constancy::SyncTarget.new(config: target, consul_url: consul_url, token_source: token_source, base_dir: self.base_dir, call_external_apis: self.call_external_apis)
|
255
209
|
end
|
256
|
-
|
257
210
|
end
|
258
211
|
end
|
259
212
|
end
|
@@ -3,13 +3,13 @@
|
|
3
3
|
class Constancy
|
4
4
|
class SyncTarget
|
5
5
|
VALID_CONFIG_KEYS = %w( name type datacenter prefix path exclude chomp delete erb_enabled )
|
6
|
-
attr_accessor :name, :type, :datacenter, :prefix, :path, :exclude, :consul, :erb_enabled
|
6
|
+
attr_accessor :name, :type, :datacenter, :prefix, :path, :exclude, :consul, :erb_enabled, :consul_url, :token_source, :call_external_apis
|
7
7
|
|
8
8
|
REQUIRED_CONFIG_KEYS = %w( prefix )
|
9
9
|
VALID_TYPES = [ :dir, :file ]
|
10
10
|
DEFAULT_TYPE = :dir
|
11
11
|
|
12
|
-
def initialize(config:,
|
12
|
+
def initialize(config:, consul_url:, token_source:, base_dir:, call_external_apis: true)
|
13
13
|
if not config.is_a? Hash
|
14
14
|
raise Constancy::ConfigFileInvalid.new("Sync target entries must be specified as hashes")
|
15
15
|
end
|
@@ -46,7 +46,15 @@ class Constancy
|
|
46
46
|
@do_delete = false
|
47
47
|
end
|
48
48
|
|
49
|
-
self.
|
49
|
+
self.call_external_apis = call_external_apis
|
50
|
+
self.consul_url = consul_url
|
51
|
+
self.token_source = token_source
|
52
|
+
token = if self.call_external_apis
|
53
|
+
self.token_source.consul_token
|
54
|
+
else
|
55
|
+
""
|
56
|
+
end
|
57
|
+
self.consul = Imperium::KV.new(Imperium::Configuration.new(url: self.consul_url, token: token))
|
50
58
|
self.erb_enabled = config['erb_enabled']
|
51
59
|
end
|
52
60
|
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# This software is public domain. No rights are reserved. See LICENSE for more information.
|
2
|
+
|
3
|
+
class Constancy
|
4
|
+
# use env vars if defined, but otherwise just return an empty string
|
5
|
+
class PassiveTokenSource
|
6
|
+
def name
|
7
|
+
"none"
|
8
|
+
end
|
9
|
+
|
10
|
+
def consul_token
|
11
|
+
ENV['CONSUL_HTTP_TOKEN'] || ENV['CONSUL_TOKEN'] || ""
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# use env vars and raise an error if none is found
|
16
|
+
class EnvTokenSource
|
17
|
+
def name
|
18
|
+
"env"
|
19
|
+
end
|
20
|
+
|
21
|
+
def consul_token
|
22
|
+
consul_token = ENV['CONSUL_HTTP_TOKEN'] || ENV['CONSUL_TOKEN']
|
23
|
+
if consul_token.nil? or consul_token == ""
|
24
|
+
raise Constancy::ConsulTokenRequired.new("Consul token_source was set to 'env' but neither CONSUL_TOKEN nor CONSUL_HTTP_TOKEN is set")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class VaultTokenSource
|
30
|
+
attr_accessor :name, :vault_addr, :vault_token, :consul_token_path, :consul_token_field
|
31
|
+
|
32
|
+
def initialize(name:, config:)
|
33
|
+
self.name = name
|
34
|
+
|
35
|
+
config ||= {}
|
36
|
+
if not config.is_a? Hash
|
37
|
+
raise Constancy::ConfigFileInvalid.new("'#{name}' must be a hash")
|
38
|
+
end
|
39
|
+
|
40
|
+
if (config.keys - Constancy::Config::VALID_VAULT_CONFIG_KEYS) != []
|
41
|
+
raise Constancy::ConfigFileInvalid.new("Only the following keys are valid in a vault config: #{Constancy::Config::VALID_VAULT_CONFIG_KEYS.join(", ")}")
|
42
|
+
end
|
43
|
+
|
44
|
+
self.consul_token_path = config['consul_token_path']
|
45
|
+
if self.consul_token_path.nil? or self.consul_token_path == ""
|
46
|
+
raise Constancy::ConfigFileInvalid.new("consul_token_path must be specified to use '#{name}' as a token source")
|
47
|
+
end
|
48
|
+
|
49
|
+
# prioritize the config file over environment variables for vault address
|
50
|
+
self.vault_addr = config['url'] || ENV['VAULT_ADDR']
|
51
|
+
if self.vault_addr.nil? or self.vault_addr == ""
|
52
|
+
raise Constancy::VaultConfigInvalid.new("Vault address must be set in #{name}.vault_addr or VAULT_ADDR")
|
53
|
+
end
|
54
|
+
|
55
|
+
self.vault_token = ENV['VAULT_TOKEN']
|
56
|
+
if self.vault_token.nil? or self.vault_token == ""
|
57
|
+
vault_token_file = File.expand_path("~/.vault-token")
|
58
|
+
if File.exist?(vault_token_file)
|
59
|
+
self.vault_token = File.read(vault_token_file)
|
60
|
+
else
|
61
|
+
raise Constancy::VaultConfigInvalid.new("Vault token must be set in ~/.vault-token or VAULT_TOKEN")
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
self.consul_token_field = config['consul_token_field'] || Constancy::Config::DEFAULT_VAULT_CONSUL_TOKEN_FIELD
|
66
|
+
end
|
67
|
+
|
68
|
+
def consul_token
|
69
|
+
if @consul_token.nil?
|
70
|
+
begin
|
71
|
+
response = Vault::Client.new(address: self.vault_addr, token: self.vault_token).logical.read(self.consul_token_path)
|
72
|
+
@consul_token = response.data[self.consul_token_field.to_sym]
|
73
|
+
if response.lease_id
|
74
|
+
at_exit {
|
75
|
+
begin
|
76
|
+
Vault::Client.new(address: self.vault_addr, token: self.vault_token).sys.revoke(response.lease_id)
|
77
|
+
rescue => e
|
78
|
+
# this is fine
|
79
|
+
end
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
rescue => e
|
84
|
+
raise Constancy::VaultConfigInvalid.new("Are you logged in to Vault?\n\n#{e}")
|
85
|
+
end
|
86
|
+
|
87
|
+
if @consul_token.nil? or @consul_token == ""
|
88
|
+
raise Constancy::VaultConfigInvalid.new("Could not acquire a Consul token from Vault")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
@consul_token
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/constancy/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: constancy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Adams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-09-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: imperium
|
@@ -87,6 +87,7 @@ files:
|
|
87
87
|
- lib/constancy/config.rb
|
88
88
|
- lib/constancy/diff.rb
|
89
89
|
- lib/constancy/sync_target.rb
|
90
|
+
- lib/constancy/token_source.rb
|
90
91
|
- lib/constancy/version.rb
|
91
92
|
homepage: https://github.com/daveadams/constancy
|
92
93
|
licenses:
|