vagrantup 0.8.7 → 0.8.8
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.
- checksums.yaml +4 -4
- data/.gitignore +21 -0
- data/CHANGELOG.md +25 -0
- data/README.md +38 -8
- data/Rakefile +13 -6
- data/bin/vagrant +6 -1
- data/config/default.rb +2 -2
- data/lib/vagrant/action/box/download.rb +14 -2
- data/lib/vagrant/action/vm/check_box.rb +8 -1
- data/lib/vagrant/action/vm/check_guest_additions.rb +6 -3
- data/lib/vagrant/action/vm/share_folders.rb +12 -1
- data/lib/vagrant/command/init.rb +1 -1
- data/lib/vagrant/downloaders/http.rb +29 -2
- data/lib/vagrant/hosts.rb +1 -0
- data/lib/vagrant/hosts/bsd.rb +1 -0
- data/lib/vagrant/hosts/freebsd.rb +51 -0
- data/lib/vagrant/provisioners/chef.rb +6 -5
- data/lib/vagrant/provisioners/chef_client.rb +1 -1
- data/lib/vagrant/provisioners/chef_solo.rb +13 -4
- data/lib/vagrant/ssh.rb +24 -49
- data/lib/vagrant/ssh/session.rb +1 -1
- data/lib/vagrant/systems/solaris.rb +57 -11
- data/lib/vagrant/test_helpers.rb +23 -0
- data/lib/vagrant/ui.rb +1 -1
- data/lib/vagrant/util.rb +0 -1
- data/lib/vagrant/util/file_checksum.rb +38 -0
- data/lib/vagrant/util/platform.rb +1 -1
- data/lib/vagrant/util/safe_exec.rb +2 -1
- data/lib/vagrant/version.rb +1 -1
- data/tasks/acceptance.rake +113 -0
- data/tasks/bundler.rake +3 -0
- data/tasks/test.rake +15 -0
- data/templates/commands/init/Vagrantfile.erb +3 -0
- data/templates/locales/en.yml +1 -1
- data/test/acceptance/base.rb +48 -0
- data/test/acceptance/box_test.rb +77 -0
- data/test/acceptance/destroy_test.rb +37 -0
- data/test/acceptance/halt_test.rb +72 -0
- data/test/acceptance/init_test.rb +33 -0
- data/test/acceptance/resume_test.rb +17 -0
- data/test/acceptance/ssh_test.rb +41 -0
- data/test/acceptance/support/config.rb +42 -0
- data/test/acceptance/support/isolated_environment.rb +226 -0
- data/test/acceptance/support/matchers/have_color.rb +9 -0
- data/test/acceptance/support/matchers/match_output.rb +14 -0
- data/test/acceptance/support/output.rb +87 -0
- data/test/acceptance/support/shared/base_context.rb +65 -0
- data/test/acceptance/support/shared/command_examples.rb +33 -0
- data/test/acceptance/support/tempdir.rb +34 -0
- data/test/acceptance/support/virtualbox.rb +36 -0
- data/test/acceptance/suspend_test.rb +56 -0
- data/test/acceptance/up_basic_test.rb +58 -0
- data/test/acceptance/up_with_box_url.rb +40 -0
- data/test/acceptance/vagrant_test.rb +47 -0
- data/test/acceptance/version_test.rb +20 -0
- data/test/buildbot/README.md +72 -0
- data/test/buildbot/buildbot_config/__init__.py +0 -0
- data/test/buildbot/buildbot_config/config/__init__.py +0 -0
- data/test/buildbot/buildbot_config/config/loader.py +24 -0
- data/test/buildbot/buildbot_config/config/master.py +24 -0
- data/test/buildbot/buildbot_config/config/slave.py +22 -0
- data/test/buildbot/buildbot_config/master/__init__.py +6 -0
- data/test/buildbot/buildbot_config/master/builders.py +78 -0
- data/test/buildbot/buildbot_config/master/buildsteps.py +100 -0
- data/test/buildbot/buildbot_config/master/change_sources.py +8 -0
- data/test/buildbot/buildbot_config/master/schedulers.py +32 -0
- data/test/buildbot/buildbot_config/master/slaves.py +60 -0
- data/test/buildbot/buildbot_config/master/status.py +52 -0
- data/test/buildbot/master/Makefile.sample +28 -0
- data/test/buildbot/master/buildbot.tac +36 -0
- data/test/buildbot/master/master.cfg +67 -0
- data/test/buildbot/master/public_html/bg_gradient.jpg +0 -0
- data/test/buildbot/master/public_html/default.css +545 -0
- data/test/buildbot/master/public_html/favicon.ico +0 -0
- data/test/buildbot/master/public_html/robots.txt +10 -0
- data/test/buildbot/master/public_html/static/css/bootstrap-1.4.0.min.css +356 -0
- data/test/buildbot/master/public_html/static/css/prettify.css +97 -0
- data/test/buildbot/master/public_html/static/css/syntax.css +60 -0
- data/test/buildbot/master/public_html/static/css/vagrant.base.css +205 -0
- data/test/buildbot/master/public_html/static/images/base_box_mac.jpg +0 -0
- data/test/buildbot/master/public_html/static/images/getting-started/success.jpg +0 -0
- data/test/buildbot/master/public_html/static/images/icons/error.png +0 -0
- data/test/buildbot/master/public_html/static/images/vagrant_chilling.png +0 -0
- data/test/buildbot/master/public_html/static/images/vagrant_holding.png +0 -0
- data/test/buildbot/master/public_html/static/images/vagrant_looking.png +0 -0
- data/test/buildbot/master/public_html/static/images/windows/alter_path.jpg +0 -0
- data/test/buildbot/master/public_html/static/images/windows/edit_path.jpg +0 -0
- data/test/buildbot/master/public_html/static/images/windows/environment_variables_button.jpg +0 -0
- data/test/buildbot/master/public_html/static/images/windows/port_and_ppk_path.jpg +0 -0
- data/test/buildbot/master/public_html/static/images/windows/ppk_selection.jpg +0 -0
- data/test/buildbot/master/public_html/static/images/windows/putty_first_screen.jpg +0 -0
- data/test/buildbot/master/public_html/static/images/windows/save_result.jpg +0 -0
- data/test/buildbot/master/public_html/static/images/windows/vbox_manage_default_location.jpg +0 -0
- data/test/buildbot/master/public_html/static/js/bootstrap-tabs.js +80 -0
- data/test/buildbot/master/public_html/static/js/jquery-1.7.min.js +4 -0
- data/test/buildbot/master/templates/authfail.html +9 -0
- data/test/buildbot/master/templates/build.html +205 -0
- data/test/buildbot/master/templates/builder.html +118 -0
- data/test/buildbot/master/templates/builders.html +33 -0
- data/test/buildbot/master/templates/buildslave.html +72 -0
- data/test/buildbot/master/templates/buildslaves.html +70 -0
- data/test/buildbot/master/templates/change.html +15 -0
- data/test/buildbot/master/templates/layouts/base.html +58 -0
- data/test/buildbot/master/templates/macros/box.html +37 -0
- data/test/buildbot/master/templates/macros/build_line.html +50 -0
- data/test/buildbot/master/templates/macros/change.html +81 -0
- data/test/buildbot/master/templates/macros/forms.html +300 -0
- data/test/buildbot/master/templates/root.html +42 -0
- data/test/buildbot/master/templates/waterfall.html +53 -0
- data/test/buildbot/requirements.txt +4 -0
- data/test/buildbot/scripts/deploy.sh +38 -0
- data/test/buildbot/scripts/setup.sh +107 -0
- data/test/buildbot/slave/buildbot.tac +43 -0
- data/test/buildbot/slave/info/admin +1 -0
- data/test/buildbot/slave/info/host +1 -0
- data/test/buildbot/tests/__init__.py +0 -0
- data/test/buildbot/tests/master/__init__.py +0 -0
- data/test/buildbot/tests/master/test_slaves.py +41 -0
- data/test/buildbot/vendor/choices-0.4.0.tar.gz +0 -0
- data/test/config/acceptance_boxes.yml +7 -0
- data/test/unit/test_helper.rb +4 -0
- data/test/unit/vagrant/action/box/download_test.rb +2 -2
- data/test/unit/vagrant/action/vm/check_box_test.rb +6 -1
- data/test/unit/vagrant/action/vm/share_folders_test.rb +1 -1
- data/test/unit/vagrant/command/init_test.rb +10 -0
- data/test/unit/vagrant/downloaders/http_test.rb +12 -1
- data/test/unit/vagrant/provisioners/chef_test.rb +7 -0
- data/test/unit/vagrant/ssh/session_test.rb +2 -2
- data/test/unit/vagrant/ssh_test.rb +5 -8
- data/vagrant.gemspec +6 -0
- metadata +177 -1
data/tasks/bundler.rake
ADDED
data/tasks/test.rake
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'rake/testtask'
|
|
2
|
+
require 'rspec/core/rake_task'
|
|
3
|
+
|
|
4
|
+
namespace :test do
|
|
5
|
+
Rake::TestTask.new do |t|
|
|
6
|
+
t.name = "unit"
|
|
7
|
+
t.libs << "test/unit"
|
|
8
|
+
t.pattern = "test/unit/**/*_test.rb"
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
RSpec::Core::RakeTask.new do |t|
|
|
12
|
+
t.name = "acceptance"
|
|
13
|
+
t.pattern = "test/acceptance/**/*_test.rb"
|
|
14
|
+
end
|
|
15
|
+
end
|
data/templates/locales/en.yml
CHANGED
|
@@ -227,7 +227,7 @@ en:
|
|
|
227
227
|
above with their current state. For more information about a specific
|
|
228
228
|
VM, run `vagrant status NAME`.
|
|
229
229
|
up:
|
|
230
|
-
vm_created: "VM already created. Booting if
|
|
230
|
+
vm_created: "VM already created. Booting if it's not already running..."
|
|
231
231
|
upgrade_to_060:
|
|
232
232
|
already_done: "Environment appears to already be upgraded to 0.6.0. Doing nothing!"
|
|
233
233
|
ask: "Are you sure you want to execute this command?"
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require "rubygems"
|
|
2
|
+
require "rspec/autorun"
|
|
3
|
+
|
|
4
|
+
require "log4r"
|
|
5
|
+
|
|
6
|
+
# Add this directory to the load path, since it just makes
|
|
7
|
+
# everything else so much easier.
|
|
8
|
+
$:.unshift File.expand_path("../", __FILE__)
|
|
9
|
+
|
|
10
|
+
# Load in the supporting files for our tests
|
|
11
|
+
require "support/shared/base_context"
|
|
12
|
+
require "support/config"
|
|
13
|
+
require "support/virtualbox"
|
|
14
|
+
require "support/matchers/match_output"
|
|
15
|
+
|
|
16
|
+
# Do not buffer output
|
|
17
|
+
$stdout.sync = true
|
|
18
|
+
$stderr.sync = true
|
|
19
|
+
|
|
20
|
+
# If VirtualBox is currently running, fail.
|
|
21
|
+
if Acceptance::VirtualBox.find_vboxsvc
|
|
22
|
+
$stderr.puts "VirtualBox must be closed and remain closed for the duration of the tests."
|
|
23
|
+
abort
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Enable logging if requested
|
|
27
|
+
if ENV["ACCEPTANCE_LOGGING"]
|
|
28
|
+
logger = Log4r::Logger.new("acceptance")
|
|
29
|
+
logger.outputters = Log4r::Outputter.stdout
|
|
30
|
+
logger.level = Log4r.const_get(ENV["ACCEPTANCE_LOGGING"].upcase)
|
|
31
|
+
logger = nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Parse the command line options and load the global configuration.
|
|
35
|
+
if !ENV.has_key?("ACCEPTANCE_CONFIG")
|
|
36
|
+
$stderr.puts "A configuration file must be passed into the acceptance test."
|
|
37
|
+
abort
|
|
38
|
+
elsif !File.file?(ENV["ACCEPTANCE_CONFIG"])
|
|
39
|
+
$stderr.puts "The configuration file must exist."
|
|
40
|
+
abort
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
$acceptance_options = Acceptance::Config.new(ENV["ACCEPTANCE_CONFIG"])
|
|
44
|
+
|
|
45
|
+
# Configure RSpec
|
|
46
|
+
RSpec.configure do |c|
|
|
47
|
+
c.expect_with :rspec, :stdlib
|
|
48
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require File.expand_path("../base", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "vagrant box" do
|
|
4
|
+
include_context "acceptance"
|
|
5
|
+
|
|
6
|
+
it "has no boxes by default" do
|
|
7
|
+
result = execute("vagrant", "box", "list")
|
|
8
|
+
result.stdout.should match_output(:no_boxes)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it "can add a box from a file" do
|
|
12
|
+
require_box("default")
|
|
13
|
+
|
|
14
|
+
# Add the box, which we expect to succeed
|
|
15
|
+
result = execute("vagrant", "box", "add", "foo", box_path("default"))
|
|
16
|
+
result.should be_success
|
|
17
|
+
|
|
18
|
+
# Verify that the box now shows up in the list of available boxes
|
|
19
|
+
result = execute("vagrant", "box", "list")
|
|
20
|
+
result.stdout.should match_output(:box_installed, "foo")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "gives an error if the file doesn't exist" do
|
|
24
|
+
result = execute("vagrant", "box", "add", "foo", "/tmp/nope/nope/nope/nonono.box")
|
|
25
|
+
result.should_not be_success
|
|
26
|
+
result.stdout.should match_output(:box_path_doesnt_exist)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "gives an error if the file is not a valid box" do
|
|
30
|
+
invalid = environment.workdir.join("nope.txt")
|
|
31
|
+
invalid.open("w+") do |f|
|
|
32
|
+
f.write("INVALID!")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
result = execute("vagrant", "box", "add", "foo", invalid.to_s)
|
|
36
|
+
result.should_not be_success
|
|
37
|
+
result.stdout.should match_output(:box_invalid)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "can add a box from an HTTP server" do
|
|
41
|
+
pending("Need to setup HTTP server functionality")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "can remove a box" do
|
|
45
|
+
require_box("default")
|
|
46
|
+
|
|
47
|
+
# Add the box, remove the box, then verify that the box no longer
|
|
48
|
+
# shows up in the list of available boxes.
|
|
49
|
+
execute("vagrant", "box", "add", "foo", box_path("default"))
|
|
50
|
+
execute("vagrant", "box", "remove", "foo")
|
|
51
|
+
result = execute("vagrant", "box", "list")
|
|
52
|
+
result.should be_success
|
|
53
|
+
result.stdout.should match_output(:no_boxes)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "can repackage a box" do
|
|
57
|
+
require_box("default")
|
|
58
|
+
|
|
59
|
+
original_size = File.size(box_path("default"))
|
|
60
|
+
logger.debug("Original package size: #{original_size}")
|
|
61
|
+
|
|
62
|
+
# Add the box, repackage it, and verify that a package.box is
|
|
63
|
+
# dumped of relatively similar size.
|
|
64
|
+
execute("vagrant", "box", "add", "foo", box_path("default"))
|
|
65
|
+
execute("vagrant", "box", "repackage", "foo")
|
|
66
|
+
|
|
67
|
+
# By default, repackage should dump into package.box into the CWD
|
|
68
|
+
repackaged_file = environment.workdir.join("package.box")
|
|
69
|
+
repackaged_file.file?.should be, "package.box should exist in cwd of environment"
|
|
70
|
+
|
|
71
|
+
# Compare the sizes
|
|
72
|
+
repackaged_size = repackaged_file.size
|
|
73
|
+
logger.debug("Repackaged size: #{repackaged_size}")
|
|
74
|
+
size_diff = (repackaged_size - original_size).abs
|
|
75
|
+
size_diff.should be < 1000, "Sizes should be very similar"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require File.expand_path("../base", __FILE__)
|
|
2
|
+
require "support/shared/command_examples"
|
|
3
|
+
|
|
4
|
+
describe "vagrant destroy" do
|
|
5
|
+
include_context "acceptance"
|
|
6
|
+
it_behaves_like "a command that requires a Vagrantfile", ["vagrant", "destroy"]
|
|
7
|
+
|
|
8
|
+
it "succeeds and ignores if the VM is not created" do
|
|
9
|
+
require_box("default")
|
|
10
|
+
|
|
11
|
+
assert_execute("vagrant", "box", "add", "base", box_path("default"))
|
|
12
|
+
assert_execute("vagrant", "init")
|
|
13
|
+
|
|
14
|
+
result = assert_execute("vagrant", "destroy")
|
|
15
|
+
result.stdout.should match_output(:vm_not_created_warning)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "is able to destroy a running virtual machine" do
|
|
19
|
+
require_box("default")
|
|
20
|
+
|
|
21
|
+
assert_execute("vagrant", "box", "add", "base", box_path("default"))
|
|
22
|
+
assert_execute("vagrant", "init")
|
|
23
|
+
assert_execute("vagrant", "up")
|
|
24
|
+
|
|
25
|
+
# Destroy the VM and assert that it worked properly (seemingly)
|
|
26
|
+
result = assert_execute("vagrant", "destroy")
|
|
27
|
+
result.stdout.should match_output(:vm_destroyed)
|
|
28
|
+
|
|
29
|
+
# Assert that the VM no longer is created
|
|
30
|
+
result = assert_execute("vagrant", "status")
|
|
31
|
+
result.stdout.should match_output(:status, "default", "not created")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# TODO:
|
|
35
|
+
# it is able to destroy a halted virtual machine
|
|
36
|
+
# it is able to destroy a suspended virtual machine
|
|
37
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
require File.expand_path("../base", __FILE__)
|
|
2
|
+
require "support/shared/command_examples"
|
|
3
|
+
|
|
4
|
+
describe "vagrant halt" do
|
|
5
|
+
include_context "acceptance"
|
|
6
|
+
it_behaves_like "a command that requires a Vagrantfile", ["vagrant", "halt"]
|
|
7
|
+
|
|
8
|
+
it "succeeds and ignores if the VM is not created" do
|
|
9
|
+
require_box("default")
|
|
10
|
+
|
|
11
|
+
assert_execute("vagrant", "box", "add", "base", box_path("default"))
|
|
12
|
+
assert_execute("vagrant", "init")
|
|
13
|
+
|
|
14
|
+
result = assert_execute("vagrant", "halt")
|
|
15
|
+
result.stdout.should match_output(:vm_not_created_warning)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "is able to halt a running virtual machine" do
|
|
19
|
+
require_box("default")
|
|
20
|
+
|
|
21
|
+
assert_execute("vagrant", "box", "add", "base", box_path("default"))
|
|
22
|
+
assert_execute("vagrant", "init")
|
|
23
|
+
assert_execute("vagrant", "up")
|
|
24
|
+
|
|
25
|
+
# Halt the VM and assert that it worked properly (seemingly)
|
|
26
|
+
result = assert_execute("vagrant", "halt")
|
|
27
|
+
result.stdout.should match_output(:vm_halt_graceful)
|
|
28
|
+
|
|
29
|
+
# Assert that the VM is no longer running
|
|
30
|
+
result = assert_execute("vagrant", "status")
|
|
31
|
+
result.stdout.should match_output(:status, "default", "powered off")
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it "is able to force halt a running virtual machine" do
|
|
35
|
+
require_box("default")
|
|
36
|
+
|
|
37
|
+
assert_execute("vagrant", "box", "add", "base", box_path("default"))
|
|
38
|
+
assert_execute("vagrant", "init")
|
|
39
|
+
assert_execute("vagrant", "up")
|
|
40
|
+
|
|
41
|
+
# Halt the VM and assert that it worked properly (seemingly)
|
|
42
|
+
result = assert_execute("vagrant", "halt", "--force")
|
|
43
|
+
result.stdout.should match_output(:vm_halt_force)
|
|
44
|
+
|
|
45
|
+
# Assert that the VM is no longer running
|
|
46
|
+
result = assert_execute("vagrant", "status")
|
|
47
|
+
result.stdout.should match_output(:status, "default", "powered off")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "is able to come back up after the machine has been halted" do
|
|
51
|
+
require_box("default")
|
|
52
|
+
|
|
53
|
+
assert_execute("vagrant", "box", "add", "base", box_path("default"))
|
|
54
|
+
assert_execute("vagrant", "init")
|
|
55
|
+
assert_execute("vagrant", "up")
|
|
56
|
+
assert_execute("vagrant", "halt")
|
|
57
|
+
|
|
58
|
+
# Assert that the VM is no longer running
|
|
59
|
+
result = assert_execute("vagrant", "status")
|
|
60
|
+
result.stdout.should match_output(:status, "default", "powered off")
|
|
61
|
+
|
|
62
|
+
assert_execute("vagrant", "up")
|
|
63
|
+
|
|
64
|
+
# Assert that the VM is once again running
|
|
65
|
+
result = assert_execute("vagrant", "status")
|
|
66
|
+
result.stdout.should match_output(:status, "default", "running")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# TODO:
|
|
70
|
+
# halt behavior on suspend machine
|
|
71
|
+
# halt behavior if machine is already powered off
|
|
72
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
require File.expand_path("../base", __FILE__)
|
|
2
|
+
|
|
3
|
+
describe "vagrant init" do
|
|
4
|
+
include_context "acceptance"
|
|
5
|
+
|
|
6
|
+
it "creates a Vagrantfile in the working directory" do
|
|
7
|
+
vagrantfile = environment.workdir.join("Vagrantfile")
|
|
8
|
+
vagrantfile.exist?.should_not be, "Vagrantfile shouldn't exist initially"
|
|
9
|
+
|
|
10
|
+
result = execute("vagrant", "init")
|
|
11
|
+
result.should be_success
|
|
12
|
+
vagrantfile.exist?.should be, "Vagrantfile should exist"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "creates a Vagrantfile with the box set to the given argument" do
|
|
16
|
+
vagrantfile = environment.workdir.join("Vagrantfile")
|
|
17
|
+
|
|
18
|
+
result = execute("vagrant", "init", "foo")
|
|
19
|
+
result.should be_success
|
|
20
|
+
vagrantfile.read.should match(/config.vm.box = "foo"$/)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "creates a Vagrantfile with the box URL set to the given argument" do
|
|
24
|
+
vagrantfile = environment.workdir.join("Vagrantfile")
|
|
25
|
+
|
|
26
|
+
result = execute("vagrant", "init", "foo", "bar")
|
|
27
|
+
result.should be_success
|
|
28
|
+
|
|
29
|
+
contents = vagrantfile.read
|
|
30
|
+
contents.should match(/config.vm.box = "foo"$/)
|
|
31
|
+
contents.should match(/config.vm.box_url = "bar"$/)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require File.expand_path("../base", __FILE__)
|
|
2
|
+
require "support/shared/command_examples"
|
|
3
|
+
|
|
4
|
+
describe "vagrant resume" do
|
|
5
|
+
include_context "acceptance"
|
|
6
|
+
it_behaves_like "a command that requires a Vagrantfile", ["vagrant", "resume"]
|
|
7
|
+
|
|
8
|
+
it "succeeds and ignores if the VM is not created" do
|
|
9
|
+
require_box("default")
|
|
10
|
+
|
|
11
|
+
assert_execute("vagrant", "box", "add", "base", box_path("default"))
|
|
12
|
+
assert_execute("vagrant", "init")
|
|
13
|
+
|
|
14
|
+
result = assert_execute("vagrant", "resume")
|
|
15
|
+
result.stdout.should match_output(:vm_not_created_warning)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require File.expand_path("../base", __FILE__)
|
|
2
|
+
require "support/shared/command_examples"
|
|
3
|
+
|
|
4
|
+
describe "vagrant ssh" do
|
|
5
|
+
include_context "acceptance"
|
|
6
|
+
it_behaves_like "a command that requires a Vagrantfile", ["vagrant", "ssh"]
|
|
7
|
+
it_behaves_like "a command that requires a virtual machine", ["vagrant", "ssh"]
|
|
8
|
+
|
|
9
|
+
it "is able to SSH into a running virtual machine" do
|
|
10
|
+
require_box("default")
|
|
11
|
+
|
|
12
|
+
assert_execute("vagrant", "box", "add", "base", box_path("default"))
|
|
13
|
+
assert_execute("vagrant", "init")
|
|
14
|
+
assert_execute("vagrant", "up")
|
|
15
|
+
|
|
16
|
+
outputted = false
|
|
17
|
+
result = assert_execute("vagrant", "ssh") do |io_type, data|
|
|
18
|
+
if io_type == :stdin and !outputted
|
|
19
|
+
data.puts("echo hello")
|
|
20
|
+
data.puts("exit")
|
|
21
|
+
outputted = true
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
result.stdout.chomp.should eql("hello"), "Vagrant should bring up a VM to be able to SSH into."
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "is able to execute a single command via the command line" do
|
|
29
|
+
require_box("default")
|
|
30
|
+
|
|
31
|
+
assert_execute("vagrant", "box", "add", "base", box_path("default"))
|
|
32
|
+
assert_execute("vagrant", "init")
|
|
33
|
+
assert_execute("vagrant", "up")
|
|
34
|
+
|
|
35
|
+
result = assert_execute("vagrant", "ssh", "-c", "echo foo")
|
|
36
|
+
result.stdout.should == "foo\n"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# TODO:
|
|
40
|
+
# SSH should fail if the VM is not running
|
|
41
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require "yaml"
|
|
2
|
+
|
|
3
|
+
require "log4r"
|
|
4
|
+
|
|
5
|
+
module Acceptance
|
|
6
|
+
# This represents a configuration object for acceptance tests.
|
|
7
|
+
class Config
|
|
8
|
+
attr_reader :vagrant_path
|
|
9
|
+
attr_reader :vagrant_version
|
|
10
|
+
attr_reader :env
|
|
11
|
+
attr_reader :box_directory
|
|
12
|
+
|
|
13
|
+
def initialize(path)
|
|
14
|
+
@logger = Log4r::Logger.new("acceptance::config")
|
|
15
|
+
@logger.info("Loading configuration from: #{path}")
|
|
16
|
+
options = YAML.load_file(path)
|
|
17
|
+
@logger.info("Loaded: #{options.inspect}")
|
|
18
|
+
|
|
19
|
+
@vagrant_path = options["vagrant_path"]
|
|
20
|
+
@vagrant_version = options["vagrant_version"]
|
|
21
|
+
@env = options["env"]
|
|
22
|
+
@box_directory = options["box_directory"]
|
|
23
|
+
|
|
24
|
+
# Verify the configuration object.
|
|
25
|
+
validate
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# This method verifies the configuration and makes sure that
|
|
29
|
+
# all the configuration is available and appears good. This
|
|
30
|
+
# method will raise an ArgumentError in the case that anything
|
|
31
|
+
# is wrong.
|
|
32
|
+
def validate
|
|
33
|
+
if !@vagrant_path || !File.file?(@vagrant_path)
|
|
34
|
+
raise ArgumentError, "'vagrant_path' must point to the `vagrant` executable"
|
|
35
|
+
elsif !@vagrant_version
|
|
36
|
+
raise ArgumentError, "`vagrant_version' must be set to the version of the `vagrant` executable"
|
|
37
|
+
elsif !@box_directory || !File.directory?(@box_directory)
|
|
38
|
+
raise ArgumentError, "`box_directory` must be set to a folder containing boxes for the tests."
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
require "pathname"
|
|
3
|
+
|
|
4
|
+
require "log4r"
|
|
5
|
+
require "childprocess"
|
|
6
|
+
|
|
7
|
+
require File.expand_path("../tempdir", __FILE__)
|
|
8
|
+
require File.expand_path("../virtualbox", __FILE__)
|
|
9
|
+
|
|
10
|
+
module Acceptance
|
|
11
|
+
# This class manages an isolated environment for Vagrant to
|
|
12
|
+
# run in. It creates a temporary directory to act as the
|
|
13
|
+
# working directory as well as sets a custom home directory.
|
|
14
|
+
class IsolatedEnvironment
|
|
15
|
+
attr_reader :homedir
|
|
16
|
+
attr_reader :workdir
|
|
17
|
+
|
|
18
|
+
# Initializes an isolated environment. You can pass in some
|
|
19
|
+
# options here to configure runing custom applications in place
|
|
20
|
+
# of others as well as specifying environmental variables.
|
|
21
|
+
#
|
|
22
|
+
# @param [Hash] apps A mapping of application name (such as "vagrant")
|
|
23
|
+
# to an alternate full path to the binary to run.
|
|
24
|
+
# @param [Hash] env Additional environmental variables to inject
|
|
25
|
+
# into the execution environments.
|
|
26
|
+
def initialize(apps=nil, env=nil)
|
|
27
|
+
@logger = Log4r::Logger.new("acceptance::isolated_environment")
|
|
28
|
+
|
|
29
|
+
@apps = apps || {}
|
|
30
|
+
@env = env || {}
|
|
31
|
+
|
|
32
|
+
# Create a temporary directory for our work
|
|
33
|
+
@tempdir = Tempdir.new("vagrant")
|
|
34
|
+
@logger.info("Initialize isolated environment: #{@tempdir.path}")
|
|
35
|
+
|
|
36
|
+
# Setup the home and working directories
|
|
37
|
+
@homedir = Pathname.new(File.join(@tempdir.path, "home"))
|
|
38
|
+
@workdir = Pathname.new(File.join(@tempdir.path, "work"))
|
|
39
|
+
|
|
40
|
+
@homedir.mkdir
|
|
41
|
+
@workdir.mkdir
|
|
42
|
+
|
|
43
|
+
# Set the home directory and virtualbox home directory environmental
|
|
44
|
+
# variables so that Vagrant and VirtualBox see the proper paths here.
|
|
45
|
+
@env["HOME"] = @homedir.to_s
|
|
46
|
+
@env["VBOX_USER_HOME"] = @homedir.to_s
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Executes a command in the context of this isolated environment.
|
|
50
|
+
# Any command executed will therefore see our temporary directory
|
|
51
|
+
# as the home directory.
|
|
52
|
+
def execute(command, *argN)
|
|
53
|
+
command = replace_command(command)
|
|
54
|
+
|
|
55
|
+
# Get the hash options passed to this method
|
|
56
|
+
options = argN.last.is_a?(Hash) ? argN.pop : {}
|
|
57
|
+
timeout = options.delete(:timeout)
|
|
58
|
+
|
|
59
|
+
# Build a child process to run this command. For the stdout/stderr
|
|
60
|
+
# we use pipes so that we can select() on it and block and stream
|
|
61
|
+
# data in as it comes.
|
|
62
|
+
@logger.info("Executing: #{command} #{argN.inspect}. Output will stream in...")
|
|
63
|
+
process = ChildProcess.build(command, *argN)
|
|
64
|
+
stdout, stdout_writer = IO.pipe
|
|
65
|
+
process.io.stdout = stdout_writer
|
|
66
|
+
|
|
67
|
+
stderr, stderr_writer = IO.pipe
|
|
68
|
+
process.io.stderr = stderr_writer
|
|
69
|
+
process.duplex = true
|
|
70
|
+
|
|
71
|
+
@env.each do |k, v|
|
|
72
|
+
process.environment[k] = v
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
Dir.chdir(@workdir.to_s) do
|
|
76
|
+
process.start
|
|
77
|
+
process.io.stdin.sync = true
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Close our side of the pipes, since we're just reading
|
|
81
|
+
stdout_writer.close
|
|
82
|
+
stderr_writer.close
|
|
83
|
+
|
|
84
|
+
# Create a hash to store all the data we see.
|
|
85
|
+
io_data = { stdout => "", stderr => "" }
|
|
86
|
+
|
|
87
|
+
# Record the start time for timeout purposes
|
|
88
|
+
start_time = Time.now.to_i
|
|
89
|
+
|
|
90
|
+
@logger.debug("Selecting on IO...")
|
|
91
|
+
while true
|
|
92
|
+
results = IO.select([stdout, stderr],
|
|
93
|
+
[process.io.stdin], nil, timeout || 5)
|
|
94
|
+
|
|
95
|
+
# Check if we have exceeded our timeout from waiting on a select()
|
|
96
|
+
raise TimeoutExceeded, process.pid if timeout && (Time.now.to_i - start_time) > timeout
|
|
97
|
+
|
|
98
|
+
# Check the readers first to see if they're ready
|
|
99
|
+
readers = results[0]
|
|
100
|
+
if !readers.empty?
|
|
101
|
+
begin
|
|
102
|
+
readers.each do |r|
|
|
103
|
+
data = r.read_nonblock(1024)
|
|
104
|
+
io_data[r] += data
|
|
105
|
+
io_name = r == stdout ? "stdout" : "stderr"
|
|
106
|
+
@logger.debug(data)
|
|
107
|
+
yield io_name.to_sym, data if block_given?
|
|
108
|
+
end
|
|
109
|
+
rescue IO::WaitReadable
|
|
110
|
+
# This just means the IO wasn't actually ready and we should
|
|
111
|
+
# wait some more. So we just let this pass through.
|
|
112
|
+
rescue EOFError
|
|
113
|
+
# Process exited, so break out of this while loop
|
|
114
|
+
break
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Check if the process exited in order to break the loop before
|
|
119
|
+
# we try to see if any stdin is ready.
|
|
120
|
+
break if process.exited?
|
|
121
|
+
|
|
122
|
+
# Check the writers to see if they're ready, and notify any listeners
|
|
123
|
+
if !results[1].empty?
|
|
124
|
+
yield :stdin, process.io.stdin if block_given?
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Continually try to wait for the process to end, but do so asynchronously
|
|
129
|
+
# so that we can also check to see if we have exceeded a timeout.
|
|
130
|
+
begin
|
|
131
|
+
# If a timeout is not set, we set a very large timeout to
|
|
132
|
+
# simulate "forever"
|
|
133
|
+
@logger.debug("Waiting for process to exit...")
|
|
134
|
+
remaining = (timeout || 32000) - (Time.now.to_i - start_time)
|
|
135
|
+
remaining = 0 if remaining < 0
|
|
136
|
+
process.poll_for_exit(remaining)
|
|
137
|
+
rescue ChildProcess::TimeoutError
|
|
138
|
+
raise TimeoutExceeded, process.pid
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
@logger.debug("Exit status: #{process.exit_code}")
|
|
142
|
+
return ExecuteProcess.new(process.exit_code, io_data[stdout], io_data[stderr])
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
# Closes the environment, cleans up the temporary directories, etc.
|
|
146
|
+
def close
|
|
147
|
+
# Only delete virtual machines if VBoxSVC is running, meaning
|
|
148
|
+
# that something related to VirtualBox started running in this
|
|
149
|
+
# environment.
|
|
150
|
+
delete_virtual_machines if VirtualBox.find_vboxsvc
|
|
151
|
+
|
|
152
|
+
# Delete the temporary directory
|
|
153
|
+
@logger.info("Removing isolated environment: #{@tempdir.path}")
|
|
154
|
+
FileUtils.rm_rf(@tempdir.path)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def delete_virtual_machines
|
|
158
|
+
# Delete all virtual machines
|
|
159
|
+
@logger.debug("Finding all virtual machines")
|
|
160
|
+
execute("VBoxManage", "list", "vms").stdout.lines.each do |line|
|
|
161
|
+
data = /^"(?<name>.+?)" {(?<uuid>.+?)}$/.match(line)
|
|
162
|
+
|
|
163
|
+
begin
|
|
164
|
+
@logger.debug("Removing VM: #{data[:name]}")
|
|
165
|
+
|
|
166
|
+
# We add a timeout onto this because sometimes for seemingly no
|
|
167
|
+
# reason it will simply freeze, although the VM is successfully
|
|
168
|
+
# "aborted." The timeout gets around this strange behavior.
|
|
169
|
+
execute("VBoxManage", "controlvm", data[:uuid], "poweroff", :timeout => 5)
|
|
170
|
+
rescue TimeoutExceeded => e
|
|
171
|
+
@logger.info("Failed to poweroff VM '#{data[:uuid]}'. Killing process.")
|
|
172
|
+
|
|
173
|
+
# Kill the process and wait a bit for it to disappear
|
|
174
|
+
Process.kill('KILL', e.pid)
|
|
175
|
+
Process.waitpid2(e.pid)
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
sleep 0.5
|
|
179
|
+
|
|
180
|
+
result = execute("VBoxManage", "unregistervm", data[:uuid], "--delete")
|
|
181
|
+
raise Exception, "VM unregistration failed!" if result.exit_status != 0
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
@logger.info("Removed all virtual machines")
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# This replaces a command with a replacement defined when this
|
|
188
|
+
# isolated environment was initialized. If nothing was defined,
|
|
189
|
+
# then the command itself is returned.
|
|
190
|
+
def replace_command(command)
|
|
191
|
+
return @apps[command] if @apps.has_key?(command)
|
|
192
|
+
return command
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# This class represents a process which has run via the IsolatedEnvironment.
|
|
197
|
+
# This is a readonly structure that can be used to inspect the exit status,
|
|
198
|
+
# stdout, stderr, etc. from the process which ran.
|
|
199
|
+
class ExecuteProcess
|
|
200
|
+
attr_reader :exit_status
|
|
201
|
+
attr_reader :stdout
|
|
202
|
+
attr_reader :stderr
|
|
203
|
+
|
|
204
|
+
def initialize(exit_status, stdout, stderr)
|
|
205
|
+
@exit_status = exit_status
|
|
206
|
+
@stdout = stdout
|
|
207
|
+
@stderr = stderr
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def success?
|
|
211
|
+
@exit_status == 0
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# This exception is raised if the timeout for a process is exceeded.
|
|
216
|
+
class TimeoutExceeded < StandardError
|
|
217
|
+
attr_reader :pid
|
|
218
|
+
|
|
219
|
+
def initialize(pid)
|
|
220
|
+
@pid = pid
|
|
221
|
+
|
|
222
|
+
super()
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|