vagrantup 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Rakefile +1 -1
  4. data/VERSION +1 -1
  5. data/bin/vagrant +1 -1
  6. data/bin/vagrant-box +1 -2
  7. data/bin/vagrant-down +1 -2
  8. data/bin/vagrant-halt +1 -2
  9. data/bin/vagrant-init +1 -2
  10. data/bin/vagrant-package +1 -2
  11. data/bin/vagrant-reload +1 -2
  12. data/bin/vagrant-resume +1 -2
  13. data/bin/vagrant-ssh +1 -2
  14. data/bin/vagrant-status +29 -0
  15. data/bin/vagrant-suspend +1 -2
  16. data/bin/vagrant-up +1 -2
  17. data/config/default.rb +5 -9
  18. data/keys/README.md +10 -0
  19. data/keys/vagrant +27 -0
  20. data/keys/vagrant.pub +1 -0
  21. data/lib/vagrant.rb +10 -5
  22. data/lib/vagrant/actions/base.rb +14 -0
  23. data/lib/vagrant/actions/box/download.rb +3 -0
  24. data/lib/vagrant/actions/collection.rb +36 -0
  25. data/lib/vagrant/actions/runner.rb +4 -10
  26. data/lib/vagrant/actions/vm/boot.rb +4 -5
  27. data/lib/vagrant/actions/vm/customize.rb +17 -0
  28. data/lib/vagrant/actions/vm/destroy.rb +11 -2
  29. data/lib/vagrant/actions/vm/forward_ports.rb +24 -0
  30. data/lib/vagrant/actions/vm/import.rb +1 -0
  31. data/lib/vagrant/actions/vm/provision.rb +30 -52
  32. data/lib/vagrant/actions/vm/reload.rb +2 -2
  33. data/lib/vagrant/actions/vm/shared_folders.rb +37 -25
  34. data/lib/vagrant/actions/vm/up.rb +8 -4
  35. data/lib/vagrant/active_list.rb +66 -0
  36. data/lib/vagrant/commands.rb +44 -0
  37. data/lib/vagrant/config.rb +64 -47
  38. data/lib/vagrant/downloaders/base.rb +3 -0
  39. data/lib/vagrant/downloaders/file.rb +11 -11
  40. data/lib/vagrant/env.rb +48 -12
  41. data/lib/vagrant/provisioners/base.rb +22 -0
  42. data/lib/vagrant/provisioners/chef.rb +102 -0
  43. data/lib/vagrant/provisioners/chef_server.rb +96 -0
  44. data/lib/vagrant/provisioners/chef_solo.rb +67 -0
  45. data/lib/vagrant/ssh.rb +25 -6
  46. data/lib/vagrant/stacked_proc_runner.rb +33 -0
  47. data/lib/vagrant/vm.rb +8 -0
  48. data/test/test_helper.rb +22 -6
  49. data/test/vagrant/actions/box/download_test.rb +11 -0
  50. data/test/vagrant/actions/collection_test.rb +110 -0
  51. data/test/vagrant/actions/runner_test.rb +11 -7
  52. data/test/vagrant/actions/vm/boot_test.rb +7 -7
  53. data/test/vagrant/actions/vm/customize_test.rb +16 -0
  54. data/test/vagrant/actions/vm/destroy_test.rb +19 -6
  55. data/test/vagrant/actions/vm/forward_ports_test.rb +52 -0
  56. data/test/vagrant/actions/vm/import_test.rb +10 -3
  57. data/test/vagrant/actions/vm/provision_test.rb +75 -70
  58. data/test/vagrant/actions/vm/reload_test.rb +3 -2
  59. data/test/vagrant/actions/vm/shared_folders_test.rb +62 -9
  60. data/test/vagrant/actions/vm/up_test.rb +4 -4
  61. data/test/vagrant/active_list_test.rb +169 -0
  62. data/test/vagrant/config_test.rb +145 -29
  63. data/test/vagrant/downloaders/base_test.rb +7 -0
  64. data/test/vagrant/downloaders/file_test.rb +12 -18
  65. data/test/vagrant/env_test.rb +96 -23
  66. data/test/vagrant/provisioners/base_test.rb +27 -0
  67. data/test/vagrant/provisioners/chef_server_test.rb +175 -0
  68. data/test/vagrant/provisioners/chef_solo_test.rb +142 -0
  69. data/test/vagrant/provisioners/chef_test.rb +116 -0
  70. data/test/vagrant/ssh_test.rb +29 -8
  71. data/test/vagrant/stacked_proc_runner_test.rb +43 -0
  72. data/test/vagrant/vm_test.rb +23 -0
  73. data/vagrant.gemspec +34 -7
  74. metadata +33 -5
  75. data/script/vagrant-ssh-expect.sh +0 -22
