chamber 1.0.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +288 -173
  3. data/bin/chamber +2 -288
  4. data/lib/chamber.rb +19 -67
  5. data/lib/chamber/binary/heroku.rb +59 -0
  6. data/lib/chamber/binary/runner.rb +94 -0
  7. data/lib/chamber/binary/travis.rb +23 -0
  8. data/lib/chamber/commands/base.rb +33 -0
  9. data/lib/chamber/commands/comparable.rb +37 -0
  10. data/lib/chamber/commands/compare.rb +46 -0
  11. data/lib/chamber/commands/context_resolver.rb +72 -0
  12. data/lib/chamber/commands/files.rb +12 -0
  13. data/lib/chamber/commands/heroku.rb +30 -0
  14. data/lib/chamber/commands/heroku/clear.rb +25 -0
  15. data/lib/chamber/commands/heroku/compare.rb +31 -0
  16. data/lib/chamber/commands/heroku/pull.rb +30 -0
  17. data/lib/chamber/commands/heroku/push.rb +25 -0
  18. data/lib/chamber/commands/initialize.rb +73 -0
  19. data/lib/chamber/commands/securable.rb +48 -0
  20. data/lib/chamber/commands/secure.rb +16 -0
  21. data/lib/chamber/commands/show.rb +23 -0
  22. data/lib/chamber/commands/travis.rb +14 -0
  23. data/lib/chamber/commands/travis/secure.rb +35 -0
  24. data/lib/chamber/configuration.rb +34 -0
  25. data/lib/chamber/environmentable.rb +23 -0
  26. data/lib/chamber/errors/undecryptable_value_error.rb +6 -0
  27. data/lib/chamber/file.rb +17 -5
  28. data/lib/chamber/file_set.rb +18 -12
  29. data/lib/chamber/filters/boolean_conversion_filter.rb +41 -0
  30. data/lib/chamber/filters/decryption_filter.rb +69 -0
  31. data/lib/chamber/filters/encryption_filter.rb +57 -0
  32. data/lib/chamber/filters/environment_filter.rb +75 -0
  33. data/lib/chamber/filters/namespace_filter.rb +37 -0
  34. data/lib/chamber/filters/secure_filter.rb +39 -0
  35. data/lib/chamber/instance.rb +40 -0
  36. data/lib/chamber/namespace_set.rb +55 -16
  37. data/lib/chamber/rails/railtie.rb +1 -1
  38. data/lib/chamber/settings.rb +103 -42
  39. data/lib/chamber/system_environment.rb +3 -93
  40. data/lib/chamber/version.rb +2 -2
  41. data/spec/fixtures/settings.yml +27 -0
  42. data/spec/lib/chamber/commands/context_resolver_spec.rb +106 -0
  43. data/spec/lib/chamber/commands/files_spec.rb +19 -0
  44. data/spec/lib/chamber/commands/heroku/clear_spec.rb +11 -0
  45. data/spec/lib/chamber/commands/heroku/compare_spec.rb +11 -0
  46. data/spec/lib/chamber/commands/heroku/pull_spec.rb +11 -0
  47. data/spec/lib/chamber/commands/heroku/push_spec.rb +11 -0
  48. data/spec/lib/chamber/commands/secure_spec.rb +29 -0
  49. data/spec/lib/chamber/commands/show_spec.rb +43 -0
  50. data/spec/lib/chamber/file_set_spec.rb +1 -1
  51. data/spec/lib/chamber/file_spec.rb +32 -9
  52. data/spec/lib/chamber/filters/boolean_conversion_filter_spec.rb +44 -0
  53. data/spec/lib/chamber/filters/decryption_filter_spec.rb +55 -0
  54. data/spec/lib/chamber/filters/encryption_filter_spec.rb +48 -0
  55. data/spec/lib/chamber/filters/environment_filter_spec.rb +35 -0
  56. data/spec/lib/chamber/filters/namespace_filter_spec.rb +73 -0
  57. data/spec/lib/chamber/filters/secure_filter_spec.rb +36 -0
  58. data/spec/lib/chamber/namespace_set_spec.rb +61 -18
  59. data/spec/lib/chamber/settings_spec.rb +99 -23
  60. data/spec/lib/chamber/system_environment_spec.rb +1 -71
  61. data/spec/lib/chamber_spec.rb +40 -26
  62. data/spec/rails-2-test/config.ru +0 -0
  63. data/spec/rails-2-test/config/application.rb +5 -0
  64. data/spec/rails-2-test/script/console +0 -0
  65. data/spec/rails-3-test/config.ru +0 -0
  66. data/spec/rails-3-test/config/application.rb +5 -0
  67. data/spec/rails-3-test/script/rails +0 -0
  68. data/spec/rails-4-test/bin/rails +0 -0
  69. data/spec/rails-4-test/config.ru +0 -0
  70. data/spec/rails-4-test/config/application.rb +5 -0
  71. data/spec/spec_key +27 -0
  72. data/spec/spec_key.pub +9 -0
  73. metadata +85 -4
