vagrant 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/.gitignore +11 -0
  2. data/Gemfile +17 -0
  3. data/README.md +45 -0
  4. data/Rakefile +41 -0
  5. data/VERSION +1 -0
  6. data/bin/.gitignore +0 -0
  7. data/bin/vagrant +15 -0
  8. data/bin/vagrant-box +35 -0
  9. data/bin/vagrant-down +28 -0
  10. data/bin/vagrant-halt +29 -0
  11. data/bin/vagrant-init +28 -0
  12. data/bin/vagrant-package +30 -0
  13. data/bin/vagrant-reload +30 -0
  14. data/bin/vagrant-resume +28 -0
  15. data/bin/vagrant-ssh +28 -0
  16. data/bin/vagrant-suspend +28 -0
  17. data/bin/vagrant-up +30 -0
  18. data/config/default.rb +29 -0
  19. data/lib/vagrant.rb +14 -0
  20. data/lib/vagrant/actions/base.rb +93 -0
  21. data/lib/vagrant/actions/box/add.rb +22 -0
  22. data/lib/vagrant/actions/box/destroy.rb +14 -0
  23. data/lib/vagrant/actions/box/download.rb +63 -0
  24. data/lib/vagrant/actions/box/unpackage.rb +49 -0
  25. data/lib/vagrant/actions/runner.rb +128 -0
  26. data/lib/vagrant/actions/vm/destroy.rb +14 -0
  27. data/lib/vagrant/actions/vm/down.rb +12 -0
  28. data/lib/vagrant/actions/vm/export.rb +41 -0
  29. data/lib/vagrant/actions/vm/forward_ports.rb +32 -0
  30. data/lib/vagrant/actions/vm/halt.rb +14 -0
  31. data/lib/vagrant/actions/vm/import.rb +17 -0
  32. data/lib/vagrant/actions/vm/move_hard_drive.rb +53 -0
  33. data/lib/vagrant/actions/vm/package.rb +61 -0
  34. data/lib/vagrant/actions/vm/provision.rb +71 -0
  35. data/lib/vagrant/actions/vm/reload.rb +17 -0
  36. data/lib/vagrant/actions/vm/resume.rb +16 -0
  37. data/lib/vagrant/actions/vm/shared_folders.rb +69 -0
  38. data/lib/vagrant/actions/vm/start.rb +50 -0
  39. data/lib/vagrant/actions/vm/suspend.rb +16 -0
  40. data/lib/vagrant/actions/vm/up.rb +35 -0
  41. data/lib/vagrant/box.rb +129 -0
  42. data/lib/vagrant/busy.rb +73 -0
  43. data/lib/vagrant/commands.rb +174 -0
  44. data/lib/vagrant/config.rb +156 -0
  45. data/lib/vagrant/downloaders/base.rb +13 -0
  46. data/lib/vagrant/downloaders/file.rb +21 -0
  47. data/lib/vagrant/downloaders/http.rb +47 -0
  48. data/lib/vagrant/env.rb +140 -0
  49. data/lib/vagrant/ssh.rb +43 -0
  50. data/lib/vagrant/util.rb +45 -0
  51. data/lib/vagrant/vm.rb +57 -0
  52. data/script/vagrant-ssh-expect.sh +22 -0
  53. data/templates/Vagrantfile +8 -0
  54. data/test/test_helper.rb +91 -0
  55. data/test/vagrant/actions/base_test.rb +32 -0
  56. data/test/vagrant/actions/box/add_test.rb +37 -0
  57. data/test/vagrant/actions/box/destroy_test.rb +18 -0
  58. data/test/vagrant/actions/box/download_test.rb +118 -0
  59. data/test/vagrant/actions/box/unpackage_test.rb +101 -0
  60. data/test/vagrant/actions/runner_test.rb +236 -0
  61. data/test/vagrant/actions/vm/destroy_test.rb +24 -0
  62. data/test/vagrant/actions/vm/down_test.rb +32 -0
  63. data/test/vagrant/actions/vm/export_test.rb +88 -0
  64. data/test/vagrant/actions/vm/forward_ports_test.rb +50 -0
  65. data/test/vagrant/actions/vm/halt_test.rb +27 -0
  66. data/test/vagrant/actions/vm/import_test.rb +36 -0
  67. data/test/vagrant/actions/vm/move_hard_drive_test.rb +108 -0
  68. data/test/vagrant/actions/vm/package_test.rb +155 -0
  69. data/test/vagrant/actions/vm/provision_test.rb +103 -0
  70. data/test/vagrant/actions/vm/reload_test.rb +44 -0
  71. data/test/vagrant/actions/vm/resume_test.rb +27 -0
  72. data/test/vagrant/actions/vm/shared_folders_test.rb +117 -0
  73. data/test/vagrant/actions/vm/start_test.rb +55 -0
  74. data/test/vagrant/actions/vm/suspend_test.rb +27 -0
  75. data/test/vagrant/actions/vm/up_test.rb +76 -0
  76. data/test/vagrant/box_test.rb +92 -0
  77. data/test/vagrant/busy_test.rb +81 -0
  78. data/test/vagrant/commands_test.rb +252 -0
  79. data/test/vagrant/config_test.rb +123 -0
  80. data/test/vagrant/downloaders/base_test.rb +20 -0
  81. data/test/vagrant/downloaders/file_test.rb +32 -0
  82. data/test/vagrant/downloaders/http_test.rb +40 -0
  83. data/test/vagrant/env_test.rb +293 -0
  84. data/test/vagrant/ssh_test.rb +95 -0
  85. data/test/vagrant/util_test.rb +64 -0
  86. data/test/vagrant/vm_test.rb +96 -0
  87. metadata +275 -0
