chamber 1.0.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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