@@ -0,0 +1,94 @@
1
+ require 'thor'
2
+ require 'chamber/binary/travis'
3
+ require 'chamber/binary/heroku'
4
+ require 'chamber/commands/show'
5
+ require 'chamber/commands/files'
6
+ require 'chamber/commands/secure'
7
+ require 'chamber/commands/compare'
8
+ require 'chamber/commands/initialize'
9
+
10
+ module Chamber
11
+ module Binary
12
+ class Runner < Thor
13
+ include Thor::Actions
14
+
15
+ class_option :rootpath,
16
+ type: :string,
17
+ aliases: '-r',
18
+ default: ENV['PWD'],
19
+ desc: 'The root filepath of the application'
20
+ class_option :basepath,
21
+ type: :string,
22
+ aliases: '-b',
23
+ desc: 'The base filepath where Chamber will look for the conventional settings files'
24
+ class_option :files,
25
+ type: :array,
26
+ aliases: '-f',
27
+ desc: 'The set of file globs that Chamber will use for processing'
28
+ class_option :namespaces,
29
+ type: :array,
30
+ aliases: '-n',
31
+ default: [],
32
+ desc: 'The set of namespaces that Chamber will use for processing'
33
+ class_option :preset,
34
+ type: :string,
35
+ aliases: '-p',
36
+ enum: ['rails'],
37
+ desc: 'Used to quickly assign a given scenario to the chamber command (eg Rails apps)'
38
+ class_option :decryption_key,
39
+ type: :string,
40
+ desc: 'The path to or contents of the private key associated with the project (typically .chamber.pem)'
41
+ class_option :encryption_key,
42
+ type: :string,
43
+ desc: 'The path to or contents of the public key associated with the project (typically .chamber.pub.pem)'
44
+ class_option :shell,
45
+ default: self.new,
46
+ desc: 'The command runner. Can be overridden for specific logging capabilities.'
47
+
48
+ desc 'travis SUBCOMMAND ...ARGS', 'For manipulating Travis CI environment variables'
49
+ subcommand 'travis', Chamber::Binary::Travis
50
+
51
+ desc 'heroku SUBCOMMAND ...ARGS', 'For manipulating Heroku environment variables'
52
+ subcommand 'heroku', Chamber::Binary::Heroku
53
+
54
+ desc 'show', 'Displays the list of settings and their values'
55
+ method_option :as_env,
56
+ type: :boolean,
57
+ aliases: '-e',
58
+ desc: 'Whether the displayed settings should be environment variable compatible'
59
+ def show
60
+ puts Commands::Show.call(options)
61
+ end
62
+
63
+ desc 'files', 'Lists the settings files which are parsed with the given options'
64
+ def files
65
+ puts Commands::Files.call(options)
66
+ end
67
+
68
+ desc 'compare', 'Displays the difference between what is currently stored in the Heroku application\'s config and what Chamber knows about locally'
69
+ method_option :keys_only,
70
+ type: :boolean,
71
+ default: true,
72
+ desc: 'Whether or not to only compare the keys but not the values of the two sets of settings'
73
+ method_option :first,
74
+ type: :array,
75
+ desc: 'The list of namespaces which will be used as the source of the comparison'
76
+ method_option :second,
77
+ type: :array,
78
+ desc: 'The list of namespaces which will be used as the destination of the comparison'
79
+ def compare
80
+ Commands::Compare.call(options)
81
+ end
82
+
83
+ desc 'secure', 'Secures any values which appear to need to be encrypted in any of the settings files which match irrespective of namespaces'
84
+ def secure
85
+ Commands::Secure.call(options)
86
+ end
87
+
88
+ desc 'init', 'Sets Chamber up matching best practices for secure configuration management'
89
+ def init
90
+ Commands::Initialize.call(options)
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,23 @@
1
+ require 'thor'
2
+ require 'chamber/commands/travis/secure'
3
+
4
+ module Chamber
5
+ module Binary
6
+ class Travis < Thor
7
+
8
+ desc 'secure', 'Uses your Travis CI public key to encrypt the settings you have chosen not to commit to the repo'
9
+ method_option :dry_run,
10
+ type: :boolean,
11
+ aliases: '-d',
12
+ desc: 'Does not actually encrypt anything to .travis.yml, but instead displays what values would be encrypted'
13
+ method_option :only_sensitive,
14
+ type: :boolean,
15
+ aliases: '-o',
16
+ default: true,
17
+ desc: 'Does not encrypt settings into .travis.yml unless they are contained in files which have been gitignored or settings which are marked as "_secure"'
18
+ def secure
19
+ Commands::Travis::Secure.call(options)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ require 'chamber/commands/context_resolver'
2
+ require 'chamber/instance'
3
+
4
+ module Chamber
5
+ module Commands
6
+ class Base
7
+
8
+ def initialize(options = {})
9
+ options = ContextResolver.resolve(options)
10
+
11
+ self.chamber = Chamber::Instance.new options
12
+ self.shell = options[:shell]
13
+ self.rootpath = options[:rootpath]
14
+ self.dry_run = options[:dry_run]
15
+ end
16
+
17
+ def self.call(options = {})
18
+ self.new(options).call
19
+ end
20
+
21
+ protected
22
+
23
+ attr_accessor :chamber,
24
+ :shell,
25
+ :dry_run
26
+ attr_reader :rootpath
27
+
28
+ def rootpath=(other)
29
+ @rootpath ||= Pathname.new(other)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,37 @@
1
+ require 'tempfile'
2
+
3
+ module Chamber
4
+ module Commands
5
+ module Comparable
6
+
7
+ def initialize(options = {})
8
+ super
9
+
10
+ self.keys_only = options[:keys_only]
11
+ end
12
+
13
+ def call
14
+ system("git diff --no-index #{first_settings_file} #{second_settings_file}")
15
+ end
16
+
17
+ protected
18
+
19
+ attr_accessor :keys_only
20
+
21
+ def first_settings_file
22
+ create_comparable_settings_file 'first', first_settings_data
23
+ end
24
+
25
+ def second_settings_file
26
+ create_comparable_settings_file 'second', second_settings_data
27
+ end
28
+
29
+ def create_comparable_settings_file(name, config)
30
+ Tempfile.open(name) do |file|
31
+ file.write config
32
+ file.to_path
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,46 @@
1
+ require 'chamber/instance'
2
+ require 'chamber/commands/base'
3
+ require 'chamber/commands/comparable'
4
+
5
+ module Chamber
6
+ module Commands
7
+ class Compare < Chamber::Commands::Base
8
+ include Chamber::Commands::Comparable
9
+
10
+ def initialize(options = {})
11
+ super
12
+
13
+ first_settings_options = options.merge(namespaces: options[:first])
14
+ self.first_settings_instance = Chamber::Instance.new(first_settings_options)
15
+
16
+ second_settings_options = options.merge(namespaces: options[:second])
17
+ self.second_settings_instance = Chamber::Instance.new(second_settings_options)
18
+ end
19
+
20
+ def self.call(options = {})
21
+ self.new(options).call
22
+ end
23
+
24
+ protected
25
+
26
+ attr_accessor :first_settings_instance,
27
+ :second_settings_instance
28
+
29
+ def first_settings_data
30
+ settings_data(first_settings_instance)
31
+ end
32
+
33
+ def second_settings_data
34
+ settings_data(second_settings_instance)
35
+ end
36
+
37
+ def settings_data(instance)
38
+ if keys_only
39
+ instance.to_environment.keys.join("\n")
40
+ else
41
+ instance.to_s(pair_separator: "\n")
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,72 @@
1
+ module Chamber
2
+ module Commands
3
+ class ContextResolver
4
+
5
+ def initialize(options = {})
6
+ self.options = options.dup
7
+ end
8
+
9
+ def resolve
10
+ options[:rootpath] ||= Pathname.pwd
11
+ options[:rootpath] = Pathname.new(options[:rootpath])
12
+ options[:encryption_key] = resolve_encryption_key(options[:encryption_key])
13
+ options[:decryption_key] = resolve_decryption_key(options[:decryption_key])
14
+ options[:namespaces] ||= []
15
+ options[:preset] ||= resolve_preset
16
+
17
+ if options[:preset] == 'rails'
18
+ options[:basepath] ||= options[:rootpath] + 'config'
19
+
20
+ if options[:namespaces] == []
21
+ require options[:rootpath].join('config', 'application')
22
+
23
+ options[:namespaces] = [::Rails.env]
24
+ end
25
+ else
26
+ options[:basepath] ||= options[:rootpath]
27
+ end
28
+
29
+ options
30
+ rescue LoadError
31
+ options
32
+ end
33
+
34
+ def self.resolve(options = {})
35
+ self.new(options).resolve
36
+ end
37
+
38
+ protected
39
+
40
+ attr_accessor :options
41
+
42
+ def resolve_preset
43
+ if in_a_rails_project?
44
+ 'rails'
45
+ end
46
+ end
47
+
48
+ def resolve_encryption_key(key)
49
+ key ||= options[:rootpath] + '.chamber.pub.pem'
50
+
51
+ key if Pathname.new(key).readable?
52
+ end
53
+
54
+ def resolve_decryption_key(key)
55
+ key ||= options[:rootpath] + '.chamber.pem'
56
+
57
+ key if Pathname.new(key).readable?
58
+ end
59
+
60
+ def in_a_rails_project?
61
+ (options[:rootpath] + 'config.ru').exist? &&
62
+ rails_executable_exists?
63
+ end
64
+
65
+ def rails_executable_exists?
66
+ options[:rootpath].join('bin', 'rails').exist? ||
67
+ options[:rootpath].join('script', 'rails').exist? ||
68
+ options[:rootpath].join('script', 'console').exist?
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,12 @@
1
+ require 'chamber/commands/base'
2
+
3
+ module Chamber
4
+ module Commands
5
+ class Files < Chamber::Commands::Base
6
+
7
+ def call
8
+ chamber.filenames
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,30 @@
1
+ require 'bundler'
2
+
3
+ module Chamber
4
+ module Commands
5
+ module Heroku
6
+
7
+ def initialize(options = {})
8
+ super
9
+
10
+ self.app = options[:app]
11
+ end
12
+
13
+ protected
14
+
15
+ attr_accessor :app
16
+
17
+ def configuration
18
+ @configuration ||= heroku("config --shell").chomp
19
+ end
20
+
21
+ def heroku(command)
22
+ Bundler.with_clean_env { `heroku #{command}#{app_option}` }
23
+ end
24
+
25
+ def app_option
26
+ app ? " --app #{app}" : ""
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,25 @@
1
+ require 'chamber/commands/base'
2
+ require 'chamber/commands/heroku'
3
+
4
+ module Chamber
5
+ module Commands
6
+ module Heroku
7
+ class Clear < Chamber::Commands::Base
8
+ include Chamber::Commands::Heroku
9
+
10
+ def call
11
+ chamber.to_environment.keys.each do |key|
12
+ next unless configuration.match(key)
13
+
14
+ if dry_run
15
+ shell.say_status 'remove', key, :blue
16
+ else
17
+ shell.say_status 'remove', key, :green
18
+ shell.say heroku("config:unset #{key}")
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ require 'chamber/commands/securable'
2
+ require 'chamber/commands/heroku'
3
+ require 'chamber/commands/comparable'
4
+
5
+ module Chamber
6
+ module Commands
7
+ module Heroku
8
+ class Compare < Chamber::Commands::Base
9
+ include Chamber::Commands::Securable
10
+ include Chamber::Commands::Heroku
11
+ include Chamber::Commands::Comparable
12
+
13
+ protected
14
+
15
+ def first_settings_data
16
+ if only_sensitive
17
+ secured_settings.to_s(pair_separator: "\n",
18
+ value_surrounder: '')
19
+ else
20
+ all_settings.to_s(pair_separator: "\n",
21
+ value_surrounder: '')
22
+ end
23
+ end
24
+
25
+ def second_settings_data
26
+ configuration
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ require 'chamber/commands/base'
2
+ require 'chamber/commands/heroku'
3
+
4
+ module Chamber
5
+ module Commands
6
+ module Heroku
7
+ class Pull < Chamber::Commands::Base
8
+ include Chamber::Commands::Heroku
9
+
10
+ def initialize(options = {})
11
+ super
12
+
13
+ self.target_file = options[:into]
14
+ end
15
+
16
+ def call
17
+ if target_file
18
+ shell.create_file target_file, configuration
19
+ else
20
+ configuration
21
+ end
22
+ end
23
+
24
+ protected
25
+
26
+ attr_accessor :target_file
27
+ end
28
+ end
29
+ end
30
+ end