@@ -0,0 +1,41 @@
1
+ module Vagrant
2
+ module Actions
3
+ module VM
4
+ class Export < Base
5
+ attr_reader :temp_dir
6
+
7
+ def execute!
8
+ setup_temp_dir
9
+ export
10
+ end
11
+
12
+ def cleanup
13
+ if temp_dir
14
+ logger.info "Removing temporary export directory..."
15
+ FileUtils.rm_r(temp_dir)
16
+ end
17
+ end
18
+
19
+ def rescue(exception)
20
+ cleanup
21
+ end
22
+
23
+ def setup_temp_dir
24
+ @temp_dir = File.join(Env.tmp_path, Time.now.to_i.to_s)
25
+
26
+ logger.info "Creating temporary directory for export..."
27
+ FileUtils.mkpath(temp_dir)
28
+ end
29
+
30
+ def ovf_path
31
+ File.join(temp_dir, Vagrant.config.vm.box_ovf)
32
+ end
33
+
34
+ def export
35
+ logger.info "Exporting VM to #{ovf_path} ..."
36
+ @runner.vm.export(ovf_path, {}, true)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ module Vagrant
2
+ module Actions
3
+ module VM
4
+ class ForwardPorts < Base
5
+ def execute!
6
+ clear
7
+ forward_ports
8
+ end
9
+
10
+ def clear
11
+ logger.info "Deleting any previously set forwarded ports..."
12
+ @runner.vm.forwarded_ports.collect { |p| p.destroy(true) }
13
+ end
14
+
15
+ def forward_ports
16
+ logger.info "Forwarding ports..."
17
+
18
+ Vagrant.config.vm.forwarded_ports.each do |name, options|
19
+ logger.info "Forwarding \"#{name}\": #{options[:guestport]} => #{options[:hostport]}"
20
+ port = VirtualBox::ForwardedPort.new
21
+ port.name = name
22
+ port.hostport = options[:hostport]
23
+ port.guestport = options[:guestport]
24
+ @runner.vm.forwarded_ports << port
25
+ end
26
+
27
+ @runner.vm.save(true)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ module Vagrant
2
+ module Actions
3
+ module VM
4
+ class Halt < Base
5
+ def execute!
6
+ raise ActionException.new("VM is not running! Nothing to shut down!") unless @runner.vm.running?
7
+
8
+ logger.info "Forcing shutdown of VM..."
9
+ @runner.vm.stop(true)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,17 @@
1
+ module Vagrant
2
+ module Actions
3
+ module VM
4
+ class Import < Base
5
+ def execute!
6
+ @runner.invoke_around_callback(:import) do
7
+ Busy.busy do
8
+ logger.info "Importing base VM (#{Vagrant::Env.box.ovf_file})..."
9
+ # Use the first argument passed to the action
10
+ @runner.vm = VirtualBox::VM.import(Vagrant::Env.box.ovf_file)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,53 @@
1
+ module Vagrant
2
+ module Actions
3
+ module VM
4
+ class MoveHardDrive < Base
5
+ def execute!
6
+ unless @runner.powered_off?
7
+ error_and_exit(<<-error)
8
+ The virtual machine must be powered off to move its disk.
9
+ error
10
+ return
11
+ end
12
+
13
+ destroy_drive_after { clone_and_attach }
14
+ end
15
+
16
+ def hard_drive
17
+ @hard_drive ||= find_hard_drive
18
+ end
19
+
20
+ # TODO won't work if the first disk is not the boot disk or even if there are multiple disks
21
+ def find_hard_drive
22
+ @runner.vm.storage_controllers.each do |sc|
23
+ sc.devices.each do |d|
24
+ return d if d.image.is_a?(VirtualBox::HardDrive)
25
+ end
26
+ end
27
+ end
28
+
29
+ def clone_and_attach
30
+ logger.info "Cloning current VM Disk to new location (#{new_image_path})..."
31
+ hard_drive.image = hard_drive.image.clone(new_image_path, Vagrant.config.vm.disk_image_format, true)
32
+
33
+ logger.info "Attaching new disk to VM ..."
34
+ @runner.vm.save
35
+ end
36
+
37
+ def destroy_drive_after
38
+ old_image = hard_drive.image
39
+
40
+ yield
41
+
42
+ logger.info "Destroying old VM Disk (#{old_image.filename})..."
43
+ old_image.destroy(true)
44
+ end
45
+
46
+ # Returns the path to the new location for the hard drive
47
+ def new_image_path
48
+ File.join(Vagrant.config.vm.hd_location, hard_drive.image.filename)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,61 @@
1
+ module Vagrant
2
+ module Actions
3
+ module VM
4
+ class Package < Base
5
+ attr_accessor :out_path
6
+ attr_accessor :include_files
7
+ attr_reader :export_action
8
+
9
+ def initialize(vm, out_path = nil, include_files = nil, *args)
10
+ super
11
+ @out_path = out_path || "package"
12
+ @include_files = include_files || []
13
+ @temp_path = nil
14
+ end
15
+
16
+ def prepare
17
+ # Verify the existance of all the additional files, if any
18
+ @include_files.each do |file|
19
+ raise ActionException.new("#{file} does not exist") unless File.exists?(file)
20
+ end
21
+
22
+ # Get the export action and store a reference to it
23
+ @export_action = @runner.find_action(Export)
24
+ raise ActionException.new("Package must be used in conjunction with export.") unless @export_action
25
+ end
26
+
27
+ def execute!
28
+ compress
29
+ end
30
+
31
+ def tar_path
32
+ File.join(FileUtils.pwd, "#{out_path}#{Vagrant.config.package.extension}")
33
+ end
34
+
35
+ def temp_path
36
+ export_action.temp_dir
37
+ end
38
+
39
+ def compress
40
+ logger.info "Packaging VM into #{tar_path} ..."
41
+ Tar.open(tar_path, File::CREAT | File::WRONLY, 0644, Tar::GNU) do |tar|
42
+ begin
43
+ current_dir = FileUtils.pwd
44
+ @include_files.each do |f|
45
+ logger.info "Packaging additional file: #{f}"
46
+ tar.append_file(f)
47
+ end
48
+
49
+ FileUtils.cd(temp_path)
50
+
51
+ # Append tree will append the entire directory tree unless a relative folder reference is used
52
+ tar.append_tree(".")
53
+ ensure
54
+ FileUtils.cd(current_dir)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,71 @@
1
+ module Vagrant
2
+ module Actions
3
+ module VM
4
+ class Provision < Base
5
+ def execute!
6
+ chown_provisioning_folder
7
+ setup_json
8
+ setup_solo_config
9
+ run_chef_solo
10
+ end
11
+
12
+ def chown_provisioning_folder
13
+ logger.info "Setting permissions on provisioning folder..."
14
+ SSH.execute do |ssh|
15
+ ssh.exec!("sudo chown #{Vagrant.config.ssh.username} #{Vagrant.config.chef.provisioning_path}")
16
+ end
17
+ end
18
+
19
+ def setup_json
20
+ logger.info "Generating JSON and uploading..."
21
+
22
+ # Set up initial configuration
23
+ data = {
24
+ :config => Vagrant.config,
25
+ :directory => Vagrant.config.vm.project_directory,
26
+ }
27
+
28
+ # And wrap it under the "vagrant" namespace
29
+ data = { :vagrant => data }
30
+
31
+ # Merge with the "extra data" which isn't put under the
32
+ # vagrant namespace by default
33
+ data.merge!(Vagrant.config.chef.json)
34
+
35
+ json = data.to_json
36
+
37
+ SSH.upload!(StringIO.new(json), File.join(Vagrant.config.chef.provisioning_path, "dna.json"))
38
+ end
39
+
40
+ def setup_solo_config
41
+ solo_file = <<-solo
42
+ file_cache_path "#{Vagrant.config.chef.provisioning_path}"
43
+ cookbook_path "#{cookbooks_path}"
44
+ solo
45
+
46
+ logger.info "Uploading chef-solo configuration script..."
47
+ SSH.upload!(StringIO.new(solo_file), File.join(Vagrant.config.chef.provisioning_path, "solo.rb"))
48
+ end
49
+
50
+ def run_chef_solo
51
+ logger.info "Running chef recipes..."
52
+ SSH.execute do |ssh|
53
+ ssh.exec!("cd #{Vagrant.config.chef.provisioning_path} && sudo chef-solo -c solo.rb -j dna.json") do |channel, data, stream|
54
+ # TODO: Very verbose. It would be easier to save the data and only show it during
55
+ # an error, or when verbosity level is set high
56
+ logger.info("#{stream}: #{data}")
57
+ end
58
+ end
59
+ end
60
+
61
+ def cookbooks_path
62
+ File.join(Vagrant.config.chef.provisioning_path, "cookbooks")
63
+ end
64
+
65
+ def collect_shared_folders
66
+ ["vagrant-provisioning", File.expand_path(Vagrant.config.chef.cookbooks_path, Env.root_path), cookbooks_path]
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,17 @@
1
+ module Vagrant
2
+ module Actions
3
+ module VM
4
+ class Reload < Base
5
+ def prepare
6
+ steps = [ForwardPorts, SharedFolders, Start]
7
+ steps.unshift(Halt) if @runner.vm.running?
8
+ steps << Provision if Vagrant.config.chef.enabled
9
+
10
+ steps.each do |action_klass|
11
+ @runner.add_action(action_klass)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module Vagrant
2
+ module Actions
3
+ module VM
4
+ class Resume < Base
5
+ def execute!
6
+ if !@runner.vm.saved?
7
+ raise ActionException.new("The vagrant virtual environment you are trying to resume is not in a suspended state.")
8
+ end
9
+
10
+ logger.info "Resuming suspended VM..."
11
+ @runner.start
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,69 @@
1
+ module Vagrant
2
+ module Actions
3
+ module VM
4
+ class SharedFolders < Base
5
+ def shared_folders
6
+ shared_folders = @runner.invoke_callback(:collect_shared_folders)
7
+
8
+ # Basic filtering of shared folders. Basically only verifies that
9
+ # the result is an array of 3 elements. In the future this should
10
+ # also verify that the host path exists, the name is valid,
11
+ # and that the guest path is valid.
12
+ shared_folders.collect do |folder|
13
+ if folder.is_a?(Array) && folder.length == 3
14
+ folder
15
+ else
16
+ nil
17
+ end
18
+ end.compact
19
+ end
20
+
21
+ def before_boot
22
+ logger.info "Creating shared folders metadata..."
23
+
24
+ shared_folders.each do |name, hostpath, guestpath|
25
+ folder = VirtualBox::SharedFolder.new
26
+ folder.name = name
27
+ folder.hostpath = hostpath
28
+ @runner.vm.shared_folders << folder
29
+ end
30
+
31
+ @runner.vm.save(true)
32
+ end
33
+
34
+ def after_boot
35
+ logger.info "Mounting shared folders..."
36
+
37
+ Vagrant::SSH.execute do |ssh|
38
+ shared_folders.each do |name, hostpath, guestpath|
39
+ logger.info "-- #{name}: #{guestpath}"
40
+ ssh.exec!("sudo mkdir -p #{guestpath}")
41
+ mount_folder(ssh, name, guestpath)
42
+ ssh.exec!("sudo chown #{Vagrant.config.ssh.username} #{guestpath}")
43
+ end
44
+ end
45
+ end
46
+
47
+ def mount_folder(ssh, name, guestpath, sleeptime=5)
48
+ # Note: This method seems pretty OS-specific and could also use
49
+ # some configuration options. For now its duct tape and "works"
50
+ # but should be looked at in the future.
51
+ attempts = 0
52
+
53
+ while true
54
+ result = ssh.exec!("sudo mount -t vboxsf #{name} #{guestpath}") do |ch, type, data|
55
+ # net/ssh returns the value in ch[:result] (based on looking at source)
56
+ ch[:result] = !!(type == :stderr && data =~ /No such device/i)
57
+ end
58
+
59
+ break unless result
60
+
61
+ attempts += 1
62
+ raise ActionException.new("Failed to mount shared folders. vboxsf was not available.") if attempts >= 10
63
+ sleep sleeptime
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,50 @@
1
+ module Vagrant
2
+ module Actions
3
+ module VM
4
+ class Start < Base
5
+ def execute!
6
+ @runner.invoke_around_callback(:boot) do
7
+ # Startup the VM
8
+ boot
9
+
10
+ # Wait for it to complete booting, or error if we could
11
+ # never detect it booted up successfully
12
+ if !wait_for_boot
13
+ error_and_exit(<<-error)
14
+ Failed to connect to VM! Failed to boot?
15
+ error
16
+ end
17
+ end
18
+ end
19
+
20
+ def collect_shared_folders
21
+ # The root shared folder for the project
22
+ ["vagrant-root", Env.root_path, Vagrant.config.vm.project_directory]
23
+ end
24
+
25
+ def boot
26
+ logger.info "Booting VM..."
27
+ @runner.vm.start(:headless, true)
28
+ end
29
+
30
+ def wait_for_boot(sleeptime=5)
31
+ logger.info "Waiting for VM to boot..."
32
+
33
+ Vagrant.config[:ssh][:max_tries].to_i.times do |i|
34
+ logger.info "Trying to connect (attempt ##{i+1} of #{Vagrant.config[:ssh][:max_tries]})..."
35
+
36
+ if Vagrant::SSH.up?
37
+ logger.info "VM booted and ready for use!"
38
+ return true
39
+ end
40
+
41
+ sleep sleeptime
42
+ end
43
+
44
+ logger.info "Failed to connect to VM! Failed to boot?"
45
+ false
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end