@@ -5,6 +5,9 @@ module Vagrant
5
5
  class Base
6
6
  include Vagrant::Util
7
7
 
8
+ # Called prior to execution so any error checks can be done
9
+ def prepare(source_url); end
10
+
8
11
  # Downloads the source file to the destination file. It is up to
9
12
  # implementors of this class to handle the logic.
10
13
  def download!(source_url, destination_file); end
@@ -3,19 +3,19 @@ module Vagrant
3
3
  # "Downloads" a file to a temporary file. Basically, this downloader
4
4
  # simply does a file copy.
5
5
  class File < Base
6
- BUFFERSIZE = 1048576 # 1 MB
6
+ def prepare(source_url)
7
+ if !::File.file?(source_url)
8
+ raise Actions::ActionException.new(<<-msg)
9
+ The given box does not exist on the file system:
7
10
 
8
- def download!(source_url, destination_file)
9
- # For now we read the contents of one into a buffer
10
- # and copy it into the other. In the future, we should do
11
- # a system-level file copy (FileUtils.cp).
12
- open(source_url) do |f|
13
- loop do
14
- break if f.eof?
15
- destination_file.write(f.read(BUFFERSIZE))
16
- end
11
+ #{source_url}
12
+ msg
17
13
  end
18
14
  end
15
+
16
+ def download!(source_url, destination_file)
17
+ FileUtils.cp(source_url, destination_file.path)
18
+ end
19
19
  end
20
20
  end
21
- end
21
+ end
data/lib/vagrant/env.rb CHANGED
@@ -24,21 +24,52 @@ module Vagrant
24
24
  load_config!
25
25
  load_home_directory!
26
26
  load_box!
27
+ load_config!
28
+ check_virtualbox!
27
29
  load_vm!
28
30
  end
29
31
 
32
+ def check_virtualbox!
33
+ version = VirtualBox::Command.version
34
+ if version.nil?
35
+ error_and_exit(<<-msg)
36
+ Vagrant could not detect VirtualBox! Make sure VirtualBox is properly installed.
37
+ If VirtualBox is installed, you may need to tweak the paths to the `VBoxManage`
38
+ application which ships with VirtualBox and the path to the global XML configuration
39
+ which VirtualBox typically stores somewhere in your home directory.
40
+
41
+ The following shows how to configure VirtualBox. This can be done in the
42
+ Vagrantfile. Note that 90% of the time, you shouldn't need to do this if VirtualBox
43
+ is installed. Please use the various Vagrant support lines to request more information
44
+ if you can't get this working.
45
+
46
+ VirtualBox::Command.vboxmanage = "/path/to/my/VBoxManage"
47
+ VirtualBox::Global.vboxconfig = "~/path/to/VirtualBox.xml"
48
+ msg
49
+ elsif version.to_f < 3.1
50
+ error_and_exit(<<-msg)
51
+ Vagrant has detected that you have VirtualBox version #{version} installed!
52
+ Vagrant requires that you use at least VirtualBox version 3.1. Please install
53
+ a more recent version of VirtualBox to continue.
54
+ msg
55
+ end
56
+ end
57
+
30
58
  def load_config!
31
59
  # Prepare load paths for config files
32
60
  load_paths = [File.join(PROJECT_ROOT, "config", "default.rb")]
33
61
  load_paths << File.join(box.directory, ROOTFILE_NAME) if box
62
+ load_paths << File.join(home_path, ROOTFILE_NAME) if Vagrant.config.vagrant.home
34
63
  load_paths << File.join(root_path, ROOTFILE_NAME) if root_path
