vagrant-berkshelf 3.0.1 → 4.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 (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