chamber 1.0.3 → 2.0.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 +288 -173
- data/bin/chamber +2 -288
- data/lib/chamber.rb +19 -67
- data/lib/chamber/binary/heroku.rb +59 -0
- data/lib/chamber/binary/runner.rb +94 -0
- data/lib/chamber/binary/travis.rb +23 -0
- data/lib/chamber/commands/base.rb +33 -0
- data/lib/chamber/commands/comparable.rb +37 -0
- data/lib/chamber/commands/compare.rb +46 -0
- data/lib/chamber/commands/context_resolver.rb +72 -0
- data/lib/chamber/commands/files.rb +12 -0
- data/lib/chamber/commands/heroku.rb +30 -0
- data/lib/chamber/commands/heroku/clear.rb +25 -0
- data/lib/chamber/commands/heroku/compare.rb +31 -0
- data/lib/chamber/commands/heroku/pull.rb +30 -0
- data/lib/chamber/commands/heroku/push.rb +25 -0
- data/lib/chamber/commands/initialize.rb +73 -0
- data/lib/chamber/commands/securable.rb +48 -0
- data/lib/chamber/commands/secure.rb +16 -0
- data/lib/chamber/commands/show.rb +23 -0
- data/lib/chamber/commands/travis.rb +14 -0
- data/lib/chamber/commands/travis/secure.rb +35 -0
- data/lib/chamber/configuration.rb +34 -0
- data/lib/chamber/environmentable.rb +23 -0
- data/lib/chamber/errors/undecryptable_value_error.rb +6 -0
- data/lib/chamber/file.rb +17 -5
- data/lib/chamber/file_set.rb +18 -12
- data/lib/chamber/filters/boolean_conversion_filter.rb +41 -0
- data/lib/chamber/filters/decryption_filter.rb +69 -0
- data/lib/chamber/filters/encryption_filter.rb +57 -0
- data/lib/chamber/filters/environment_filter.rb +75 -0
- data/lib/chamber/filters/namespace_filter.rb +37 -0
- data/lib/chamber/filters/secure_filter.rb +39 -0
- data/lib/chamber/instance.rb +40 -0
- data/lib/chamber/namespace_set.rb +55 -16
- data/lib/chamber/rails/railtie.rb +1 -1
- data/lib/chamber/settings.rb +103 -42
- data/lib/chamber/system_environment.rb +3 -93
- data/lib/chamber/version.rb +2 -2
- data/spec/fixtures/settings.yml +27 -0
- data/spec/lib/chamber/commands/context_resolver_spec.rb +106 -0
- data/spec/lib/chamber/commands/files_spec.rb +19 -0
- data/spec/lib/chamber/commands/heroku/clear_spec.rb +11 -0
- data/spec/lib/chamber/commands/heroku/compare_spec.rb +11 -0
- data/spec/lib/chamber/commands/heroku/pull_spec.rb +11 -0
- data/spec/lib/chamber/commands/heroku/push_spec.rb +11 -0
- data/spec/lib/chamber/commands/secure_spec.rb +29 -0
- data/spec/lib/chamber/commands/show_spec.rb +43 -0
- data/spec/lib/chamber/file_set_spec.rb +1 -1
- data/spec/lib/chamber/file_spec.rb +32 -9
- data/spec/lib/chamber/filters/boolean_conversion_filter_spec.rb +44 -0
- data/spec/lib/chamber/filters/decryption_filter_spec.rb +55 -0
- data/spec/lib/chamber/filters/encryption_filter_spec.rb +48 -0
- data/spec/lib/chamber/filters/environment_filter_spec.rb +35 -0
- data/spec/lib/chamber/filters/namespace_filter_spec.rb +73 -0
- data/spec/lib/chamber/filters/secure_filter_spec.rb +36 -0
- data/spec/lib/chamber/namespace_set_spec.rb +61 -18
- data/spec/lib/chamber/settings_spec.rb +99 -23
- data/spec/lib/chamber/system_environment_spec.rb +1 -71
- data/spec/lib/chamber_spec.rb +40 -26
- data/spec/rails-2-test/config.ru +0 -0
- data/spec/rails-2-test/config/application.rb +5 -0
- data/spec/rails-2-test/script/console +0 -0
- data/spec/rails-3-test/config.ru +0 -0
- data/spec/rails-3-test/config/application.rb +5 -0
- data/spec/rails-3-test/script/rails +0 -0
- data/spec/rails-4-test/bin/rails +0 -0
- data/spec/rails-4-test/config.ru +0 -0
- data/spec/rails-4-test/config/application.rb +5 -0
- data/spec/spec_key +27 -0
- data/spec/spec_key.pub +9 -0
- metadata +85 -4
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'chamber/commands/base'
|
2
|
+
require 'chamber/commands/securable'
|
3
|
+
require 'chamber/commands/heroku'
|
4
|
+
|
5
|
+
module Chamber
|
6
|
+
module Commands
|
7
|
+
module Heroku
|
8
|
+
class Push < Chamber::Commands::Base
|
9
|
+
include Chamber::Commands::Securable
|
10
|
+
include Chamber::Commands::Heroku
|
11
|
+
|
12
|
+
def call
|
13
|
+
securable_environment_variables.each do |key, value|
|
14
|
+
if dry_run
|
15
|
+
shell.say_status 'push', key, :blue
|
16
|
+
else
|
17
|
+
shell.say_status 'push', key, :green
|
18
|
+
shell.say heroku(%Q{config:set #{key}=#{value}})
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'chamber/commands/base'
|
3
|
+
|
4
|
+
module Chamber
|
5
|
+
module Commands
|
6
|
+
class Initialize < Chamber::Commands::Base
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
super
|
10
|
+
|
11
|
+
self.basepath = options[:basepath]
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
shell.create_file private_key_filepath, rsa_private_key.to_pem
|
16
|
+
shell.create_file public_key_filepath, rsa_public_key.to_pem
|
17
|
+
|
18
|
+
`chmod 600 #{private_key_filepath}`
|
19
|
+
`chmod 644 #{public_key_filepath}`
|
20
|
+
|
21
|
+
unless ::File.read(gitignore_filepath).match(/^.chamber.pem$/)
|
22
|
+
shell.append_to_file gitignore_filepath, private_key_filepath.basename
|
23
|
+
end
|
24
|
+
|
25
|
+
shell.copy_file settings_template_filepath, settings_filepath
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.call(options = {})
|
29
|
+
self.new(options).call
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
attr_accessor :basepath
|
35
|
+
|
36
|
+
def settings_template_filepath
|
37
|
+
@settings_template_filepath ||= templates_path + 'settings.yml'
|
38
|
+
end
|
39
|
+
|
40
|
+
def templates_path
|
41
|
+
@templates_path ||= Pathname.new(::File.expand_path('../../../../templates', __FILE__))
|
42
|
+
end
|
43
|
+
|
44
|
+
def settings_filepath
|
45
|
+
@settings_filepath ||= basepath + 'settings.yml'
|
46
|
+
end
|
47
|
+
|
48
|
+
def gitignore_filepath
|
49
|
+
@gitignore_filepath ||= rootpath + '.gitignore'
|
50
|
+
end
|
51
|
+
|
52
|
+
def private_key_filepath
|
53
|
+
@private_key_filepath ||= rootpath + '.chamber.pem'
|
54
|
+
end
|
55
|
+
|
56
|
+
def public_key_filepath
|
57
|
+
@public_key_filepath ||= rootpath + '.chamber.pub.pem'
|
58
|
+
end
|
59
|
+
|
60
|
+
def rsa_key
|
61
|
+
@rsa_key ||= OpenSSL::PKey::RSA.new(2048)
|
62
|
+
end
|
63
|
+
|
64
|
+
def rsa_private_key
|
65
|
+
rsa_key
|
66
|
+
end
|
67
|
+
|
68
|
+
def rsa_public_key
|
69
|
+
rsa_key.public_key
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
require 'chamber/instance'
|
3
|
+
|
4
|
+
module Chamber
|
5
|
+
module Commands
|
6
|
+
module Securable
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
super
|
10
|
+
|
11
|
+
ignored_settings_options = options.
|
12
|
+
merge(files: ignored_settings_filepaths).
|
13
|
+
reject { |k, v| k == 'basepath' }
|
14
|
+
self.ignored_settings_instance = Chamber::Instance.new(ignored_settings_options)
|
15
|
+
self.all_settings_instance = Chamber::Instance.new(options)
|
16
|
+
self.only_sensitive = options[:only_sensitive]
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
attr_accessor :only_sensitive,
|
22
|
+
:ignored_settings_instance,
|
23
|
+
:all_settings_instance
|
24
|
+
|
25
|
+
def securable_environment_variables
|
26
|
+
if only_sensitive
|
27
|
+
secured_settings.to_environment
|
28
|
+
else
|
29
|
+
all_settings.to_environment
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def secured_settings
|
34
|
+
ignored_settings_instance.settings.merge(all_settings.secured)
|
35
|
+
end
|
36
|
+
|
37
|
+
def all_settings
|
38
|
+
all_settings_instance.settings
|
39
|
+
end
|
40
|
+
|
41
|
+
def ignored_settings_filepaths
|
42
|
+
shell_escaped_chamber_filenames = chamber.filenames.map { |filename| Shellwords.escape(filename) }
|
43
|
+
|
44
|
+
`git ls-files --other --ignored --exclude-from=.gitignore | sed -e "s|^|#{Shellwords.escape(rootpath)}/|" | grep --colour=never -E '#{shell_escaped_chamber_filenames.join('|')}'`.split("\n")
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'chamber/commands/base'
|
2
|
+
|
3
|
+
module Chamber
|
4
|
+
module Commands
|
5
|
+
class Secure < Chamber::Commands::Base
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
super(options.merge(namespaces: ['*']))
|
9
|
+
end
|
10
|
+
|
11
|
+
def call
|
12
|
+
chamber.secure
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'chamber/commands/base'
|
3
|
+
|
4
|
+
module Chamber
|
5
|
+
module Commands
|
6
|
+
class Show < Chamber::Commands::Base
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
super
|
10
|
+
|
11
|
+
self.as_env = options[:as_env]
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
as_env ? chamber.to_s(pair_separator: "\n") : PP.pp(chamber.to_hash, StringIO.new, 60).string.chomp
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
|
20
|
+
attr_accessor :as_env
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'chamber/commands/base'
|
2
|
+
require 'chamber/commands/travis'
|
3
|
+
require 'chamber/commands/securable'
|
4
|
+
|
5
|
+
module Chamber
|
6
|
+
module Commands
|
7
|
+
module Travis
|
8
|
+
class Secure < Chamber::Commands::Base
|
9
|
+
include Chamber::Commands::Travis
|
10
|
+
include Chamber::Commands::Securable
|
11
|
+
|
12
|
+
def call
|
13
|
+
securable_environment_variables.each do |key, value|
|
14
|
+
if dry_run
|
15
|
+
shell.say_status 'encrypt', key, :blue
|
16
|
+
else
|
17
|
+
command = first_environment_variable?(key) ? '--override' : '--append'
|
18
|
+
|
19
|
+
shell.say_status 'encrypt', key, :green
|
20
|
+
travis_encrypt("#{command} #{key}=#{value}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
def first_environment_variable?(key)
|
28
|
+
@first_environment_key ||= securable_environment_variables.first[0]
|
29
|
+
|
30
|
+
@first_environment_key == key
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Chamber
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :basepath,
|
4
|
+
:decryption_key,
|
5
|
+
:encryption_key,
|
6
|
+
:files,
|
7
|
+
:namespaces
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
self.basepath = options[:basepath] || ''
|
11
|
+
self.namespaces = options[:namespaces] || []
|
12
|
+
self.decryption_key = options[:decryption_key]
|
13
|
+
self.encryption_key = options[:encryption_key]
|
14
|
+
self.files = options[:files] || [
|
15
|
+
self.basepath + 'credentials*.yml',
|
16
|
+
self.basepath + 'settings*.yml',
|
17
|
+
self.basepath + 'settings' ]
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_hash
|
21
|
+
{
|
22
|
+
basepath: self.basepath,
|
23
|
+
decryption_key: self.decryption_key,
|
24
|
+
encryption_key: self.encryption_key,
|
25
|
+
files: self.files,
|
26
|
+
namespaces: self.namespaces,
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def basepath=(pathlike)
|
31
|
+
@basepath = pathlike == '' ? '' : Pathname.new(::File.expand_path(pathlike))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'hashie/mash'
|
2
|
+
|
3
|
+
module Chamber
|
4
|
+
module Environmentable
|
5
|
+
def with_environment(settings, parent_keys, hash_block, value_block)
|
6
|
+
environment_hash = Hashie::Mash.new
|
7
|
+
|
8
|
+
settings.each_pair do |key, value|
|
9
|
+
environment_keys = parent_keys.dup.push(key)
|
10
|
+
|
11
|
+
if value.respond_to? :each_pair
|
12
|
+
environment_hash.merge!(hash_block.call(key, value, environment_keys))
|
13
|
+
else
|
14
|
+
environment_key = environment_keys.join('_').upcase
|
15
|
+
|
16
|
+
environment_hash.merge!(value_block.call(key, value, environment_key))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
environment_hash
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/chamber/file.rb
CHANGED
@@ -6,7 +6,7 @@ require 'erb'
|
|
6
6
|
# Internal: Represents a single file containing settings information in a given
|
7
7
|
# file set.
|
8
8
|
#
|
9
|
-
|
9
|
+
module Chamber
|
10
10
|
class File < Pathname
|
11
11
|
|
12
12
|
###
|
@@ -37,7 +37,9 @@ class File < Pathname
|
|
37
37
|
# # => <Chamber::File>
|
38
38
|
#
|
39
39
|
def initialize(options = {})
|
40
|
-
self.namespaces
|
40
|
+
self.namespaces = options[:namespaces] || {}
|
41
|
+
self.decryption_key = options[:decryption_key]
|
42
|
+
self.encryption_key = options[:encryption_key]
|
41
43
|
|
42
44
|
super options.fetch(:path)
|
43
45
|
end
|
@@ -62,13 +64,23 @@ class File < Pathname
|
|
62
64
|
# ```
|
63
65
|
#
|
64
66
|
def to_settings
|
65
|
-
@data ||= Settings.new(settings:
|
66
|
-
namespaces:
|
67
|
+
@data ||= Settings.new(settings: file_contents_hash,
|
68
|
+
namespaces: namespaces,
|
69
|
+
decryption_key: decryption_key,
|
70
|
+
encryption_key: encryption_key)
|
71
|
+
end
|
72
|
+
|
73
|
+
def secure
|
74
|
+
secure_settings = to_settings.secure
|
75
|
+
|
76
|
+
::File.open(self, 'w') { |file| file.write YAML.dump(secure_settings.to_hash) }
|
67
77
|
end
|
68
78
|
|
69
79
|
protected
|
70
80
|
|
71
|
-
attr_accessor :namespaces
|
81
|
+
attr_accessor :namespaces,
|
82
|
+
:decryption_key,
|
83
|
+
:encryption_key
|
72
84
|
|
73
85
|
private
|
74
86
|
|
data/lib/chamber/file_set.rb
CHANGED
@@ -107,12 +107,14 @@ require 'chamber/settings'
|
|
107
107
|
# FileSet.new(files: '/tmp/settings/*.json',
|
108
108
|
# namespaces: %w{blue green})
|
109
109
|
#
|
110
|
-
|
110
|
+
module Chamber
|
111
111
|
class FileSet
|
112
112
|
|
113
113
|
def initialize(options = {})
|
114
|
-
self.namespaces
|
115
|
-
self.
|
114
|
+
self.namespaces = options[:namespaces] || {}
|
115
|
+
self.decryption_key = options[:decryption_key]
|
116
|
+
self.encryption_key = options[:encryption_key]
|
117
|
+
self.paths = options.fetch(:files)
|
116
118
|
end
|
117
119
|
|
118
120
|
###
|
@@ -161,21 +163,23 @@ class FileSet
|
|
161
163
|
# # => <Chamber::Settings>
|
162
164
|
#
|
163
165
|
def to_settings
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
if block_given?
|
168
|
-
yield file.to_settings
|
169
|
-
else
|
170
|
-
settings.merge!(file.to_settings)
|
166
|
+
files.reduce(Settings.new) do |settings, file|
|
167
|
+
settings.merge(file.to_settings).tap do |merged|
|
168
|
+
yield merged if block_given?
|
171
169
|
end
|
172
170
|
end
|
173
171
|
end
|
174
172
|
|
173
|
+
def secure
|
174
|
+
files.each(&:secure)
|
175
|
+
end
|
176
|
+
|
175
177
|
protected
|
176
178
|
|
177
179
|
attr_reader :namespaces,
|
178
180
|
:paths
|
181
|
+
attr_accessor :decryption_key,
|
182
|
+
:encryption_key
|
179
183
|
|
180
184
|
###
|
181
185
|
# Internal: Allows the paths for the FileSet to be set. It can either be an
|
@@ -210,8 +214,10 @@ class FileSet
|
|
210
214
|
current_glob_files = Pathname.glob(glob)
|
211
215
|
relevant_glob_files = relevant_files & current_glob_files
|
212
216
|
|
213
|
-
relevant_glob_files.map! { |file| File.new( path:
|
214
|
-
namespaces:
|
217
|
+
relevant_glob_files.map! { |file| File.new( path: file,
|
218
|
+
namespaces: namespaces,
|
219
|
+
decryption_key: decryption_key,
|
220
|
+
encryption_key: encryption_key) }
|
215
221
|
|
216
222
|
sorted_relevant_files += relevant_glob_files
|
217
223
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Chamber
|
2
|
+
module Filters
|
3
|
+
class BooleanConversionFilter
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
self.data = options.fetch(:data).dup
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.execute(options = {})
|
10
|
+
self.new(options).send(:execute)
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
attr_accessor :data
|
16
|
+
|
17
|
+
def execute(settings = data)
|
18
|
+
settings.each_pair do |key, value|
|
19
|
+
if value.respond_to? :each_pair
|
20
|
+
execute(value)
|
21
|
+
else
|
22
|
+
break if value.nil?
|
23
|
+
|
24
|
+
settings[key] = if value.is_a? String
|
25
|
+
case value
|
26
|
+
when 'false', 'f', 'no'
|
27
|
+
false
|
28
|
+
when 'true', 't', 'yes'
|
29
|
+
true
|
30
|
+
else
|
31
|
+
value
|
32
|
+
end
|
33
|
+
else
|
34
|
+
value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|