35
64
 
36
65
  # Then clear out the old data
37
66
  Config.reset!
38
67
 
39
68
  load_paths.each do |path|
40
- logger.info "Loading config from #{path}..."
41
- load path if File.exist?(path)
69
+ if File.exist?(path)
70
+ logger.info "Loading config from #{path}..."
71
+ load path
72
+ end
42
73
  end
43
74
 
44
75
  # Execute the configurations
@@ -63,11 +94,6 @@ module Vagrant
63
94
  return unless root_path
64
95
 
65
96
  @@box = Box.find(Vagrant.config.vm.box) if Vagrant.config.vm.box
66
-
67
- if @@box
68
- logger.info("Reloading configuration to account for loaded box...")
69
- load_config!
70
- end
71
97
  end
72
98
 
73
99
  def load_vm!
@@ -81,18 +107,28 @@ module Vagrant
81
107
  end
82
108
 
83
109
  def persist_vm(vm)
110
+ # Save to the dotfile for this project
84
111
  File.open(dotfile_path, 'w+') do |f|
85
112
  f.write(vm.uuid)
86
113
  end
114
+
115
+ # Also add to the global store
116
+ ActiveList.add(vm)
117
+ end
118
+
119
+ def depersist_vm(vm)
120
+ # Delete the dotfile if it exists
121
+ File.delete(dotfile_path) if File.exist?(dotfile_path)
122
+
123
+ # Remove from the global store
124
+ ActiveList.remove(vm)
87
125
  end
88
126
 
89
127
  def load_root_path!(path=nil)
90
- path ||= Pathname.new(Dir.pwd)
128
+ path = Pathname.new(File.expand_path(path || Dir.pwd))
91
129
 
92
- # Stop if we're at the root. 2nd regex matches windows drives
93
- # such as C:. and Z:. Portability of this check will need to be
94
- # researched.
95
- return false if path.to_s == '/' || path.to_s =~ /^[A-Z]:\.$/
130
+ # Stop if we're at the root.
131
+ return false if path.root?
96
132
 
97
133
  file = "#{path}/#{ROOTFILE_NAME}"
98
134
  if File.exist?(file)
