berkeley_library-docker 0.1.1 → 0.2.0.pre.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +8 -2
- data/lib/berkeley_library/docker/module_info.rb +1 -1
- data/lib/berkeley_library/docker/railtie.rb +21 -3
- data/lib/berkeley_library/docker/secret.rb +33 -16
- data/spec/berkeley_library/docker/railtie_spec.rb +23 -23
- data/spec/berkeley_library/docker/secret_spec.rb +20 -17
- data/spec/berkeley_library/docker_spec.rb +1 -1
- data/spec/spec_helper.rb +4 -2
- data/spec/spec_utils.rb +6 -6
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5fccf11c3452690216d316ab64be7b2e57dc82a51b9d384ec9b1feb0419f7947
|
4
|
+
data.tar.gz: dad729f07dcdc468b82d2ff72d10cfc19899bbea8ef67d06158c1f94722cb0ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3fcb0b7e40466acef102e32c380db573b7463123d91b5673961654fa449e77a39fcf6f5fce75d9b6d1ab97ce048d7398a90cc431dd02a4b6fffac6377d493d35
|
7
|
+
data.tar.gz: 9a27223955e09e2d76eeecde8b3958821b749676d3d948508a89cb24c4c03b4bf432b2b94b58acfcf6c7208f462a30951ad062bd0e91326ab5520e94f1fa8e8f
|
data/README.md
CHANGED
@@ -7,14 +7,20 @@ require 'berkeley_library/docker'
|
|
7
7
|
BerkeleyLibrary::Docker::Secret.load_secrets!
|
8
8
|
```
|
9
9
|
|
10
|
-
|
10
|
+
---
|
11
|
+
|
12
|
+
> **Note on (Rails) load order:** This gem loads the environment using Rails' `before_configuration` hook, a la the [dotenv](https://github.com/bkeepers/dotenv#note-on-load-order) gem. Manually call `BerkeleyLibrary::Docker::Secret.load_secrets!` if you need to load secrets earlier.
|
13
|
+
|
14
|
+
---
|
15
|
+
|
16
|
+
Secrets are loaded from `/run/secrets` by default. You can override this by passing another path (or glob pattern) to the `load_secrets!` method, or by setting the `UCBLIB_SECRETS_PATTERN` environment variable. For example:
|
11
17
|
|
12
18
|
```ruby
|
13
19
|
# Load any file under /tmp/secrets
|
14
20
|
BerkeleyLibrary::Docker::Secret.load_secrets! '/tmp/secrets/**/*'
|
15
21
|
|
16
22
|
# Load only a specific file
|
17
|
-
ENV['
|
23
|
+
ENV['UCBLIB_SECRETS_PATTERN'] = '/run/secrets/DB_PASSWORD'
|
18
24
|
BerkeleyLibrary::Docker::Secret.load_secrets!
|
19
25
|
```
|
20
26
|
|
@@ -7,7 +7,7 @@ module BerkeleyLibrary
|
|
7
7
|
SUMMARY = 'Utility functions for Dockerizing Ruby apps'.freeze
|
8
8
|
DESCRIPTION = 'Utility functions for making Ruby apps "just work" in Docker containers.'.freeze
|
9
9
|
LICENSE = 'MIT'.freeze
|
10
|
-
VERSION = '0.
|
10
|
+
VERSION = '0.2.0-rc2'.freeze
|
11
11
|
HOMEPAGE = 'https://github.com/BerkeleyLibrary/docker'.freeze
|
12
12
|
|
13
13
|
private_class_method :new
|
@@ -2,10 +2,28 @@ require 'berkeley_library/docker/secret'
|
|
2
2
|
|
3
3
|
module BerkeleyLibrary
|
4
4
|
module Docker
|
5
|
-
class Railtie < Rails::Railtie
|
6
|
-
|
5
|
+
class Railtie < ::Rails::Railtie
|
6
|
+
include Logging
|
7
7
|
|
8
|
-
|
8
|
+
def secrets
|
9
|
+
@secrets ||= {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def load_secrets!
|
13
|
+
@secrets = Secret.load_secrets!
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
config.before_configuration { load_secrets! }
|
19
|
+
|
20
|
+
# @note Logging occurs later because the Rails logger, which we use to
|
21
|
+
# bootstrap ours, isn't initialized until after `before_configuration`.
|
22
|
+
initializer 'berkeley_library-docker.log_loaded_secrets' do
|
23
|
+
secrets.each do |_, secret|
|
24
|
+
log_info "Loaded secret: #{secret.inspect}"
|
25
|
+
end
|
26
|
+
end
|
9
27
|
end
|
10
28
|
end
|
11
29
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'berkeley_library/docker/logging'
|
2
|
+
require 'digest'
|
3
|
+
require 'time'
|
2
4
|
|
3
5
|
module BerkeleyLibrary
|
4
6
|
module Docker
|
@@ -6,31 +8,46 @@ module BerkeleyLibrary
|
|
6
8
|
class << self
|
7
9
|
include Logging
|
8
10
|
|
9
|
-
PATH_OVERRIDE_ENVVAR = '
|
10
|
-
|
11
|
+
PATH_OVERRIDE_ENVVAR = 'UCBLIB_SECRETS_PATTERN'
|
12
|
+
DEFAULT_SECRETS_PATTERN = '/run/secrets/*'
|
11
13
|
|
12
|
-
def load_secrets!(glob = nil)
|
13
|
-
|
14
|
+
def load_secrets!(glob = nil, reload = false)
|
15
|
+
glob = normalize_glob(glob)
|
16
|
+
|
17
|
+
files_from(glob).each_with_object({}) do |path, new_secrets|
|
18
|
+
secret_name = File.basename(path)
|
19
|
+
secret_value = File.read(path).strip
|
20
|
+
|
21
|
+
ENV[secret_name] = secret_value
|
22
|
+
|
23
|
+
new_secrets[secret_name] = {
|
24
|
+
name: secret_name,
|
25
|
+
file: path,
|
26
|
+
checksum: "sha256:#{Digest::SHA256.hexdigest(secret_value)}",
|
27
|
+
glob: glob,
|
28
|
+
timestamp: Time.now.to_i,
|
29
|
+
}
|
30
|
+
end
|
14
31
|
end
|
15
32
|
|
16
33
|
private
|
17
34
|
|
18
|
-
def
|
19
|
-
|
20
|
-
ENV[secret] = File.read(filepath).strip
|
21
|
-
log_info "Loaded secret `ENV['#{secret}']` from #{filepath}"
|
35
|
+
def files_from(glob)
|
36
|
+
Dir[glob].filter_map { |fname| fname if File.file? fname }
|
22
37
|
end
|
23
38
|
|
24
|
-
def
|
25
|
-
glob
|
26
|
-
|
39
|
+
def normalize_glob(glob)
|
40
|
+
(glob || ENV[PATH_OVERRIDE_ENVVAR] || DEFAULT_SECRETS_PATTERN)
|
41
|
+
.then(&:strip)
|
42
|
+
.then(&method(:ensure_dir_asterisk))
|
43
|
+
end
|
27
44
|
|
28
|
-
|
29
|
-
|
45
|
+
def ensure_dir_asterisk(glob)
|
46
|
+
if !glob.end_with?('*') && File.directory?(glob)
|
47
|
+
File.join(glob, '*')
|
48
|
+
else
|
49
|
+
glob
|
30
50
|
end
|
31
|
-
|
32
|
-
log_info "Searching '#{glob}' for secrets files"
|
33
|
-
Dir[glob].filter_map { |fname| fname if File.file? fname }
|
34
51
|
end
|
35
52
|
end
|
36
53
|
end
|
@@ -1,36 +1,36 @@
|
|
1
|
-
require 'rails' # require Rails first to mimic load order
|
2
|
-
require 'berkeley_library/docker/railtie'
|
3
1
|
require 'spec_helper'
|
2
|
+
require 'rails'
|
3
|
+
require 'berkeley_library/docker/railtie'
|
4
|
+
require 'logger'
|
4
5
|
|
5
6
|
module BerkeleyLibrary
|
6
7
|
module Docker
|
7
8
|
describe Railtie do
|
8
|
-
|
9
|
+
# @note Simply defining the app class causes railtie
|
10
|
+
# `before_configuration` blocks to run, and we only get to define one
|
11
|
+
# application, hence this one test needs to handle everything.
|
12
|
+
it 'causes rails to load the environment' do
|
13
|
+
with_secret('FOOBAR', 'BAZ') do
|
14
|
+
with_secret('CLIENT_KEY', 'd33db55f') do
|
15
|
+
logger = spy Logger.new(STDOUT)
|
9
16
|
|
10
|
-
|
11
|
-
let(:initializers) { app.initializers.tsort_each.collect(&:name) }
|
12
|
-
let(:railtie_name) { BerkeleyLibrary::Docker::Railtie::NAME }
|
17
|
+
expect(ENV['CLIENT_KEY']).to be nil
|
13
18
|
|
14
|
-
|
15
|
-
|
16
|
-
|
19
|
+
# Use Class.new so that `logger` is within scope
|
20
|
+
@klass = Class.new(Rails::Application) do
|
21
|
+
Rails.logger = logger
|
22
|
+
config.client_key = ENV['CLIENT_KEY']
|
23
|
+
end
|
17
24
|
|
18
|
-
|
19
|
-
with_secret('API_TOKEN', 'd33db55f') do
|
20
|
-
app = TestApp.create
|
21
|
-
expect(ENV['API_TOKEN']).to be nil
|
25
|
+
expect(ENV['CLIENT_KEY']).to eq 'd33db55f'
|
22
26
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|
27
|
+
app = @klass.create
|
28
|
+
app.initialize!
|
27
29
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
expect(railtie_index).to be_between(logger_index, config_index)
|
30
|
+
expect(app.config.client_key).to eq 'd33db55f'
|
31
|
+
expect(logger).to have_received(:info).twice.with(/Loaded secret:.*/)
|
32
|
+
end
|
33
|
+
end
|
34
34
|
end
|
35
35
|
end
|
36
36
|
end
|
@@ -7,42 +7,45 @@ module BerkeleyLibrary
|
|
7
7
|
describe Secret do
|
8
8
|
it 'loads secrets into the environment' do
|
9
9
|
with_secret('DB_PASSWORD', 'f00BarbAz') do
|
10
|
-
expect
|
11
|
-
|
12
|
-
|
10
|
+
expect { Secret.load_secrets! }
|
11
|
+
.to change { ENV['DB_PASSWORD'] }
|
12
|
+
.from(nil).to('f00BarbAz')
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
16
16
|
it 'reads multiline secrets' do
|
17
17
|
with_secret('SSH_PRIVATE_KEY', "d33db55f\nd33db55f") do
|
18
|
-
expect
|
19
|
-
|
20
|
-
|
18
|
+
expect { Secret.load_secrets! }
|
19
|
+
.to change { ENV['SSH_PRIVATE_KEY'] }
|
20
|
+
.from(nil).to("d33db55f\nd33db55f")
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
it 'strips trailing whitespace' do
|
25
25
|
with_secret('API_TOKEN', "d33db55f\n") do
|
26
|
-
expect
|
27
|
-
|
28
|
-
|
26
|
+
expect { Secret.load_secrets! }
|
27
|
+
.to change { ENV['API_TOKEN'] }
|
28
|
+
.from(nil).to('d33db55f')
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
it 'ignores subdirectories in the secrets directory' do
|
33
33
|
FileUtils.mkdir_p(File.join(SPEC_SECRETS_PATH, 'some-dir'))
|
34
|
-
expect{ Secret.load_secrets! }.not_to raise_error
|
34
|
+
expect { Secret.load_secrets! }.not_to raise_error
|
35
|
+
expect { Secret.load_secrets! }.not_to change { ENV }
|
35
36
|
end
|
36
37
|
|
37
|
-
it 'searches ENV["
|
38
|
-
|
39
|
-
Secret.load_secrets!
|
40
|
-
expect(ENV['MASTER_KEY']).to be nil
|
38
|
+
it 'searches ENV["UCBLIB_SECRETS_PATTERN"] by default' do
|
39
|
+
secrets_subdir = File.join(SPEC_SECRETS_PATH, 'super-secrets')
|
41
40
|
|
42
|
-
|
41
|
+
with_secret('MASTER_KEY', 'd33db55f', secrets_subdir) do
|
42
|
+
expect { Secret.load_secrets! }.not_to change { ENV }
|
43
43
|
|
44
|
-
|
45
|
-
|
44
|
+
ENV['UCBLIB_SECRETS_PATTERN'] = secrets_subdir
|
45
|
+
|
46
|
+
expect { Secret.load_secrets! }
|
47
|
+
.to change { ENV['MASTER_KEY'] }
|
48
|
+
.from(nil).to('d33db55f')
|
46
49
|
end
|
47
50
|
end
|
48
51
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
require_relative './spec_utils.rb'
|
2
|
+
require 'tmpdir'
|
2
3
|
|
3
|
-
# Avoid issues with `/run` being read-only on MacOS
|
4
|
-
|
4
|
+
# Avoid issues with `/run` being read-only on MacOS. Also ensures that
|
5
|
+
# tests always start with a clean secrets directory.
|
6
|
+
SPEC_SECRETS_PATH = ENV['UCBLIB_SECRETS_PATTERN'] = Dir.mktmpdir
|
5
7
|
|
6
8
|
RSpec.configure do |config|
|
7
9
|
config.include SpecUtils
|
data/spec/spec_utils.rb
CHANGED
@@ -3,12 +3,12 @@ require 'fileutils'
|
|
3
3
|
|
4
4
|
module SpecUtils
|
5
5
|
def rollback_environment(&block)
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
ENV.to_h.tap do |old_env|
|
7
|
+
begin
|
8
|
+
yield
|
9
|
+
ensure
|
10
|
+
ENV.replace(old_env)
|
11
|
+
end
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: berkeley_library-docker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0.pre.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dan Schmidt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-11-
|
11
|
+
date: 2022-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -120,9 +120,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
120
120
|
version: 2.7.6
|
121
121
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
122
|
requirements:
|
123
|
-
- - "
|
123
|
+
- - ">"
|
124
124
|
- !ruby/object:Gem::Version
|
125
|
-
version:
|
125
|
+
version: 1.3.1
|
126
126
|
requirements: []
|
127
127
|
rubygems_version: 3.1.6
|
128
128
|
signing_key:
|