New abstraction: Systems. Updated config and environment to properly load configured system.
This allows the OS-specific tasks to be pulled out into separate classes, so that other systems could potentially be supported. For now, a "Linux" system has been created.
This commit is contained in:
parent
1429723277
commit
95938c652d
|
@ -21,6 +21,7 @@ Vagrant::Config.run do |config|
|
||||||
config.vm.shared_folder_uid = nil
|
config.vm.shared_folder_uid = nil
|
||||||
config.vm.shared_folder_gid = nil
|
config.vm.shared_folder_gid = nil
|
||||||
config.vm.boot_mode = "vrdp"
|
config.vm.boot_mode = "vrdp"
|
||||||
|
config.vm.system = :linux
|
||||||
|
|
||||||
config.package.name = 'vagrant'
|
config.package.name = 'vagrant'
|
||||||
config.package.extension = '.box'
|
config.package.extension = '.box'
|
||||||
|
|
|
@ -86,6 +86,7 @@ module Vagrant
|
||||||
attr_accessor :provisioner
|
attr_accessor :provisioner
|
||||||
attr_accessor :shared_folder_uid
|
attr_accessor :shared_folder_uid
|
||||||
attr_accessor :shared_folder_gid
|
attr_accessor :shared_folder_gid
|
||||||
|
attr_accessor :system
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@forwarded_ports = {}
|
@forwarded_ports = {}
|
||||||
|
|
|
@ -174,8 +174,7 @@ module Vagrant
|
||||||
return if !root_path || !File.file?(dotfile_path)
|
return if !root_path || !File.file?(dotfile_path)
|
||||||
|
|
||||||
File.open(dotfile_path) do |f|
|
File.open(dotfile_path) do |f|
|
||||||
@vm = Vagrant::VM.find(f.read)
|
@vm = Vagrant::VM.find(f.read, self)
|
||||||
@vm.env = self if @vm
|
|
||||||
end
|
end
|
||||||
rescue Errno::ENOENT
|
rescue Errno::ENOENT
|
||||||
@vm = nil
|
@vm = nil
|
||||||
|
@ -206,9 +205,7 @@ module Vagrant
|
||||||
# in {Command.up}. This will very likely be refactored at a later
|
# in {Command.up}. This will very likely be refactored at a later
|
||||||
# time.
|
# time.
|
||||||
def create_vm
|
def create_vm
|
||||||
@vm = VM.new
|
@vm = VM.new(self)
|
||||||
@vm.env = self
|
|
||||||
@vm
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Persists this environment's VM to the dotfile so it can be
|
# Persists this environment's VM to the dotfile so it can be
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
module Vagrant
|
||||||
|
module Systems
|
||||||
|
# The base class for a "system." A system represents an installed
|
||||||
|
# operating system on a given box. There are some portions of
|
||||||
|
# Vagrant which are fairly OS-specific (such as mounting shared
|
||||||
|
# folders) and while the number is few, this abstraction allows
|
||||||
|
# more obscure operating systems to be installed without having
|
||||||
|
# to directly modify Vagrant internals.
|
||||||
|
#
|
||||||
|
# Subclasses of the system base class are expected to implement
|
||||||
|
# all the methods. These methods are described in the comments
|
||||||
|
# above their definition.
|
||||||
|
#
|
||||||
|
# **This is by no means a complete specification. The methods
|
||||||
|
# required by systems can and will change at any time. Any
|
||||||
|
# changes will be noted on release notes.**
|
||||||
|
class Base
|
||||||
|
include Vagrant::Util
|
||||||
|
|
||||||
|
# The VM which this system is tied to.
|
||||||
|
attr_reader :vm
|
||||||
|
|
||||||
|
# Initializes the system. Any subclasses MUST make sure this
|
||||||
|
# method is called on the parent. Therefore, if a subclass overrides
|
||||||
|
# `initialize`, then you must call `super`.
|
||||||
|
def initialize(vm)
|
||||||
|
@vm = vm
|
||||||
|
end
|
||||||
|
|
||||||
|
# Mounts a shared folder. This method is called by the shared
|
||||||
|
# folder action with an open SSH session (passed in as `ssh`).
|
||||||
|
# This method should create, mount, and properly set permissions
|
||||||
|
# on the shared folder. This method should also properly
|
||||||
|
# adhere to any configuration values such as `shared_folder_uid`
|
||||||
|
# on `config.vm`.
|
||||||
|
#
|
||||||
|
# @param [Object] ssh The Net::SSH session.
|
||||||
|
# @param [String] name The name of the shared folder.
|
||||||
|
# @param [String] guestpath The path on the machine which the user
|
||||||
|
# wants the folder mounted.
|
||||||
|
def mount_shared_folder(ssh, name, guestpath); end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,46 @@
|
||||||
|
module Vagrant
|
||||||
|
module Systems
|
||||||
|
# A general Vagrant system implementation for "linux." In general,
|
||||||
|
# any linux-based OS will work fine with this system, although its
|
||||||
|
# not tested exhaustively. BSD or other based systems may work as
|
||||||
|
# well, but that hasn't been tested at all.
|
||||||
|
#
|
||||||
|
# At any rate, this system implementation should server as an
|
||||||
|
# example of how to implement any custom systems necessary.
|
||||||
|
class Linux < Base
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
# Overridden methods
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
def mount_shared_folder(ssh, name, guestpath)
|
||||||
|
ssh.exec!("sudo mkdir -p #{guestpath}")
|
||||||
|
mount_folder(ssh, name, guestpath)
|
||||||
|
ssh.exec!("sudo chown #{vm.env.config.ssh.username} #{guestpath}")
|
||||||
|
end
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
# "Private" methods which assist above methods
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
def mount_folder(ssh, name, guestpath, sleeptime=5)
|
||||||
|
# Determine the permission string to attach to the mount command
|
||||||
|
perms = []
|
||||||
|
perms << "uid=#{vm.env.config.vm.shared_folder_uid}"
|
||||||
|
perms << "gid=#{vm.env.config.vm.shared_folder_gid}"
|
||||||
|
perms = " -o #{perms.join(",")}" if !perms.empty?
|
||||||
|
|
||||||
|
attempts = 0
|
||||||
|
while true
|
||||||
|
result = ssh.exec!("sudo mount -t vboxsf#{perms} #{name} #{guestpath}") do |ch, type, data|
|
||||||
|
# net/ssh returns the value in ch[:result] (based on looking at source)
|
||||||
|
ch[:result] = !!(type == :stderr && data =~ /No such device/i)
|
||||||
|
end
|
||||||
|
|
||||||
|
break unless result
|
||||||
|
|
||||||
|
attempts += 1
|
||||||
|
raise Actions::ActionException.new(:vm_mount_fail) if attempts >= 10
|
||||||
|
sleep sleeptime
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,22 +2,48 @@ module Vagrant
|
||||||
class VM < Actions::Runner
|
class VM < Actions::Runner
|
||||||
include Vagrant::Util
|
include Vagrant::Util
|
||||||
|
|
||||||
attr_accessor :env
|
attr_reader :env
|
||||||
|
attr_reader :system
|
||||||
attr_accessor :vm
|
attr_accessor :vm
|
||||||
attr_accessor :from
|
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
# Finds a virtual machine by a given UUID and either returns
|
# Finds a virtual machine by a given UUID and either returns
|
||||||
# a Vagrant::VM object or returns nil.
|
# a Vagrant::VM object or returns nil.
|
||||||
def find(uuid)
|
def find(uuid, env=nil)
|
||||||
vm = VirtualBox::VM.find(uuid)
|
vm = VirtualBox::VM.find(uuid)
|
||||||
return nil if vm.nil?
|
return nil if vm.nil?
|
||||||
new(vm)
|
new(env, vm)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def initialize(vm=nil)
|
def initialize(env, vm=nil)
|
||||||
|
@env = env
|
||||||
@vm = vm
|
@vm = vm
|
||||||
|
|
||||||
|
load_system!
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_system!
|
||||||
|
system = env.config.vm.system
|
||||||
|
|
||||||
|
if system.is_a?(Class)
|
||||||
|
@system = system.new(self)
|
||||||
|
error_and_exit(:system_invalid_class, :system => system.to_s) unless @system.is_a?(Systems::Base)
|
||||||
|
elsif system.is_a?(Symbol)
|
||||||
|
# Hard-coded internal systems
|
||||||
|
mapping = {
|
||||||
|
:linux => Systems::Linux
|
||||||
|
}
|
||||||
|
|
||||||
|
if !mapping.has_key?(system)
|
||||||
|
error_and_exit(:system_unknown_type, :system => system.to_s)
|
||||||
|
return # for tests
|
||||||
|
end
|
||||||
|
|
||||||
|
@system = mapping[system].new(self)
|
||||||
|
else
|
||||||
|
error_and_exit(:system_unspecified)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def uuid
|
def uuid
|
||||||
|
|
|
@ -103,7 +103,17 @@
|
||||||
For a more detailed guide please consult:
|
For a more detailed guide please consult:
|
||||||
|
|
||||||
http://vagrantup.com/docs/getting-started/windows.html
|
http://vagrantup.com/docs/getting-started/windows.html
|
||||||
|
:system_invalid_class: |-
|
||||||
|
The specified system does not inherit from `Vagrant::Systems::Base`. The
|
||||||
|
specified system class must inherit from this class.
|
||||||
|
|
||||||
|
The specified system class was: <%= system %>
|
||||||
|
:system_unknown_type: |-
|
||||||
|
The specified system type is unknown: <%= system %>. Please change this
|
||||||
|
to a proper value.
|
||||||
|
:system_unspecified: |-
|
||||||
|
A VM system type must be specified! This is done via the `config.vm.system`
|
||||||
|
configuration value. Please read the documentation online for more information.
|
||||||
:virtualbox_import_failure: |-
|
:virtualbox_import_failure: |-
|
||||||
The VM import failed! Try running `VBoxManage import` on the box file
|
The VM import failed! Try running `VBoxManage import` on the box file
|
||||||
manually for more verbose error output.
|
manually for more verbose error output.
|
||||||
|
|
|
@ -41,6 +41,7 @@ class Test::Unit::TestCase
|
||||||
config.vm.forward_port("ssh", 22, 2222)
|
config.vm.forward_port("ssh", 22, 2222)
|
||||||
config.vm.shared_folder_uid = nil
|
config.vm.shared_folder_uid = nil
|
||||||
config.vm.shared_folder_gid = nil
|
config.vm.shared_folder_gid = nil
|
||||||
|
config.vm.system = :linux
|
||||||
|
|
||||||
config.package.name = 'vagrant'
|
config.package.name = 'vagrant'
|
||||||
config.package.extension = '.box'
|
config.package.extension = '.box'
|
||||||
|
|
|
@ -356,11 +356,10 @@ class EnvironmentTest < Test::Unit::TestCase
|
||||||
|
|
||||||
should "loading of the uuid from the dotfile" do
|
should "loading of the uuid from the dotfile" do
|
||||||
vm = mock("vm")
|
vm = mock("vm")
|
||||||
vm.expects(:env=).with(@env)
|
|
||||||
|
|
||||||
filemock = mock("filemock")
|
filemock = mock("filemock")
|
||||||
filemock.expects(:read).returns("foo")
|
filemock.expects(:read).returns("foo")
|
||||||
Vagrant::VM.expects(:find).with("foo").returns(vm)
|
Vagrant::VM.expects(:find).with("foo", @env).returns(vm)
|
||||||
File.expects(:open).with(@env.dotfile_path).once.yields(filemock)
|
File.expects(:open).with(@env.dotfile_path).once.yields(filemock)
|
||||||
File.expects(:file?).with(@env.dotfile_path).once.returns(true)
|
File.expects(:file?).with(@env.dotfile_path).once.returns(true)
|
||||||
@env.load_vm!
|
@env.load_vm!
|
||||||
|
@ -371,7 +370,7 @@ class EnvironmentTest < Test::Unit::TestCase
|
||||||
should "not set the environment if the VM is nil" do
|
should "not set the environment if the VM is nil" do
|
||||||
filemock = mock("filemock")
|
filemock = mock("filemock")
|
||||||
filemock.expects(:read).returns("foo")
|
filemock.expects(:read).returns("foo")
|
||||||
Vagrant::VM.expects(:find).with("foo").returns(nil)
|
Vagrant::VM.expects(:find).with("foo", @env).returns(nil)
|
||||||
File.expects(:open).with(@env.dotfile_path).once.yields(filemock)
|
File.expects(:open).with(@env.dotfile_path).once.yields(filemock)
|
||||||
File.expects(:file?).with(@env.dotfile_path).once.returns(true)
|
File.expects(:file?).with(@env.dotfile_path).once.returns(true)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
require File.join(File.dirname(__FILE__), '..', '..', 'test_helper')
|
||||||
|
|
||||||
|
class LinuxSystemTest < Test::Unit::TestCase
|
||||||
|
setup do
|
||||||
|
@klass = Vagrant::Systems::Linux
|
||||||
|
|
||||||
|
@vm = mock("vm")
|
||||||
|
@vm.stubs(:env).returns(mock_environment)
|
||||||
|
@instance = @klass.new(@vm)
|
||||||
|
end
|
||||||
|
|
||||||
|
context "mounting shared folders" do
|
||||||
|
setup do
|
||||||
|
@ssh = mock("ssh")
|
||||||
|
@name = "foo"
|
||||||
|
@guestpath = "/bar"
|
||||||
|
end
|
||||||
|
|
||||||
|
should "create the dir, mount the folder, then set permissions" do
|
||||||
|
mount_seq = sequence("mount_seq")
|
||||||
|
@ssh.expects(:exec!).with("sudo mkdir -p #{@guestpath}").in_sequence(mount_seq)
|
||||||
|
@instance.expects(:mount_folder).with(@ssh, @name, @guestpath).in_sequence(mount_seq)
|
||||||
|
@ssh.expects(:exec!).with("sudo chown #{@vm.env.config.ssh.username} #{@guestpath}").in_sequence(mount_seq)
|
||||||
|
|
||||||
|
@instance.mount_shared_folder(@ssh, @name, @guestpath)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
# "Private" methods tests
|
||||||
|
#-------------------------------------------------------------------
|
||||||
|
context "mounting the main folder" do
|
||||||
|
setup do
|
||||||
|
@ssh = mock("ssh")
|
||||||
|
@name = "foo"
|
||||||
|
@guestpath = "bar"
|
||||||
|
@sleeptime = 0
|
||||||
|
@limit = 10
|
||||||
|
|
||||||
|
@success_return = false
|
||||||
|
end
|
||||||
|
|
||||||
|
def mount_folder
|
||||||
|
@instance.mount_folder(@ssh, @name, @guestpath, @sleeptime)
|
||||||
|
end
|
||||||
|
|
||||||
|
should "execute the proper mount command" do
|
||||||
|
@ssh.expects(:exec!).with("sudo mount -t vboxsf -o uid=#{@vm.env.config.ssh.username},gid=#{@vm.env.config.ssh.username} #{@name} #{@guestpath}").returns(@success_return)
|
||||||
|
mount_folder
|
||||||
|
end
|
||||||
|
|
||||||
|
should "test type of text and text string to detect error" do
|
||||||
|
data = mock("data")
|
||||||
|
data.expects(:[]=).with(:result, !@success_return)
|
||||||
|
|
||||||
|
@ssh.expects(:exec!).yields(data, :stderr, "No such device").returns(@success_return)
|
||||||
|
mount_folder
|
||||||
|
end
|
||||||
|
|
||||||
|
should "test type of text and test string to detect success" do
|
||||||
|
data = mock("data")
|
||||||
|
data.expects(:[]=).with(:result, @success_return)
|
||||||
|
|
||||||
|
@ssh.expects(:exec!).yields(data, :stdout, "Nothing such device").returns(@success_return)
|
||||||
|
mount_folder
|
||||||
|
end
|
||||||
|
|
||||||
|
should "raise an ActionException if the command fails constantly" do
|
||||||
|
@ssh.expects(:exec!).times(@limit).returns(!@success_return)
|
||||||
|
|
||||||
|
assert_raises(Vagrant::Actions::ActionException) {
|
||||||
|
mount_folder
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
should "not raise any exception if the command succeeded" do
|
||||||
|
@ssh.expects(:exec!).once.returns(@success_return)
|
||||||
|
|
||||||
|
assert_nothing_raised {
|
||||||
|
mount_folder
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
should "add uid AND gid to mount" do
|
||||||
|
uid = "foo"
|
||||||
|
gid = "bar"
|
||||||
|
env = mock_environment do |config|
|
||||||
|
config.vm.shared_folder_uid = uid
|
||||||
|
config.vm.shared_folder_gid = gid
|
||||||
|
end
|
||||||
|
|
||||||
|
@vm.stubs(:env).returns(env)
|
||||||
|
|
||||||
|
@ssh.expects(:exec!).with("sudo mount -t vboxsf -o uid=#{uid},gid=#{gid} #{@name} #{@guestpath}").returns(@success_return)
|
||||||
|
mount_folder
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -14,7 +14,7 @@ class VMTest < Test::Unit::TestCase
|
||||||
|
|
||||||
context "being an action runner" do
|
context "being an action runner" do
|
||||||
should "be an action runner" do
|
should "be an action runner" do
|
||||||
vm = Vagrant::VM.new
|
vm = Vagrant::VM.new(@env)
|
||||||
assert vm.is_a?(Vagrant::Actions::Runner)
|
assert vm.is_a?(Vagrant::Actions::Runner)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -27,7 +27,7 @@ class VMTest < Test::Unit::TestCase
|
||||||
|
|
||||||
should "return a Vagrant::VM object for that VM otherwise" do
|
should "return a Vagrant::VM object for that VM otherwise" do
|
||||||
VirtualBox::VM.expects(:find).with("foo").returns("bar")
|
VirtualBox::VM.expects(:find).with("foo").returns("bar")
|
||||||
result = Vagrant::VM.find("foo")
|
result = Vagrant::VM.find("foo", mock_environment)
|
||||||
assert result.is_a?(Vagrant::VM)
|
assert result.is_a?(Vagrant::VM)
|
||||||
assert_equal "bar", result.vm
|
assert_equal "bar", result.vm
|
||||||
end
|
end
|
||||||
|
@ -35,10 +35,63 @@ class VMTest < Test::Unit::TestCase
|
||||||
|
|
||||||
context "vagrant VM instance" do
|
context "vagrant VM instance" do
|
||||||
setup do
|
setup do
|
||||||
@vm = Vagrant::VM.new(@mock_vm)
|
@vm = Vagrant::VM.new(@env, @mock_vm)
|
||||||
@mock_vm.stubs(:uuid).returns("foo")
|
@mock_vm.stubs(:uuid).returns("foo")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context "loading associated system" do
|
||||||
|
should "error and exit if system is not specified" do
|
||||||
|
@vm.env.config.vm.system = nil
|
||||||
|
|
||||||
|
@vm.expects(:error_and_exit).with(:system_unspecified).once
|
||||||
|
@vm.load_system!
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with a class" do
|
||||||
|
class FakeSystemClass
|
||||||
|
def initialize(vm); end
|
||||||
|
end
|
||||||
|
|
||||||
|
should "initialize class if given" do
|
||||||
|
@vm.env.config.vm.system = Vagrant::Systems::Linux
|
||||||
|
|
||||||
|
@vm.expects(:error_and_exit).never
|
||||||
|
@vm.load_system!
|
||||||
|
|
||||||
|
assert @vm.system.is_a?(Vagrant::Systems::Linux)
|
||||||
|
end
|
||||||
|
|
||||||
|
should "error and exit if class has invalid parent" do
|
||||||
|
@vm.env.config.vm.system = FakeSystemClass
|
||||||
|
@vm.expects(:error_and_exit).with(:system_invalid_class, :system => @vm.env.config.vm.system.to_s).once
|
||||||
|
@vm.load_system!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "with a symbol" do
|
||||||
|
should "initialize proper symbols" do
|
||||||
|
valid = {
|
||||||
|
:linux => Vagrant::Systems::Linux
|
||||||
|
}
|
||||||
|
|
||||||
|
valid.each do |symbol, klass|
|
||||||
|
@vm.env.config.vm.system = symbol
|
||||||
|
@vm.expects(:error_and_exit).never
|
||||||
|
@vm.load_system!
|
||||||
|
|
||||||
|
assert @vm.system.is_a?(klass)
|
||||||
|
assert_equal @vm, @vm.system.vm
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
should "error and exit with invalid symbol" do
|
||||||
|
@vm.env.config.vm.system = :shall_never_exist
|
||||||
|
@vm.expects(:error_and_exit).with(:system_unknown_type, :system => @vm.env.config.vm.system.to_s).once
|
||||||
|
@vm.load_system!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "uuid" do
|
context "uuid" do
|
||||||
should "call UUID on VM object" do
|
should "call UUID on VM object" do
|
||||||
uuid = mock("uuid")
|
uuid = mock("uuid")
|
||||||
|
|
Loading…
Reference in New Issue