@@ -0,0 +1,22 @@
1
+ module Vagrant
2
+ module Provisioners
3
+ # The base class for a "provisioner." A provisioner is responsible for
4
+ # provisioning a Vagrant system. This has been abstracted out to provide
5
+ # support for multiple solutions such as Chef Solo, Chef Client, and
6
+ # Puppet.
7
+ class Base
8
+ include Vagrant::Util
9
+
10
+ # This is the method called to "prepare" the provisioner. This is called
11
+ # before any actions are run by the action runner (see {Vagrant::Actions::Runner}).
12
+ # This can be used to setup shared folders, forward ports, etc. Whatever is
13
+ # necessary on a "meta" level.
14
+ def prepare; end
15
+
16
+ # This is the method called to provision the system. This method
17
+ # is expected to do whatever necessary to provision the system (create files,
18
+ # SSH, etc.)
19
+ def provision!; end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,102 @@
1
+ module Vagrant
2
+ module Provisioners
3
+ # This class is a base class where the common functinality shared between
4
+ # chef-solo and chef-client provisioning are stored. This is **not an actual
5
+ # provisioner**. Instead, {ChefSolo} or {ChefServer} should be used.
6
+ class Chef < Base
7
+ # This is the configuration which is available through `config.chef`
8
+ class ChefConfig < Vagrant::Config::Base
9
+ # Chef server specific config
10
+ attr_accessor :chef_server_url
11
+ attr_accessor :validation_key_path
12
+ attr_accessor :validation_client_name
13
+ attr_accessor :client_key_path
14
+
15
+ # Chef solo specific config
16
+ attr_accessor :cookbooks_path
17
+
18
+ # Shared config
19
+ attr_accessor :provisioning_path
20
+ attr_accessor :json
21
+
22
+ def initialize
23
+ @validation_client_name = "chef-validator"
24
+ @client_key_path = "/etc/chef/client.pem"
25
+
26
+ @cookbooks_path = "cookbooks"
27
+ @provisioning_path = "/tmp/vagrant-chef"
28
+ @json = {
29
+ :instance_role => "vagrant",
30
+ :run_list => ["recipe[vagrant_main]"]
31
+ }
32
+ end
33
+
34
+ # Returns the run list for the provisioning
35
+ def run_list
36
+ json[:run_list]
37
+ end
38
+
39
+ # Sets the run list to the specified value
40
+ def run_list=(value)
41
+ json[:run_list] = value
42
+ end
43
+
44
+ # Adds a recipe to the run list
45
+ def add_recipe(name)
46
+ name = "recipe[#{name}]" unless name =~ /^recipe\[(.+?)\]$/
47
+ run_list << name
48
+ end
49
+
50
+ # Adds a role to the run list
51
+ def add_role(name)
52
+ name = "role[#{name}]" unless name =~ /^role\[(.+?)\]$/
53
+ run_list << name
54
+ end
55
+
56
+ def to_json
57
+ # Overridden so that the 'json' key could be removed, since its just
58
+ # merged into the config anyways
59
+ data = instance_variables_hash
60
+ data.delete(:json)
61
+ data.to_json
62
+ end
63
+ end
64
+
65
+ # Tell the Vagrant configure class about our custom configuration
66
+ Config.configures :chef, ChefConfig
67
+
68
+ def prepare
69
+ raise Actions::ActionException.new("Vagrant::Provisioners::Chef is not a valid provisioner! Use ChefSolo or ChefServer instead.")
70
+ end
71
+
72
+ def chown_provisioning_folder
73
+ logger.info "Setting permissions on chef provisioning folder..."
74
+ SSH.execute do |ssh|
75
+ ssh.exec!("sudo mkdir -p #{Vagrant.config.chef.provisioning_path}")
76
+ ssh.exec!("sudo chown #{Vagrant.config.ssh.username} #{Vagrant.config.chef.provisioning_path}")
77
+ end
78
+ end
79
+
80
+ def setup_json
81
+ logger.info "Generating chef JSON and uploading..."
82
+
83
+ # Set up initial configuration
84
+ data = {
85
+ :config => Vagrant.config,
86
+ :directory => Vagrant.config.vm.project_directory,
87
+ }
88
+
89
+ # And wrap it under the "vagrant" namespace
90
+ data = { :vagrant => data }
91
+
92
+ # Merge with the "extra data" which isn't put under the
93
+ # vagrant namespace by default
94
+ data.merge!(Vagrant.config.chef.json)
95
+
96
+ json = data.to_json
97
+
98
+ SSH.upload!(StringIO.new(json), File.join(Vagrant.config.chef.provisioning_path, "dna.json"))
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,96 @@
1
+ module Vagrant
2
+ module Provisioners
3
+ # This class implements provisioning via chef-client, allowing provisioning
4
+ # with a chef server.
5
+ class ChefServer < Chef
6
+ def prepare
7
+ if Vagrant.config.chef.validation_key_path.nil?
8
+ raise Actions::ActionException.new(<<-msg)
9
+ Chef server provisioning requires that the `config.chef.validation_key_path` configuration
10
+ be set to a path on your local machine of the validation key used to register the
11
+ VM with the chef server.
12
+ msg
13
+ elsif !File.file?(Vagrant.config.chef.validation_key_path)
14
+ raise Actions::ActionException.new(<<-msg)
15
+ The validation key set for `config.chef.validation_key_path` does not exist! This
16
+ file needs to exist so it can be uploaded to the virtual machine. It is
17
+ currently set to "#{Vagrant.config.chef.validation_key_path}"
18
+ msg
19
+ end
20
+
21
+ if Vagrant.config.chef.chef_server_url.nil?
22
+ raise Actions::ActionException.new(<<-msg)
23
+ Chef server provisioning requires that the `config.chef.chef_server_url` be set to the
24
+ URL of your chef server. Examples include "http://12.12.12.12:4000" and
25
+ "http://myserver.com:4000" (the port of course can be different, but 4000 is the default)
26
+ msg
27
+ end
28
+ end
29
+
30
+ def provision!
31
+ chown_provisioning_folder
32
+ create_client_key_folder
33
+ upload_validation_key
34
+ setup_json
35
+ setup_config
36
+ run_chef_client
37
+ end
38
+
39
+ def create_client_key_folder
40
+ logger.info "Creating folder to hold client key..."
41
+ path = Pathname.new(Vagrant.config.chef.client_key_path)
42
+
43
+ SSH.execute do |ssh|
44
+ ssh.exec!("sudo mkdir -p #{path.dirname}")
45
+ end
46
+ end
47
+
48
+ def upload_validation_key
49
+ logger.info "Uploading chef client validation key..."
50
+ SSH.upload!(validation_key_path, guest_validation_key_path)
51
+ end
52
+
53
+ def setup_config
54
+ solo_file = <<-solo
55
+ log_level :info
56
+ log_location STDOUT
57
+ ssl_verify_mode :verify_none
58
+ chef_server_url "#{Vagrant.config.chef.chef_server_url}"
59
+
60
+ validation_client_name "#{Vagrant.config.chef.validation_client_name}"
61
+ validation_key "#{guest_validation_key_path}"
62
+ client_key "#{Vagrant.config.chef.client_key_path}"
63
+
64
+ file_store_path "/srv/chef/file_store"
65
+ file_cache_path "/srv/chef/cache"
66
+
67
+ pid_file "/var/run/chef/chef-client.pid"
68
+
69
+ Mixlib::Log::Formatter.show_time = true
70
+ solo
71
+
72
+ logger.info "Uploading chef-client configuration script..."
73
+ SSH.upload!(StringIO.new(solo_file), File.join(Vagrant.config.chef.provisioning_path, "client.rb"))
74
+ end
75
+
76
+ def run_chef_client
77
+ logger.info "Running chef-client..."
78
+ SSH.execute do |ssh|
79
+ ssh.exec!("cd #{Vagrant.config.chef.provisioning_path} && sudo chef-client -c client.rb -j dna.json") do |channel, data, stream|
80
+ # TODO: Very verbose. It would be easier to save the data and only show it during
81
+ # an error, or when verbosity level is set high
82
+ logger.info("#{stream}: #{data}")
83
+ end
84
+ end
85
+ end
86
+
87
+ def validation_key_path
88
+ File.expand_path(Vagrant.config.chef.validation_key_path, Env.root_path)
89
+ end
90
+
91
+ def guest_validation_key_path
92
+ File.join(Vagrant.config.chef.provisioning_path, "validation.pem")
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,67 @@
1
+ module Vagrant
2
+ module Provisioners
3
+ # This class implements provisioning via chef-solo.
4
+ class ChefSolo < Chef
5
+ def prepare
6
+ share_cookbook_folders
7
+ end
8
+
9
+ def provision!
10
+ chown_provisioning_folder
11
+ setup_json
12
+ setup_solo_config
13
+ run_chef_solo
14
+ end
15
+
16
+ def share_cookbook_folders
17
+ host_cookbook_paths.each_with_index do |cookbook, i|
18
+ Vagrant.config.vm.share_folder("vagrant-chef-solo-#{i}", cookbook_path(i), cookbook)
19
+ end
20
+ end
21
+
22
+ def setup_solo_config
23
+ solo_file = <<-solo
24
+ file_cache_path "#{Vagrant.config.chef.provisioning_path}"
25
+ cookbook_path #{cookbooks_path}
26
+ solo
27
+
28
+ logger.info "Uploading chef-solo configuration script..."
29
+ SSH.upload!(StringIO.new(solo_file), File.join(Vagrant.config.chef.provisioning_path, "solo.rb"))
30
+ end
31
+
32
+ def run_chef_solo
33
+ logger.info "Running chef-solo..."
34
+ SSH.execute do |ssh|
35
+ ssh.exec!("cd #{Vagrant.config.chef.provisioning_path} && sudo chef-solo -c solo.rb -j dna.json") do |channel, data, stream|
36
+ # TODO: Very verbose. It would be easier to save the data and only show it during
37
+ # an error, or when verbosity level is set high
38
+ logger.info("#{stream}: #{data}")
39
+ end
40
+ end
41
+ end
42
+
43
+ def host_cookbook_paths
44
+ cookbooks = Vagrant.config.chef.cookbooks_path
45
+ cookbooks = [cookbooks] unless cookbooks.is_a?(Array)
46
+ cookbooks.collect! { |cookbook| File.expand_path(cookbook, Env.root_path) }
47
+ return cookbooks
48
+ end
49
+
50
+ def cookbook_path(i)
51
+ File.join(Vagrant.config.chef.provisioning_path, "cookbooks-#{i}")
52
+ end
53
+
54
+ def cookbooks_path
55
+ result = []
56
+ host_cookbook_paths.each_with_index do |host_path, i|
57
+ result << cookbook_path(i)
58
+ end
59
+
60
+ # We're lucky that ruby's string and array syntax for strings is the
61
+ # same as JSON, so we can just convert to JSON here and use that
62
+ result = result.to_s if result.length == 1
63
+ result.to_json
64
+ end
65
+ end
66
+ end
67
+ end
data/lib/vagrant/ssh.rb CHANGED
@@ -1,19 +1,22 @@
1
1
  module Vagrant
