vagrant-berkshelf 3.0.1 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +36 -17
  3. data/.travis.yml +6 -4
  4. data/CONTRIBUTING.md +4 -4
  5. data/Gemfile +2 -2
  6. data/{LICENSE.txt → LICENSE} +1 -1
  7. data/README.md +67 -24
  8. data/Rakefile +14 -0
  9. data/lib/vagrant-berkshelf.rb +17 -1
  10. data/lib/vagrant-berkshelf/action/base.rb +48 -0
  11. data/lib/vagrant-berkshelf/action/check.rb +44 -0
  12. data/lib/vagrant-berkshelf/action/clean.rb +29 -0
  13. data/lib/vagrant-berkshelf/action/install.rb +35 -0
  14. data/lib/vagrant-berkshelf/action/load.rb +59 -0
  15. data/lib/vagrant-berkshelf/action/share.rb +38 -0
  16. data/lib/vagrant-berkshelf/action/upload.rb +99 -0
  17. data/lib/vagrant-berkshelf/config.rb +87 -0
  18. data/lib/vagrant-berkshelf/env.rb +14 -0
  19. data/lib/vagrant-berkshelf/errors.rb +79 -0
  20. data/lib/vagrant-berkshelf/helpers.rb +180 -0
  21. data/lib/vagrant-berkshelf/plugin.rb +40 -0
  22. data/lib/vagrant-berkshelf/version.rb +5 -0
  23. data/spec/spec_helper.rb +1 -4
  24. data/spec/unit/vagrant-berkshelf/config_spec.rb +119 -0
  25. data/vagrant-berkshelf.gemspec +26 -14
  26. metadata +35 -52
  27. data/Thorfile +0 -59
  28. data/integration/Berksfile +0 -3
  29. data/integration/Gemfile +0 -10
  30. data/integration/Vagrantfile +0 -19
  31. data/lib/berkshelf/vagrant.rb +0 -68
  32. data/lib/berkshelf/vagrant/action.rb +0 -64
  33. data/lib/berkshelf/vagrant/action/clean.rb +0 -27
  34. data/lib/berkshelf/vagrant/action/configure_chef.rb +0 -27
  35. data/lib/berkshelf/vagrant/action/install.rb +0 -69
  36. data/lib/berkshelf/vagrant/action/load_shelf.rb +0 -52
  37. data/lib/berkshelf/vagrant/action/upload.rb +0 -53
  38. data/lib/berkshelf/vagrant/berks_config.rb +0 -48
  39. data/lib/berkshelf/vagrant/chef_config.rb +0 -88
  40. data/lib/berkshelf/vagrant/config.rb +0 -113
  41. data/lib/berkshelf/vagrant/env.rb +0 -16
  42. data/lib/berkshelf/vagrant/env_helpers.rb +0 -160
  43. data/lib/berkshelf/vagrant/errors.rb +0 -71
  44. data/lib/berkshelf/vagrant/plugin.rb +0 -41
  45. data/lib/berkshelf/vagrant/version.rb +0 -5
  46. data/spec/unit/berkshelf/vagrant/config_spec.rb +0 -97
  47. data/spec/unit/berkshelf/vagrant/errors_spec.rb +0 -12
  48. data/spec/unit/berkshelf/vagrant_spec.rb +0 -31
