consult 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 31a2190f1f1f38f5b2f674fa7d1ed91be5522d335e10c655f05493cbfdc056c3
4
+ data.tar.gz: 0c51acfbd537bb327f6360a4e6ef59fa8ec79b35c7326e421b14362f7a47d827
5
+ SHA512:
6
+ metadata.gz: b68efe09e942c634da159734f792da61e9fe16dc288e95b3cfafa1c0392457998f4149d0a6c5c1748238ff7ac3bf1165e63c80ed2d956914a372e591dbfc1545
7
+ data.tar.gz: 14b46ab1dccee0d9739ec820ee94c89af221aedf83e5ec46f072df6fbe9b78c353cc603cd6274d68c8f5eafecde2adb20653c0ef322881843971d17465b209ef
data/.gitignore ADDED
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/support/rendered/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,34 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.4
3
+
4
+ Metrics/LineLength:
5
+ Max: 117
6
+
7
+ Metrics/MethodLength:
8
+ Max: 20
9
+
10
+ Metrics/ClassLength:
11
+ Max: 400
12
+
13
+ Metrics/AbcSize:
14
+ Max: 30
15
+
16
+ Metrics/BlockLength:
17
+ Exclude:
18
+ - 'spec/**/*_spec.rb'
19
+ - 'test/**/*_test.rb'
20
+ - '*.gemspec'
21
+ - 'Gemfile'
22
+ - 'Guardfile'
23
+
24
+ # Reserve space for blocks to visually distinguish blocks from hashes
25
+ Layout/SpaceInsideHashLiteralBraces:
26
+ EnforcedStyle: no_space
27
+
28
+ # use 'raise' instead of 'fail'
29
+ Style/SignalException:
30
+ EnforcedStyle: only_raise
31
+
32
+ # mixed: Use slashes on single-line regexes, and %r on multi-line regexes.
33
+ Style/RegexpLiteral:
34
+ EnforcedStyle: mixed
data/.travis.yml ADDED
@@ -0,0 +1,15 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.5.0
5
+ - 2.4.1
6
+
7
+ services:
8
+ - docker
9
+
10
+ before_install:
11
+ - gem install bundler -v 1.16.1
12
+ - docker pull consul
13
+ - docker pull vault
14
+ - docker run -d --name=dev-consul -p 8500:8500 consul
15
+ - docker run -d --name=dev-vault -p 8200:8200 --cap-add=IPC_LOCK -e 'VAULT_DEV_ROOT_TOKEN_ID=94e1a9ed-5d72-5677-27ab-ebc485cca368' vault
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in consult.gemspec
6
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,106 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ consult (0.5.0)
5
+ activesupport (> 4, < 6)
6
+ diplomat
7
+ vault
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (5.1.4)
13
+ concurrent-ruby (~> 1.0, >= 1.0.2)
14
+ i18n (~> 0.7)
15
+ minitest (~> 5.1)
16
+ tzinfo (~> 1.1)
17
+ byebug (9.1.0)
18
+ coderay (1.1.2)
19
+ concurrent-ruby (1.0.5)
20
+ diff-lcs (1.3)
21
+ diplomat (2.0.2)
22
+ faraday (~> 0.9)
23
+ json
24
+ docile (1.1.5)
25
+ faraday (0.14.0)
26
+ multipart-post (>= 1.2, < 3)
27
+ ffi (1.9.18)
28
+ formatador (0.2.5)
29
+ guard (2.14.1)
30
+ formatador (>= 0.2.4)
31
+ listen (>= 2.7, < 4.0)
32
+ lumberjack (~> 1.0)
33
+ nenv (~> 0.1)
34
+ notiffany (~> 0.0)
35
+ pry (>= 0.9.12)
36
+ shellany (~> 0.0)
37
+ thor (>= 0.18.1)
38
+ guard-compat (1.2.1)
39
+ guard-rspec (4.7.3)
40
+ guard (~> 2.1)
41
+ guard-compat (~> 1.1)
42
+ rspec (>= 2.99.0, < 4.0)
43
+ i18n (0.9.5)
44
+ concurrent-ruby (~> 1.0)
45
+ json (2.1.0)
46
+ listen (3.1.5)
47
+ rb-fsevent (~> 0.9, >= 0.9.4)
48
+ rb-inotify (~> 0.9, >= 0.9.7)
49
+ ruby_dep (~> 1.2)
50
+ lumberjack (1.0.12)
51
+ method_source (0.9.0)
52
+ minitest (5.11.3)
53
+ multipart-post (2.0.0)
54
+ nenv (0.3.0)
55
+ notiffany (0.1.1)
56
+ nenv (~> 0.1)
57
+ shellany (~> 0.0)
58
+ pry (0.11.3)
59
+ coderay (~> 1.1.0)
60
+ method_source (~> 0.9.0)
61
+ rake (10.5.0)
62
+ rb-fsevent (0.10.2)
63
+ rb-inotify (0.9.10)
64
+ ffi (>= 0.5.0, < 2)
65
+ rspec (3.7.0)
66
+ rspec-core (~> 3.7.0)
67
+ rspec-expectations (~> 3.7.0)
68
+ rspec-mocks (~> 3.7.0)
69
+ rspec-core (3.7.0)
70
+ rspec-support (~> 3.7.0)
71
+ rspec-expectations (3.7.0)
72
+ diff-lcs (>= 1.2.0, < 2.0)
73
+ rspec-support (~> 3.7.0)
74
+ rspec-mocks (3.7.0)
75
+ diff-lcs (>= 1.2.0, < 2.0)
76
+ rspec-support (~> 3.7.0)
77
+ rspec-support (3.7.0)
78
+ ruby_dep (1.5.0)
79
+ shellany (0.0.1)
80
+ simplecov (0.15.1)
81
+ docile (~> 1.1.0)
82
+ json (>= 1.8, < 3)
83
+ simplecov-html (~> 0.10.0)
84
+ simplecov-html (0.10.2)
85
+ thor (0.20.0)
86
+ thread_safe (0.3.6)
87
+ tzinfo (1.2.5)
88
+ thread_safe (~> 0.1)
89
+ vault (0.10.1)
90
+
91
+ PLATFORMS
92
+ ruby
93
+
94
+ DEPENDENCIES
95
+ bundler (~> 1.16)
96
+ byebug
97
+ consult!
98
+ guard
99
+ guard-rspec
100
+ pry
101
+ rake (~> 10.0)
102
+ rspec (~> 3.0)
103
+ simplecov
104
+
105
+ BUNDLED WITH
106
+ 1.16.1
data/Guardfile ADDED
@@ -0,0 +1,42 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ ## Uncomment and set this to only include directories you want to watch
5
+ # directories %w(app lib config test spec features) \
6
+ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
+
8
+ ## Note: if you are using the `directories` clause above and you are not
9
+ ## watching the project directory ('.'), then you will want to move
10
+ ## the Guardfile to a watched dir and symlink it back, e.g.
11
+ #
12
+ # $ mkdir config
13
+ # $ mv Guardfile config/
14
+ # $ ln -s config/Guardfile .
15
+ #
16
+ # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
+
18
+ # Note: The cmd option is now required due to the increasing number of ways
19
+ # rspec may be run, below are examples of the most common uses.
20
+ # * bundler: 'bundle exec rspec'
21
+ # * bundler binstubs: 'bin/rspec'
22
+ # * spring: 'bin/rspec' (This will use spring if running and you have
23
+ # installed the spring binstubs per the docs)
24
+ # * zeus: 'zeus rspec' (requires the server to be started separately)
25
+ # * 'just' rspec: 'rspec'
26
+
27
+ guard :rspec, cmd: 'bundle exec rspec' do
28
+ require 'guard/rspec/dsl'
29
+ dsl = Guard::RSpec::Dsl.new(self)
30
+
31
+ # Feel free to open issues for suggestions and improvements
32
+
33
+ # RSpec files
34
+ rspec = dsl.rspec
35
+ watch(rspec.spec_helper) { rspec.spec_dir }
36
+ watch(rspec.spec_support) { rspec.spec_dir }
37
+ watch(rspec.spec_files)
38
+
39
+ # Ruby files
40
+ ruby = dsl.ruby
41
+ dsl.watch_spec_files_for(ruby.lib_files)
42
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Jeff Fraser
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/Procfile ADDED
@@ -0,0 +1,3 @@
1
+ consul: consul agent -dev
2
+ vault: vault server -dev -dev-root-token-id=94e1a9ed-5d72-5677-27ab-ebc485cca368
3
+ #guard: bundle exec guard
data/README.md ADDED
@@ -0,0 +1,289 @@
1
+ # Consult
2
+
3
+ Render configuration and secrets from [Consul] and [Vault] with ERB templates.
4
+
5
+ This gem is a spiritual sibling to [Consul Template], but specifically intended for use in Ruby or Rails environments. It does not have the same features as Consul Template; it is intended for simpler scenarios. Most importantly, leases and configuration changes are _not_ watched to automatically re-render. Consult is intended for more static or medium-to-long lived configuration.
6
+
7
+ If this gem is included in a Rails, the template will render on Rails boot. Because of this, configuration or credential changes can be picked up by restarting your app.
8
+
9
+ This gem is considered _beta_. At Veracross, we are just beginning to use it in staging environments.
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ ```ruby
16
+ gem 'consult'
17
+ ```
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install consult
26
+
27
+ ## Usage
28
+
29
+ Using Consult requires a configuration YAML file and a series of templates. The configuration file serves as a manifest of templates and their settings, along with optional connection settings to Vault and Consul.
30
+
31
+ ### Configuration
32
+
33
+ ```yaml
34
+ # The environment you are operating it. Defaults to ENV['RAILS_ENV'] or Rails.env if Rails is present.
35
+ env: test
36
+
37
+ # Optional
38
+ consul:
39
+ # Prefers `CONSUL_HTTP_ADDR` environment variable
40
+ address: http://0.0.0.0:8500
41
+ # Prefers `CONSUL_HTTP_TOKEN` environment variable, or a ~/.consul-token file.
42
+ # Setting a token here is not best practice because consul tokens should have a relatively short TTL
43
+ # and be read from the environment, but this is convenient for testing.
44
+ token: 5d3f1c66-d405-4ad1-b634-ea30be4fb539
45
+
46
+ # Optional
47
+ vault:
48
+ # Prefers `VAULT_ADDR` environment variable
49
+ address: http://0.0.0.0:8200
50
+ # Prefers `VAULT_TOKEN` environment variable, or a ~/.vault-token file
51
+ # Setting a token here is not best practice because vault tokens should have a relatively short TTL
52
+ # and be read from the environment, but this is convenient for testing.
53
+ token: 8fcd5aed-3eb9-412d-8923-1397af7aede2
54
+
55
+ # Enumerate the templates.
56
+ templates:
57
+ database:
58
+ # Relative paths are assumed to be in #{Rails.root}/config.
59
+ # Path to the template
60
+ path: templates/database.yml.erb
61
+ # Destination for the rendered template
62
+ dest: rendered/database.yml
63
+ # Which environments to render this template in
64
+ environments: all
65
+ # If the file is less than this old, do not re-render
66
+ ttl: 3600 # seconds
67
+
68
+ secrets:
69
+ path: templates/secrets.yml.erb
70
+ dest: rendered/secrets.yml
71
+ environments: test
72
+
73
+ should_be_excluded:
74
+ path: templates/fake.yml.erb
75
+ dest: rendered/fake.yml
76
+ environments: production # won't be rendered because it doesn't match `env` at the top
77
+ ```
78
+
79
+ ### Conventions
80
+
81
+ Because you can use this to fetch secrets or render out things like `database.yml`, you should delete `secrets.yml` and `database.yml` from your app and add them to `.gitignore`. Only keep your templates in source control.
82
+
83
+ ### Templates
84
+
85
+ Templates are ERB files, and as such can do anything ERB can do. However, Consult does provide a few helper functions.
86
+
87
+ Note that under the hood, Consult is using [Diplomat](https://github.com/WeAreFarmGeek/diplomat) and the [Vault Gem](https://github.com/hashicorp/vault-ruby). Consul objects are therefore Diplomat objects, and likewise Vault objects are Vault Gem objects. See their API docs for more information. Diplomat generally returns structs with title cased properties.
88
+
89
+ #### Consul Functions
90
+
91
+ **service(name)** - Fetch the nodes for the specified service.
92
+
93
+ ```yaml
94
+ <% service("redis").each do |node| %>
95
+ host: <%= node.Address %>
96
+ port: <%= node.ServicePort %>
97
+ <% end %>
98
+ ```
99
+
100
+ returns
101
+
102
+ host: redis1.local
103
+ port: 6379
104
+
105
+ **query(name_or_id, options: nil)** - Execute the specified prepared Query by name or ID
106
+
107
+ ```ruby
108
+ <% query('pg-production').tap do |result| %>
109
+ service: <%= result.Service %>
110
+ nodes:
111
+ <% result.Nodes.each do |node| %>
112
+ address: <%= node['Node']['Address']
113
+ <% end %>
114
+ <% end %>
115
+ ```
116
+
117
+ **query_nodes(name_or_id, options: nil)** - Return only the nodes from a prepared query
118
+
119
+ ```yml
120
+ <% query_nodes('pg-production').each do |node| %>
121
+ <%= node['Node'] %>:
122
+ host: <%= node['Address'] %>
123
+ datacenter: <%= node['Datacenter'] %>
124
+ <% end %>
125
+ ```
126
+
127
+ pg1:
128
+ host: 10.0.100.101
129
+ datacenter: us-east-1
130
+ pg2:
131
+ host: 10.0.100.102
132
+ datacenter: us-east-2
133
+
134
+ #### Vault Functions
135
+
136
+ **secret(path)** - Fetch a secret at the given path.
137
+
138
+ username: <%= secret('secret/credentials').data[:username] %>
139
+
140
+ yields
141
+
142
+ username: kylo.ren
143
+
144
+ **secrets(path)** - List all secrets at the given path
145
+
146
+ ```ruby
147
+ <% secrets('secret').each do |path| %>
148
+ <%= path %>
149
+ <% end %>
150
+ ```
151
+
152
+ yields
153
+
154
+ foo
155
+ bar
156
+ baz
157
+
158
+ #### Utility Functions
159
+
160
+ **timestamp** - Renders the current utc timestamp.
161
+
162
+ <%= timestamp %>
163
+
164
+ renders
165
+
166
+ 2018-02-23 14:20:29 UTC
167
+
168
+ **indent(string, level, separator = '\n')** - Indents a multi-line string by `level`
169
+
170
+ ```yml
171
+ keys:
172
+ multi_line: |
173
+ <%= indent secret('secret/keys/multi_line).data[:value], 4 %>
174
+ ```
175
+
176
+ renders
177
+
178
+ ```yml
179
+ keys:
180
+ multi_line: |
181
+ 30ada39cccf79aadbd1d870bc15f0086
182
+ 7ea8d734e81e9c6710faa15b0aff516c
183
+ 27778ab3b1e10db2028352f12c3c07bb
184
+ e7ec40d1e45834681b4dc3548230d1ca
185
+ ```
186
+
187
+ **with(whatever)** - takes `whatever` and yields it back. Equivalent to `tap`, but provided as a bridge from [Consul Template]/Go template conventions.
188
+
189
+ ```yml
190
+ <% with secret "secrets/credentials" do |s| %>
191
+ username: <%= s.data[:username] %>
192
+ password: <%= s.data[:password] %>
193
+ <% end %>
194
+ ```
195
+
196
+ #### More Full Examples
197
+
198
+ Render multiple servers into a database.yml file, keyed by their name.
199
+
200
+ ```yml
201
+ # database.yml.erb
202
+ <% service("postgres").each do |node| %>
203
+ '<%= node.Node %>':
204
+ host: <%= node.Address %>
205
+ port: <%= node.ServicePort %>
206
+ <%- with secret "secret/base/sql-server/#{node.Node}/web" do |s| -%>
207
+ # Credential lease good until <%= (timestamp + s.lease_duration).to_s %>
208
+ username: <%= s.data[:username] %>
209
+ password: <%= s.data[:password] %>
210
+ <% end -%>
211
+ <% end %>
212
+ ```
213
+
214
+ Yields something like
215
+
216
+ ```yml
217
+ # database.yml
218
+ 'db1':
219
+ host: 10.0.100.101
220
+ port: 5432
221
+ # Credential lease good until 2018-02-24 16:08:29 UTC
222
+ username: foo
223
+ password: bar
224
+ 'db2':
225
+ host: 10.0.100.102
226
+ port: 5432
227
+ # Credential lease good until 2018-02-24 16:08:29 UTC
228
+ username: baz
229
+ password: qux
230
+ ```
231
+
232
+ #### Secrets
233
+
234
+ ```yml
235
+ # secrets.yml.erb
236
+ shared:
237
+ rollbar_token: <%= secret('secrets/third_party').data[:rollbar] %>
238
+ scout_token: <%= secret('secrets/third_party').data[:scout] %>
239
+
240
+ development:
241
+ secret_key_base: abcd1234....
242
+
243
+ production:
244
+ secret_key_base: <%= secret('secret/apps/myapp').data[:secret_key_base] %>
245
+ ```
246
+
247
+ Then reference secrets in your app with `Rails.application.secrets`.
248
+
249
+ ```ruby
250
+ # config/intiializers/rollbar.rb
251
+ Rollbar.configure do |config|
252
+ config.access_token = Rails.application.secrets.rollbar_token
253
+ end
254
+ ```
255
+
256
+ ## Why we built this
257
+
258
+ We use [Consul Template] for server-level configuration, but application level configuration is more tricky. It is difficult to solve the problem of fetching secrets and config in a consistent way in both development and production. We wanted to avoid having [Consul Template] in use in production, but some other custom solution in development.
259
+
260
+ Using this gem, the implementation is the same in dev and prod, and it is frictionless since the templates render when Rails boots.
261
+
262
+ ## Development
263
+
264
+ After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment. See below for testing instructions.
265
+
266
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
267
+
268
+ ### Testing
269
+
270
+ Testing is easiest by running Consul and Vault in Docker. Just boot up their minimal containers:
271
+
272
+ $ docker pull consul
273
+ $ docker pull vault
274
+ $ docker run -d --name=dev-consul -p 8500:8500 consul
275
+ $ docker run -d --name=dev-vault -p 8200:8200 --cap-add=IPC_LOCK -e 'VAULT_DEV_ROOT_TOKEN_ID=94e1a9ed-5d72-5677-27ab-ebc485cca368' vault
276
+
277
+ Then run `bundle exec rspec`, or `bundle exec guard`.
278
+
279
+ ## Contributing
280
+
281
+ Bug reports and pull requests are welcome on GitHub at https://github.com/veracross/consult.
282
+
283
+ ## License
284
+
285
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
286
+
287
+ [Vault]: https://github.com/hashicorp/vault
288
+ [Consul]: https://github.com/hashicorp/consul
289
+ [Consul Template]: https://github.com/hashicorp/consul-template
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'consult'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+ Consult.load config_dir: 'spec/support'
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ require 'pry'
12
+ Pry.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+ # Do any other automated setup that you need to do here
data/consult.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'consult/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'consult'
9
+ spec.version = Consult::VERSION
10
+ spec.authors = ['Jeff Fraser']
11
+ spec.email = ['jeff.fraser@veracross.com']
12
+
13
+ spec.summary = 'Manage consul/vault backed template files in Ruby.'
14
+ spec.description = 'Manage consul/vault backed template files in Ruby.'
15
+ spec.homepage = 'https://github.com/veracross/consult'
16
+ spec.license = 'MIT'
17
+
18
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
19
+
20
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
21
+ f.match(%r{^(test|spec|features)/})
22
+ end
23
+ spec.bindir = 'exe'
24
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
+ spec.require_paths = ['lib']
26
+
27
+ spec.add_dependency 'activesupport', '> 4', '< 6'
28
+ spec.add_dependency 'diplomat'
29
+ spec.add_dependency 'vault'
30
+
31
+ spec.add_development_dependency 'bundler', '~> 1.16'
32
+ spec.add_development_dependency 'byebug'
33
+ spec.add_development_dependency 'guard'
34
+ spec.add_development_dependency 'guard-rspec'
35
+ spec.add_development_dependency 'pry'
36
+ spec.add_development_dependency 'rake', '~> 10.0'
37
+ spec.add_development_dependency 'rspec', '~> 3.0'
38
+ spec.add_development_dependency 'simplecov'
39
+ end
data/lib/consult.rb ADDED
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pathname'
4
+ require 'yaml'
5
+ require 'active_support/core_ext/hash'
6
+ require 'erb'
7
+ require 'vault'
8
+ require 'diplomat'
9
+
10
+ require 'consult/version'
11
+ require 'consult/utilities'
12
+ require 'consult/template'
13
+
14
+ module Consult
15
+ @config = {}
16
+ @templates = []
17
+
18
+ CONSUL_DISK_TOKEN = Pathname.new("#{Dir.home}/.consul-token").freeze
19
+
20
+ class << self
21
+ attr_reader :config, :templates
22
+
23
+ def load(config_dir: nil)
24
+ root directory: config_dir
25
+ yaml = root.join('config', 'consult.yml')
26
+ @config = yaml.exist? ? YAML.safe_load(ERB.new(yaml.read).result, [], [], true) : {}
27
+ @config.deep_symbolize_keys!
28
+ @templates = @config[:templates]&.map { |name, config| Template.new(name, config) }
29
+
30
+ configure_consul
31
+ configure_vault
32
+ end
33
+
34
+ def configure_consul
35
+ @config[:consul] ||= {}
36
+
37
+ # We map conventional `address` and `token` params to Diplomat's unconventional `url` and `acl_token` settings
38
+ # Additionally: prefer env vars over explicit config
39
+ configured_address = @config[:consul].delete(:address)
40
+ @config[:consul][:url] = ENV['CONSUL_HTTP_ADDR'] || configured_address || @config[:consul][:url]
41
+ @config[:consul][:acl_token] = consul_token
42
+
43
+ Diplomat.configure do |c|
44
+ @config[:consul].each do |opt, val|
45
+ c.send "#{opt}=".to_sym, val
46
+ end
47
+ end
48
+ end
49
+
50
+ def configure_vault
51
+ return unless @config.key? :vault
52
+
53
+ Vault.configure do |c|
54
+ @config[:vault].each do |opt, val|
55
+ c.send "#{opt}=".to_sym, val
56
+ end
57
+ end
58
+ end
59
+
60
+ def root(directory: nil)
61
+ @_root ||= directory ? Pathname.new(directory) : (!!defined?(Rails) && ::Rails.root)
62
+ end
63
+
64
+ def env
65
+ @config[:env] || ENV['RAILS_ENV'] || Rails.env
66
+ end
67
+
68
+ # Return only the templates that are relevant for the current environment
69
+ def active_templates
70
+ templates.select(&:should_render?)
71
+ end
72
+
73
+ # Render templates.
74
+ def render!
75
+ active_templates.each(&:render)
76
+ end
77
+
78
+ # Map more conventional `token` parameter to Diplomat's `acl_token` configuration.
79
+ # Additionally, we support ~/.consul-token, similar to Vault's support for ~/.vault-token
80
+ def consul_token
81
+ ENV['CONSUL_HTTP_TOKEN'] ||
82
+ @config[:consul].delete(:token) ||
83
+ @config[:consul][:acl_token] ||
84
+ CONSUL_DISK_TOKEN.read.chomp
85
+ end
86
+ end
87
+ end
88
+
89
+ require 'consult/rails/engine' if defined?(Rails)
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Consult
4
+ module Rails
5
+ # When using Rails, this will render your templates
6
+ # on app boot.
7
+ class Railtie < ::Rails::Railtie
8
+ config.before_configuration do
9
+ Consult.load
10
+ Consult.render!
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'consult/template_functions'
4
+
5
+ module Consult
6
+ class Template
7
+ include Utilities
8
+ include TemplateFunctions
9
+
10
+ attr_reader :name, :config
11
+
12
+ def initialize(name, config)
13
+ @name = name
14
+ @config = config
15
+ end
16
+
17
+ def render(save: true)
18
+ renderer = ERB.new(File.read(path, encoding: 'utf-8'), nil, '-')
19
+ result = renderer.result(binding)
20
+
21
+ File.open(dest, 'w') { |f| f << result } if save
22
+ result
23
+ rescue StandardError => e
24
+ puts "Error rendering template: #{name}"
25
+ raise e
26
+ end
27
+
28
+ def path
29
+ resolve @config.fetch(:path)
30
+ end
31
+
32
+ def dest
33
+ resolve @config.fetch(:dest)
34
+ end
35
+
36
+ def should_render?
37
+ (@config[:environments] == 'all' || @config[:environments].include?(Consult.env)) && expired?
38
+ end
39
+
40
+ def expired?
41
+ # Treat renders as expired if a TTL isn't set, or it has never been rendered before
42
+ return true if !config.key?(:ttl) || !dest.exist?
43
+ dest.mtime < (Time.now - @config[:ttl].to_i)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Consult
4
+ module TemplateFunctions
5
+ ############
6
+ # Vault
7
+ ############
8
+ def secret(path)
9
+ Vault.logical.read(path)
10
+ end
11
+
12
+ def secrets(path)
13
+ Vault.logical.list(path)
14
+ end
15
+
16
+ ############
17
+ # Consul
18
+ ############
19
+ def service(key, scope: :all, options: nil, meta: nil)
20
+ Diplomat::Service.get(key, scope, options, meta)
21
+ end
22
+
23
+ # Execute a prepared query
24
+ def query(name_or_id, options: nil)
25
+ Diplomat::Query.execute(name_or_id, options)
26
+ end
27
+
28
+ # Return just the nodes from a prepared query
29
+ def query_nodes(*args)
30
+ query(*args)&.Nodes&.map { |node| node['Node'] }
31
+ end
32
+
33
+ ############
34
+ # Utility
35
+ ############
36
+ # Provided as a bridge to consul-template/go conventions. Simply yields
37
+ # back whatever was provided.
38
+ def with(whatever)
39
+ yield whatever
40
+ end
41
+
42
+ def timestamp
43
+ Time.now.utc
44
+ end
45
+
46
+ # Indent a multi-line string by the provided amount.
47
+ def indent(string, level, separator = "\n")
48
+ string.split(separator).map do |line|
49
+ ' ' * level + line
50
+ end.join(separator)
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Consult
4
+ module Utilities
5
+ def resolve(path)
6
+ pathname = Pathname.new(path)
7
+ pathname.relative? ? Consult.root.join(pathname) : pathname
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module Consult
2
+ VERSION = '0.5.0'
3
+ end
metadata ADDED
@@ -0,0 +1,225 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: consult
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Jeff Fraser
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-02-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">"
18
+ - !ruby/object:Gem::Version
19
+ version: '4'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '6'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">"
28
+ - !ruby/object:Gem::Version
29
+ version: '4'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '6'
33
+ - !ruby/object:Gem::Dependency
34
+ name: diplomat
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: vault
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: bundler
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.16'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '1.16'
75
+ - !ruby/object:Gem::Dependency
76
+ name: byebug
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ - !ruby/object:Gem::Dependency
90
+ name: guard
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ - !ruby/object:Gem::Dependency
104
+ name: guard-rspec
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ - !ruby/object:Gem::Dependency
118
+ name: pry
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ - !ruby/object:Gem::Dependency
132
+ name: rake
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '10.0'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '10.0'
145
+ - !ruby/object:Gem::Dependency
146
+ name: rspec
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '3.0'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '3.0'
159
+ - !ruby/object:Gem::Dependency
160
+ name: simplecov
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - ">="
171
+ - !ruby/object:Gem::Version
172
+ version: '0'
173
+ description: Manage consul/vault backed template files in Ruby.
174
+ email:
175
+ - jeff.fraser@veracross.com
176
+ executables: []
177
+ extensions: []
178
+ extra_rdoc_files: []
179
+ files:
180
+ - ".gitignore"
181
+ - ".rspec"
182
+ - ".rubocop.yml"
183
+ - ".travis.yml"
184
+ - Gemfile
185
+ - Gemfile.lock
186
+ - Guardfile
187
+ - LICENSE.txt
188
+ - Procfile
189
+ - README.md
190
+ - Rakefile
191
+ - bin/console
192
+ - bin/setup
193
+ - consult.gemspec
194
+ - lib/consult.rb
195
+ - lib/consult/rails/engine.rb
196
+ - lib/consult/template.rb
197
+ - lib/consult/template_functions.rb
198
+ - lib/consult/utilities.rb
199
+ - lib/consult/version.rb
200
+ homepage: https://github.com/veracross/consult
201
+ licenses:
202
+ - MIT
203
+ metadata:
204
+ allowed_push_host: https://rubygems.org
205
+ post_install_message:
206
+ rdoc_options: []
207
+ require_paths:
208
+ - lib
209
+ required_ruby_version: !ruby/object:Gem::Requirement
210
+ requirements:
211
+ - - ">="
212
+ - !ruby/object:Gem::Version
213
+ version: '0'
214
+ required_rubygems_version: !ruby/object:Gem::Requirement
215
+ requirements:
216
+ - - ">="
217
+ - !ruby/object:Gem::Version
218
+ version: '0'
219
+ requirements: []
220
+ rubyforge_project:
221
+ rubygems_version: 2.7.3
222
+ signing_key:
223
+ specification_version: 4
224
+ summary: Manage consul/vault backed template files in Ruby.
225
+ test_files: []