2
2
  class SSH
3
- SCRIPT = File.join(File.dirname(__FILE__), '..', '..', 'script', 'vagrant-ssh-expect.sh')
3
+ include Vagrant::Util
4
4
 
5
5
  class << self
6
6
  def connect(opts={})
7
7
  options = {}
8
- [:host, :password, :username].each do |param|
8
+ [:host, :username, :private_key_path].each do |param|
9
9
  options[param] = opts[param] || Vagrant.config.ssh.send(param)
10
10
  end
11
11
 
12
- Kernel.exec "#{SCRIPT} #{options[:username]} #{options[:password]} #{options[:host]} #{port(opts)}".strip
12
+ Kernel.exec "ssh -p #{port(opts)} -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i #{options[:private_key_path]} #{options[:username]}@#{options[:host]}".strip
13
13
  end
14
14
 
15
- def execute
16
- Net::SSH.start(Vagrant.config.ssh.host, Vagrant.config[:ssh][:username], :port => port, :password => Vagrant.config[:ssh][:password]) do |ssh|
15
+ def execute(opts={})
16
+ Net::SSH.start(Vagrant.config.ssh.host,
17
+ Vagrant.config[:ssh][:username],
18
+ opts.merge( :port => port,
19
+ :keys => [Vagrant.config.ssh.private_key_path])) do |ssh|
17
20
  yield ssh