@@ -0,0 +1,59 @@
1
+ require_relative 'base'
2
+
3
+ module VagrantPlugins
4
+ module Berkshelf
5
+ module Action
6
+ class Load < Base
7
+ def call(env)
8
+ if !berkshelf_enabled?(env)
9
+ @logger.info "Berkshelf disabled, skipping"
10
+ return @app.call(env)
11
+ end
12
+
13
+ if File.exist?(datafile_path(env))
14
+ env[:machine].ui.info "Loading Berkshelf datafile..."
15
+ shelf = File.read(datafile_path(env)).chomp
16
+
17
+ @logger.debug "Shelf: #{shelf.inspect}"
18
+
19
+ env[:berkshelf].shelf = shelf
20
+ end
21
+
22
+ if !provision_enabled?(env)
23
+ @logger.info "Provisioning disabled, skipping"
24
+ return @app.call(env)
25
+ end
26
+
27
+ if !env[:berkshelf].shelf
28
+ shelf = mkshelf(env)
29
+ env[:machine].ui.detail "The Berkshelf shelf is at #{shelf.inspect}"
30
+
31
+ @logger.debug "Persisting datafile share to memory"
32
+ env[:berkshelf].shelf = shelf
33
+
34
+ @logger.debug "Saving datafile to disk"
35
+ FileUtils.mkdir_p(datafile_path(env).dirname)
36
+ datafile_path(env).open("w+") do |f|
37
+ f.write(env[:berkshelf].shelf)
38
+ end
39
+ end
40
+
41
+ @app.call(env)
42
+ end
43
+
44
+ # Create a new Berkshelf shelf for the current machine.
45
+ # @return [String]
46
+ # the path to the temporary directory
47
+ def mkshelf(env)
48
+ shelves = Berkshelf.shelves_path
49
+
50
+ if !File.exist?(shelves)
51
+ FileUtils.mkdir_p(shelves)
52
+ end
53
+
54
+ Dir.mktmpdir(['berkshelf', "-#{env[:machine].name}"], shelves)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,38 @@
1
+ require_relative 'base'
2
+
3
+ module VagrantPlugins
4
+ module Berkshelf
5
+ module Action
6
+ class Share < Base
7
+ def call(env)
8
+ if !berkshelf_enabled?(env)
9
+ @logger.info "Berkshelf disabled, skipping"
10
+ return @app.call(env)
11
+ end
12
+
13
+ if !provision_enabled?(env)
14
+ @logger.info "Provisioning disabled, skipping"
15
+ return @app.call(env)
16
+ end
17
+
18
+ if !chef_solo?(env) && !chef_zero?(env)
19
+ @logger.info "Provisioner does not need a share"
20
+ return @app.call(env)
21
+ end
22
+
23
+ env[:machine].ui.info "Sharing cookbooks with VM"
24
+
25
+ list = provisioners(:chef_solo, env) + provisioners(:chef_zero, env)
26
+ list.each do |chef|
27
+ value = chef.config.send(:prepare_folders_config, env[:berkshelf].shelf)
28
+
29
+ @logger.debug "Setting cookbooks_path = #{value.inspect}"
30
+ chef.config.cookbooks_path = value
31
+ end
32
+
33
+ @app.call(env)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,99 @@
1
+ require 'json'
2
+ require 'tempfile'
3
+
4
+ require_relative 'base'
5
+
6
+ module VagrantPlugins
7
+ module Berkshelf
8
+ module Action
9
+ class Upload < Base
10
+ def call(env)
11
+ if !berkshelf_enabled?(env)
12
+ @logger.info "Berkshelf disabled, skipping"
13
+ return @app.call(env)
14
+ end
15
+
16
+ if !provision_enabled?(env)
17
+ @logger.info "Provisioning disabled, skipping"
18
+ return @app.call(env)
19
+ end
20
+
21
+ if !chef_client?(env)
22
+ @logger.info "Provisioner does need to upload"
23
+ return @app.call(env)
24
+ end
25
+
26
+ upload(env)
27
+ @app.call(env)
28
+ end
29
+
30
+ private
31
+
32
+ # Generate a custom Berkshelf config from the existing Berkshelf
33
+ # config on disk, with values in the Vagrantfile taking precedence,
34
+ # based on the current provisioner. It is assumed the provisioner is
35
+ # the "chef_client" provisioner.
36
+ #
37
+ # The path to the temporary configuration file is yielded to the
38
+ # block. This method ensures the temporary file is cleaned up
39
+ # automatically.
40
+ #
41
+ # @param [Vagrant::Provisioner] provisioner
42
+ # @param [Proc] block
43
+ def with_provision_berkshelf_config(provisioner, &block)
44
+ config = current_berkshelf_config
45
+
46
+ config[:chef] ||= {}
47
+ config[:chef].merge(
48
+ chef_server_url: provisioner.config.chef_server_url,
49
+ node_name: provisioner.config.node_name,
50
+ client_key: provisioner.config.client_key_path,
51
+ validation_key: provisioner.config.validation_key_path,
52
+ validation_client_name: provisioner.config.validation_client_name,
53
+ )
54
+
55
+ tmpfile = Tempfile.new('config.json')
56
+ tmpfile.write(config.to_json)
57
+ tmpfile.rewind
58
+ yield tmpfile.path
59
+ ensure
60
+ if defined?(tmpfile)
61
+ tmpfile.close
62
+ tmpfile.unlink
63
+ end
64
+ end
65
+
66
+ # The current JSON representation of the Berkshelf config on disk.
67
+ #
68
+ # @return [Hash<Symbol, Object>]
69
+ def current_berkshelf_config
70
+ path = File.expand_path(ENV['BERKSHELF_CONFIG'] || '~/.berkshelf/config.json')
71
+
72
+ if File.exist?(path)
73
+ JSON.parse(File.read(), symbolize_names: true)
74
+ else
75
+ {}
76
+ end
77
+ end
78
+
79
+ # Upload the resolved Berkshelf cookbooks to the target Chef Server
80
+ # specified in the Vagrantfile.
81
+ #
82
+ # @param [Vagrant::Environment] env
83
+ def upload(env)
84
+ provisioners(:chef_client, env).each do |provisioner|
85
+ with_provision_berkshelf_config(provisioner) do |config|
86
+ env[:machine].ui.info "Uploading cookbooks to #{provisioner.config.chef_server_url}"
87
+ berks("upload",
88
+ config: config,
89
+ berksfile: provisioner.config.berkshelf.berksfile_path,
90
+ force: true,
91
+ freeze: false,
92
+ )
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,87 @@
1
+ require 'pathname'
2
+ require 'vagrant/util/hash_with_indifferent_access'
3
+
4
+ module VagrantPlugins
5
+ module Berkshelf
6
+ class Config < Vagrant.plugin("2", :config)
7
+ # The path to the Berksfile to use.
8
+ # @return [String]
9
+ attr_accessor :berksfile_path
10
+
11
+ # Disable the use of Berkshelf in Vagrant.
12
+ # @return [Boolean]
13
+ attr_accessor :enabled
14
+
15
+ # The array of cookbook groups to exclusively install during provisioning.
16
+ # @return [Array<Symbol>]
17
+ attr_accessor :only
18
+
19
+ # The array of cookbook groups to exclude during provisioning.
20
+ # @return [Array<Symbol>]
21
+ attr_accessor :except
22
+
23
+ # An array of additional arguments to pass to the Berkshelf command.
24
+ # @return [Array<String>]
25
+ attr_accessor :args
26
+
27
+ def initialize
28
+ super
29
+
30
+ @berksfile_path = UNSET_VALUE
31
+ @enabled = UNSET_VALUE
32
+ @except = Array.new
33
+ @only = Array.new
34
+ @args = Array.new
35
+
36
+ @__finalized = false
37
+ end
38
+
39
+ def finalize!
40
+ @berksfile_path = nil if @berksfile_path == UNSET_VALUE
41
+
42
+ if @enabled == UNSET_VALUE
43
+ if @berksfile_path
44
+ # Automatically enable if a Berksfile path was given
45
+ @enabled = true
46
+ elsif File.exist?("Berksfile")
47
+ # Automatically enable when a Berksfile is persent
48
+ @berksfile_path = "Berksfile"
49
+ @enabled = true
50
+ end
51
+ end
52
+
53
+ @enabled = false if @enabled == UNSET_VALUE
54
+
55
+ @__finalized = true
56
+ end
57
+
58
+ def validate(machine)
59
+ errors = _detected_errors
60
+
61
+ if @enabled
62
+ if @berksfile_path.to_s.strip.empty?
63
+ errors << "berksfile_path must be set"
64
+ else
65
+ unless Pathname.new(@berksfile_path).absolute?
66
+ @berksfile_path = File.expand_path(@berksfile_path, machine.env.root_path)
67
+ end
68
+ end
69
+ end
70
+
71
+ { "Berkshelf" => errors }
72
+ end
73
+
74
+ def to_hash
75
+ raise "Must finalize first." if !@__finalized
76
+
77
+ {
78
+ enabled: @enabled,
79
+ berksfile_path: @berksfile_path,
80
+ except: @except,
81
+ only: @only,
82
+ args: @args,
83
+ }
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,14 @@
1
+ require 'vagrant/ui'
2
+
3
+ module VagrantPlugins
4
+ module Berkshelf
5
+ class Env
6
+ # @return [String]
7
+ attr_accessor :shelf
8
+
9
+ def initialize
10
+ @shelf = nil
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,79 @@
1
+ require 'vagrant/errors'
2
+
3
+ module VagrantPlugins
4
+ module Berkshelf
5
+ INSTALL_CHEFDK_INSTRUCTIONS = <<-EOH.freeze
6
+ Please download and install the latest version of the ChefDK from:
7
+
8
+ https://downloads.getchef.com/chef-dk
9
+
10
+ and follow the installation instructions. Do not forget to add the ChefDK to
11
+ your PATH.
12
+ EOH
13
+
14
+ class BerkshelfNotFound < Vagrant::Errors::VagrantError
15
+ def error_message
16
+ <<-EOH
17
+ Vagrant Berkshelf could not find the 'berks' executable in your PATH.
18
+
19
+ #{INSTALL_CHEFDK_INSTRUCTIONS}
20
+ EOH
21
+ end
22
+ end
23
+
24
+ class BerksCommandFailed < Vagrant::Errors::VagrantError
25
+ def initialize(command, stdout, stderr)
26
+ @command, @stdout, @stderr = command, stdout, stderr
27
+ super
28
+ end
29
+
30
+ def chefdk?
31
+ @command.include?("chefdk")
32
+ end
33
+
34
+ def not_chefdk_message
35
+ <<-EOH
36
+ It appears that you are not using the ChefDK. Please note that Vagrant Berkshelf
37
+ works best when used with the ChefDK, and other installation methods are not
38
+ officially supported.
39
+
40
+ #{INSTALL_CHEFDK_INSTRUCTIONS}
41
+ EOH
42
+ end
43
+
44
+ def error_message
45
+ base = <<-EOH
46
+ The following berks command failed to execute:
47
+
48
+ #{@command}
49
+
50
+ The stdout and stderr are shown below:
51
+
52
+ stdout: #{@stdout}
53
+ stderr: #{@stderr}
54
+ EOH
55
+
56
+ base << "\n#{not_chefdk_message}" if !chefdk?
57
+ base
58
+ end
59
+ end
60
+
61
+ class InvalidBerkshelfVersionError < Vagrant::Errors::VagrantError
62
+ def initialize(bin, constraint, version)
63
+ @bin = bin
64
+ @constraint = constraint
65
+ @version = version
66
+ super
67
+ end
68
+
69
+ def error_message
70
+ <<-EOH
71
+ The Berkshelf version at #{@bin.inspect} is invalid.
72
+ Vagrant Berkshelf requires #{@constraint}, but the current version is #{@version}.
73
+
74
+ #{INSTALL_CHEFDK_INSTRUCTIONS}
75
+ EOH
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,180 @@
1
+ require 'json'
2
+ require 'vagrant/util'
3
+
4
+ require_relative 'errors'
5
+
6
+ module VagrantPlugins
7
+ module Berkshelf
8
+ # A module of common helper functions that can be mixed into Berkshelf::Vagrant actions
9
+ module Helpers
10
+ include Vagrant::Util
11
+
12
+ # Execute a berkshelf command with the given arguments and flags.
13
+ #
14
+ # @overload berks(command, args)
15
+ # @param [String] berks CLI command to run
16
+ # @param [Object] any number of arguments to pass to CLI
17
+ # @overload berks(command, args, options)
18
+ # @param [String] berks CLI command to run
19
+ # @param [Object] any number of arguments to pass to CLI
20
+ # @param [Hash] options to convert to flags for the CLI
21
+ #
22
+ # @return [String]
23
+ # output of the command
24
+ #
25
+ # @raise [InvalidBerkshelfVersionError]
26
+ # version of Berks installed does not satisfy the application's constraint
27
+ # @raise [BerksNotFoundError]
28
+ # berks command is not found in the user's path
29
+ def berks(command, *args)
30
+ options = args.last.is_a?(Hash) ? args.pop : {}
31
+ args = args.dup
32
+
33
+ if options[:berksfile_path]
34
+ args << "--berksfile"
35
+ args << options[:berksfile_path]
36
+ end
37
+
38
+ if !options.fetch(:except, []).empty?
39
+ args << "--except"
40
+ args += options[:except]
41
+ end
42
+
43
+ if !options.fetch(:only, []).empty?
44
+ args << "--only"
45
+ args += options[:only]
46
+ end
47
+
48
+ if !options.fetch(:args, []).empty?
49
+ args += options[:args]
50
+ end
51
+
52
+ final_command = [berks_bin, command, *args]
53
+
54
+ Bundler.with_clean_env do
55
+ r = Subprocess.execute(*final_command)
56
+ if r.exit_code != 0
57
+ raise BerksCommandFailed.new(final_command.join(' '), r.stdout, r.stderr)
58
+ end
59
+ r
60
+ end
61
+ end
62
+
63
+ # The path to the Berkshelf binary on disk.
64
+ # @return [String, nil]
65
+ def berks_bin
66
+ Which.which("berks")
67
+ end
68
+
69
+ # Filter all of the provisioners of the given vagrant environment with the given name
70
+ #
71
+ # @param [Symbol] name
72
+ # name of provisioner to filter
73
+ # @param [Vagrant::Environment, Hash] env
74
+ # environment to inspect
75
+ #
76
+ # @return [Array]
77
+ def provisioners(type, env)
78
+ env[:machine].config.vm.provisioners.select do |provisioner|
79
+ # Vagrant 1.7 changes provisioner.name to provisioner.type
80
+ if provisioner.respond_to? :type
81
+ provisioner.type.to_sym == type
82
+ else
83
+ provisioner.name.to_sym == type
84
+ end
85
+ end
86
+ end
87
+
88
+ # Determine if the given vagrant environment contains a chef_solo provisioner
89
+ #
90
+ # @param [Vagrant::Environment] env
91
+ #
92
+ # @return [Boolean]
93
+ def chef_solo?(env)
94
+ provisioners(:chef_solo, env).any?
95
+ end
96
+
97
+ # Determine if the given vagrant environment contains a chef_zero provisioner
98
+ #
99
+ # @param [Vagrant::Environment] env
100
+ #
101
+ # @return [Boolean]
102
+ def chef_zero?(env)
103
+ provisioners(:chef_zero, env).any?
104
+ end
105
+
106
+ # Determine if the given vagrant environment contains a chef_client provisioner
107
+ #
108
+ # @param [Vagrant::Environment] env
109
+ #
110
+ # @return [Boolean]
111
+ def chef_client?(env)
112
+ provisioners(:chef_client, env).any?
113
+ end
114
+
115
+ # Determine if the Berkshelf plugin should be run for the given environment
116
+ #
117
+ # @param [Vagrant::Environment] env
118
+ #
119
+ # @return [Boolean]
120
+ def berkshelf_enabled?(env)
121
+ env[:machine].config.berkshelf.enabled
122
+ end
123
+
124
+ # Determine if --no-provision was specified
125
+ #
126
+ # @param [Vagrant::Environment] env
127
+ #
128
+ # @return [Boolean]
129
+ def provision_enabled?(env)
130
+ env.fetch(:provision_enabled, true)
131
+ end
132
+
133
+ # The path to the Vagrant Berkshelf data file inside the machine's data
134
+ # directory.
135
+ #
136
+ # @return [String]
137
+ def datafile_path(env)
138
+ env[:machine].data_dir.join("berkshelf")
139
+ end
140
+
141
+ #
142
+ # Execute the given command, removing any Ruby-specific environment
143
+ # variables. This is an "enhanced" version of +Bundler.with_clean_env+,
144
+ # which only removes Bundler-specific values. We need to remove all
145
+ # values, specifically:
146
+ #
147
+ # - _ORIGINAL_GEM_PATH
148
+ # - GEM_PATH
149
+ # - GEM_HOME
150
+ # - GEM_ROOT
151
+ # - BUNDLE_BIN_PATH
152
+ # - BUNDLE_GEMFILE
153
+ # - RUBYLIB
154
+ # - RUBYOPT
155
+ # - RUBY_ENGINE
156
+ # - RUBY_ROOT
157
+ # - RUBY_VERSION
158
+ #
159
+ # This will escape Vagrant's environment entirely, which is required if
160
+ # calling an executable that lives in another Ruby environment. The
161
+ # original environment restored at the end of this call.
162
+ #
163
+ # @param [Proc] block
164
+ # the block to execute with the cleaned environment
165
+ #
166
+ def with_clean_env(&block)
167
+ original = ENV.to_hash
168
+
169
+ ENV.delete("_ORIGINAL_GEM_PATH")
170
+ ENV.delete_if { |k,_| k.start_with?("BUNDLE_") }
171
+ ENV.delete_if { |k,_| k.start_with?("GEM_") }
172
+ ENV.delete_if { |k,_| k.start_with?("RUBY") }
173
+
174
+ yield
175
+ ensure
176
+ ENV.replace(original.to_hash)
177
+ end
178
+ end
179
+ end
180
+ end