18
21
  end
19
22
  end
@@ -29,7 +32,7 @@ module Vagrant
29
32
  check_thread = Thread.new do
30
33
  begin
31
34
  Thread.current[:result] = false
32
- Net::SSH.start(Vagrant.config.ssh.host, Vagrant.config.ssh.username, :port => port, :password => Vagrant.config.ssh.password, :timeout => Vagrant.config.ssh.timeout) do |ssh|
35
+ execute(:timeout => Vagrant.config.ssh.timeout) do |ssh|
33
36
  Thread.current[:result] = true
34
37
  end
35
38
  rescue Errno::ECONNREFUSED, Net::SSH::Disconnect
@@ -39,6 +42,22 @@ module Vagrant
39
42
 
40
43
  check_thread.join(Vagrant.config.ssh.timeout)
41
44
  return check_thread[:result]
45
+ rescue Net::SSH::AuthenticationFailed
46
+ error_and_exit(<<-msg)
47
+ SSH authentication failed! While this could be due to a variety of reasons,
48
+ the two most common are: private key path is incorrect or you're using a box
49
+ which was built for Vagrant 0.1.x.
50
+
51
+ Vagrant 0.2.x dropped support for password-based authentication. If you're
52
+ tring to `vagrant up` a box which does not support Vagrant's private/public
53
+ keypair, then this error will be raised. To resolve this, read the guide
54
+ on converting base boxes from password-based to keypairs here:
55
+
56
+ http://vagrantup.com/docs/converting_password_to_key_ssh.html
57
+
58
+ If the box was built for 0.2.x and contains a custom public key, perhaps
59
+ the path to the private key is incorrect. Check your `config.ssh.private_key_path`.
60
+ msg
42
61
  end
43
62
 
44
63
  def port(opts={})