Merge pull request #2784 from mitchellh/f-host-capabilities
Host Capabilities
This commit is contained in:
commit
9a419c3f63
|
@ -93,7 +93,7 @@ module Vagrant
|
|||
autoload :Environment, 'vagrant/environment'
|
||||
autoload :Errors, 'vagrant/errors'
|
||||
autoload :Guest, 'vagrant/guest'
|
||||
autoload :Hosts, 'vagrant/hosts'
|
||||
autoload :Host, 'vagrant/host'
|
||||
autoload :Machine, 'vagrant/machine'
|
||||
autoload :MachineState, 'vagrant/machine_state'
|
||||
autoload :Plugin, 'vagrant/plugin'
|
||||
|
|
|
@ -487,22 +487,34 @@ module Vagrant
|
|||
def host
|
||||
return @host if defined?(@host)
|
||||
|
||||
# Attempt to figure out the host class. Note that the order
|
||||
# matters here, so please don't touch. Specifically: The symbol
|
||||
# check is done after the detect check because the symbol check
|
||||
# will return nil, and we don't want to trigger a detect load.
|
||||
# Determine the host class to use. ":detect" is an old Vagrant config
|
||||
# that shouldn't be valid anymore, but we respect it here by assuming
|
||||
# its old behavior. No need to deprecate this because I thin it is
|
||||
# fairly harmless.
|
||||
host_klass = config_global.vagrant.host
|
||||
if host_klass.nil? || host_klass == :detect
|
||||
hosts = Vagrant.plugin("2").manager.hosts.to_hash
|
||||
host_klass = nil if host_klass == :detect
|
||||
|
||||
# Get the flattened list of available hosts
|
||||
host_klass = Hosts.detect(hosts)
|
||||
begin
|
||||
@host = Host.new(
|
||||
host_klass,
|
||||
Vagrant.plugin("2").manager.hosts,
|
||||
Vagrant.plugin("2").manager.host_capabilities,
|
||||
self)
|
||||
rescue Errors::CapabilityHostNotDetected
|
||||
# If the auto-detect failed, then we create a brand new host
|
||||
# with no capabilities and use that. This should almost never happen
|
||||
# since Vagrant works on most host OS's now, so this is a "slow path"
|
||||
klass = Class.new(Vagrant.plugin("2", :host)) do
|
||||
def detect?(env); true; end
|
||||
end
|
||||
|
||||
# If no host class is detected, we use the base class.
|
||||
host_klass ||= Vagrant.plugin("2", :host)
|
||||
hosts = { generic: [klass, nil] }
|
||||
host_caps = {}
|
||||
|
||||
@host ||= host_klass.new(@ui)
|
||||
@host = Host.new(:generic, hosts, host_caps, self)
|
||||
rescue Errors::CapabilityHostExplicitNotDetected => e
|
||||
raise Errors::HostExplicitNotDetected, e.extra_data
|
||||
end
|
||||
end
|
||||
|
||||
# Action runner for executing actions in the context of this environment.
|
||||
|
|
|
@ -333,6 +333,10 @@ module Vagrant
|
|||
error_key(:guest_not_detected)
|
||||
end
|
||||
|
||||
class HostExplicitNotDetected < VagrantError
|
||||
error_key(:host_explicit_not_detected)
|
||||
end
|
||||
|
||||
class LinuxMountFailed < VagrantError
|
||||
error_key(:linux_mount_failed)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
require "vagrant/capability_host"
|
||||
|
||||
module Vagrant
|
||||
# This class handles host-OS specific interations. It is responsible for
|
||||
# detecting the proper host OS implementation and delegating capabilities
|
||||
# to plugins.
|
||||
#
|
||||
# See {Guest} for more information on capabilities.
|
||||
class Host
|
||||
include CapabilityHost
|
||||
|
||||
def initialize(host, hosts, capabilities, env)
|
||||
initialize_capabilities!(host, hosts, capabilities, env)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,28 +0,0 @@
|
|||
require 'log4r'
|
||||
|
||||
module Vagrant
|
||||
module Hosts
|
||||
# This method detects the correct host based on the `match?` methods
|
||||
# implemented in the registered hosts.
|
||||
#
|
||||
# @param [Hash] registry Hash mapping key to host class
|
||||
def self.detect(registry)
|
||||
logger = Log4r::Logger.new("vagrant::hosts")
|
||||
|
||||
# Sort the hosts by their precedence
|
||||
host_klasses = registry.values.sort_by { |a| a.precedence }.reverse
|
||||
logger.debug("Host path search classes: #{host_klasses.inspect}")
|
||||
|
||||
# Test for matches and return the host class that matches
|
||||
host_klasses.each do |klass|
|
||||
if klass.match?
|
||||
logger.info("Host class: #{klass}")
|
||||
return klass
|
||||
end
|
||||
end
|
||||
|
||||
# No matches found...
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
|
@ -26,6 +26,16 @@ module Vagrant
|
|||
# @return [Hash<Symbol, Registry>]
|
||||
attr_reader :guest_capabilities
|
||||
|
||||
# This contains all the hosts and their parents.
|
||||
#
|
||||
# @return [Registry<Symbol, Array<Class, Symbol>>]
|
||||
attr_reader :hosts
|
||||
|
||||
# This contains all the registered host capabilities.
|
||||
#
|
||||
# @return [Hash<Symbol, Registry>]
|
||||
attr_reader :host_capabilities
|
||||
|
||||
# This contains all the provider plugins by name, and returns
|
||||
# the provider class and options.
|
||||
#
|
||||
|
@ -44,6 +54,8 @@ module Vagrant
|
|||
@configs = Hash.new { |h, k| h[k] = Registry.new }
|
||||
@guests = Registry.new
|
||||
@guest_capabilities = Hash.new { |h, k| h[k] = Registry.new }
|
||||
@hosts = Registry.new
|
||||
@host_capabilities = Hash.new { |h, k| h[k] = Registry.new }
|
||||
@providers = Registry.new
|
||||
@synced_folders = Registry.new
|
||||
end
|
||||
|
|
|
@ -10,56 +10,9 @@ module Vagrant
|
|||
# matches the host class.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def self.match?
|
||||
nil
|
||||
end
|
||||
|
||||
# The precedence of the host when checking for matches. This is to
|
||||
# allow certain host such as generic OS's ("Linux", "BSD", etc.)
|
||||
# to be specified last.
|
||||
#
|
||||
# The hosts with the higher numbers will be checked first.
|
||||
#
|
||||
# If you're implementing a basic host, you can probably ignore this.
|
||||
def self.precedence
|
||||
5
|
||||
end
|
||||
|
||||
# Initializes a new host class.
|
||||
#
|
||||
# The only required parameter is a UI object so that the host
|
||||
# objects have some way to communicate with the outside world.
|
||||
#
|
||||
# @param [UI] ui UI for the hosts to output to.
|
||||
def initialize(ui)
|
||||
@ui = ui
|
||||
end
|
||||
|
||||
# Returns true of false denoting whether or not this host supports
|
||||
# NFS shared folder setup. This method ideally should verify that
|
||||
# NFS is installed.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def nfs?
|
||||
def detect?(env)
|
||||
false
|
||||
end
|
||||
|
||||
# Exports the given hash of folders via NFS.
|
||||
#
|
||||
# @param [String] id A unique ID that is guaranteed to be unique to
|
||||
# match these sets of folders.
|
||||
# @param [String] ip IP of the guest machine.
|
||||
# @param [Hash] folders Shared folders to sync.
|
||||
def nfs_export(id, ip, folders)
|
||||
end
|
||||
|
||||
# Prunes any NFS exports made by Vagrant which aren't in the set
|
||||
# of valid ids given.
|
||||
#
|
||||
# @param [Array<String>] valid_ids Valid IDs that should not be
|
||||
# pruned.
|
||||
def nfs_prune(valid_ids)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -89,17 +89,33 @@ module Vagrant
|
|||
results
|
||||
end
|
||||
|
||||
# This returns all registered host classes.
|
||||
# This returns all the registered guests.
|
||||
#
|
||||
# @return [Hash]
|
||||
def hosts
|
||||
Registry.new.tap do |result|
|
||||
@registered.each do |plugin|
|
||||
result.merge!(plugin.host)
|
||||
result.merge!(plugin.components.hosts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This returns all the registered host capabilities.
|
||||
#
|
||||
# @return [Hash]
|
||||
def host_capabilities
|
||||
results = Hash.new { |h, k| h[k] = Registry.new }
|
||||
|
||||
@registered.each do |plugin|
|
||||
plugin.components.host_capabilities.each do |host, caps|
|
||||
results[host].merge!(caps)
|
||||
end
|
||||
end
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
|
||||
# This returns all registered providers.
|
||||
#
|
||||
# @return [Hash]
|
||||
|
|
|
@ -135,7 +135,7 @@ module Vagrant
|
|||
#
|
||||
# @param [String] name Name of the guest.
|
||||
# @param [String] parent Name of the parent guest (if any)
|
||||
def self.guest(name=UNSET_VALUE, parent=nil, &block)
|
||||
def self.guest(name, parent=nil, &block)
|
||||
components.guests.register(name.to_sym) do
|
||||
parent = parent.to_sym if parent
|
||||
|
||||
|
@ -160,14 +160,26 @@ module Vagrant
|
|||
# the given key.
|
||||
#
|
||||
# @param [String] name Name of the host.
|
||||
def self.host(name=UNSET_VALUE, &block)
|
||||
data[:hosts] ||= Registry.new
|
||||
# @param [String] parent Name of the parent host (if any)
|
||||
def self.host(name, parent=nil, &block)
|
||||
components.hosts.register(name.to_sym) do
|
||||
parent = parent.to_sym if parent
|
||||
|
||||
# Register a new host class only if a name was given
|
||||
data[:hosts].register(name.to_sym, &block) if name != UNSET_VALUE
|
||||
[block.call, parent]
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# Return the registry
|
||||
data[:hosts]
|
||||
# Defines a capability for the given host. The block should return
|
||||
# a class/module that has a method with the capability name, ready
|
||||
# to be executed. This means that if it is an instance method,
|
||||
# the block should return an instance of the class.
|
||||
#
|
||||
# @param [String] host The name of the host
|
||||
# @param [String] cap The name of the capability
|
||||
def self.host_capability(host, cap, &block)
|
||||
components.host_capabilities[host.to_sym].register(cap.to_sym, &block)
|
||||
nil
|
||||
end
|
||||
|
||||
# Registers additional providers to be available.
|
||||
|
|
|
@ -8,5 +8,6 @@ module Vagrant
|
|||
autoload :SafeExec, 'vagrant/util/safe_exec'
|
||||
autoload :StackedProcRunner, 'vagrant/util/stacked_proc_runner'
|
||||
autoload :TemplateRenderer, 'vagrant/util/template_renderer'
|
||||
autoload :Subprocess, 'vagrant/util/subprocess'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
module VagrantPlugins
|
||||
module HostArch
|
||||
module Cap
|
||||
class NFS
|
||||
def self.nfs_check_command(env)
|
||||
if systemd?
|
||||
return "/usr/sbin/systemctl status nfsd"
|
||||
else
|
||||
return "/etc/rc.d/nfs-server status"
|
||||
end
|
||||
end
|
||||
|
||||
def self.nfs_start_command(env)
|
||||
if systemd?
|
||||
return "/usr/sbin/systemctl start nfsd rpc-idmapd rpc-mountd rpcbind"
|
||||
else
|
||||
return "sh -c 'for s in {rpcbind,nfs-common,nfs-server}; do /etc/rc.d/$s start; done'"
|
||||
end
|
||||
end
|
||||
|
||||
def self.nfs_installed(environment)
|
||||
Kernel.system("grep -Fq nfs /proc/filesystems")
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# This tests to see if systemd is used on the system. This is used
|
||||
# in newer versions of Arch, and requires a change in behavior.
|
||||
def self.systemd?
|
||||
`ps -o comm= 1`.chomp == 'systemd'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,46 +1,11 @@
|
|||
require "vagrant"
|
||||
|
||||
require Vagrant.source_root.join("plugins/hosts/linux/host")
|
||||
|
||||
module VagrantPlugins
|
||||
module HostArch
|
||||
class Host < VagrantPlugins::HostLinux::Host
|
||||
def self.match?
|
||||
class Host < Vagrant.plugin("2", :host)
|
||||
def detect?(env)
|
||||
File.exist?("/etc/arch-release")
|
||||
end
|
||||
|
||||
def self.nfs?
|
||||
# HostLinux checks for nfsd which returns false unless the
|
||||
# services are actively started. This leads to a misleading
|
||||
# error message. Checking for nfs (no d) seems to work
|
||||
# regardless. Also fixes useless use of cat, regex, and
|
||||
# redirection.
|
||||
Kernel.system("grep -Fq nfs /proc/filesystems")
|
||||
end
|
||||
|
||||
# Normal, mid-range precedence.
|
||||
def self.precedence
|
||||
5
|
||||
end
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
if systemd?
|
||||
@nfs_check_command = "/usr/sbin/systemctl status nfsd"
|
||||
@nfs_start_command = "/usr/sbin/systemctl start nfsd rpc-idmapd rpc-mountd rpcbind"
|
||||
else
|
||||
@nfs_check_command = "/etc/rc.d/nfs-server status"
|
||||
@nfs_start_command = "sh -c 'for s in {rpcbind,nfs-common,nfs-server}; do /etc/rc.d/$s start; done'"
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# This tests to see if systemd is used on the system. This is used
|
||||
# in newer versions of Arch, and requires a change in behavior.
|
||||
def systemd?
|
||||
`ps -o comm= 1`.chomp == 'systemd'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,10 +6,27 @@ module VagrantPlugins
|
|||
name "Arch host"
|
||||
description "Arch host support."
|
||||
|
||||
host("arch") do
|
||||
require File.expand_path("../host", __FILE__)
|
||||
host("arch", "linux") do
|
||||
require_relative "host"
|
||||
Host
|
||||
end
|
||||
|
||||
host_capability("arch", "nfs_installed") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
# Linux-specific helpers we need to determine paths that can
|
||||
# be overriden.
|
||||
host_capability("arch", "nfs_check_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("arch", "nfs_start_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,183 @@
|
|||
require "log4r"
|
||||
|
||||
require "vagrant/util"
|
||||
|
||||
module VagrantPlugins
|
||||
module HostBSD
|
||||
module Cap
|
||||
class NFS
|
||||
extend Vagrant::Util::Retryable
|
||||
|
||||
def self.nfs_export(environment, ui, id, ips, folders)
|
||||
nfs_exports_template = environment.host.capability(:nfs_exports_template)
|
||||
nfs_restart_command = environment.host.capability(:nfs_restart_command)
|
||||
logger = Log4r::Logger.new("vagrant::hosts::bsd")
|
||||
|
||||
nfs_checkexports! if File.file?("/etc/exports")
|
||||
|
||||
# We need to build up mapping of directories that are enclosed
|
||||
# within each other because the exports file has to have subdirectories
|
||||
# of an exported directory on the same line. e.g.:
|
||||
#
|
||||
# "/foo" "/foo/bar" ...
|
||||
# "/bar"
|
||||
#
|
||||
# We build up this mapping within the following hash.
|
||||
logger.debug("Compiling map of sub-directories for NFS exports...")
|
||||
dirmap = {}
|
||||
folders.each do |_, opts|
|
||||
hostpath = opts[:hostpath].dup
|
||||
hostpath.gsub!('"', '\"')
|
||||
|
||||
found = false
|
||||
dirmap.each do |dirs, diropts|
|
||||
dirs.each do |dir|
|
||||
if dir.start_with?(hostpath) || hostpath.start_with?(dir)
|
||||
# TODO: verify opts and diropts are _identical_, raise an error
|
||||
# if not. NFS mandates subdirectories have identical options.
|
||||
dirs << hostpath
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
break if found
|
||||
end
|
||||
|
||||
if !found
|
||||
dirmap[[hostpath]] = opts.dup
|
||||
end
|
||||
end
|
||||
|
||||
# Sort all the keys by length so that the directory closest to
|
||||
# the root is exported first.
|
||||
dirmap.each do |dirs, _|
|
||||
dirs.sort_by! { |d| d.length }
|
||||
end
|
||||
|
||||
# Setup the NFS options
|
||||
dirmap.each do |dirs, opts|
|
||||
if !opts[:bsd__nfs_options]
|
||||
opts[:bsd__nfs_options] = ["alldirs"]
|
||||
end
|
||||
|
||||
hasmapall = false
|
||||
opts[:bsd__nfs_options].each do |opt|
|
||||
# mapall/maproot are mutually exclusive, so we have to check
|
||||
# for both here.
|
||||
if opt =~ /^mapall=/ || opt =~ /^maproot=/
|
||||
hasmapall = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if !hasmapall
|
||||
opts[:bsd__nfs_options] << "mapall=#{opts[:map_uid]}:#{opts[:map_gid]}"
|
||||
end
|
||||
|
||||
opts[:bsd__compiled_nfs_options] = opts[:bsd__nfs_options].map do |opt|
|
||||
"-#{opt}"
|
||||
end.join(" ")
|
||||
end
|
||||
|
||||
logger.info("Exporting the following for NFS...")
|
||||
dirmap.each do |dirs, opts|
|
||||
logger.info("NFS DIR: #{dirs.inspect}")
|
||||
logger.info("NFS OPTS: #{opts.inspect}")
|
||||
end
|
||||
|
||||
output = Vagrant::Util::TemplateRenderer.render(nfs_exports_template,
|
||||
:uuid => id,
|
||||
:ips => ips,
|
||||
:folders => dirmap,
|
||||
:user => Process.uid)
|
||||
|
||||
# The sleep ensures that the output is truly flushed before any `sudo`
|
||||
# commands are issued.
|
||||
ui.info I18n.t("vagrant.hosts.bsd.nfs_export")
|
||||
sleep 0.5
|
||||
|
||||
# First, clean up the old entry
|
||||
nfs_cleanup(id)
|
||||
|
||||
# Output the rendered template into the exports
|
||||
output.split("\n").each do |line|
|
||||
line.gsub!('"', '\"')
|
||||
line.gsub!("'", "'\\\\''")
|
||||
system(%Q[sudo -s -- "echo '#{line}' >> /etc/exports"])
|
||||
end
|
||||
|
||||
# We run restart here instead of "update" just in case nfsd
|
||||
# is not starting
|
||||
system(nfs_restart_command)
|
||||
end
|
||||
|
||||
def self.nfs_exports_template(environment)
|
||||
"nfs/exports"
|
||||
end
|
||||
|
||||
def self.nfs_installed(environment)
|
||||
retryable(:tries => 10, :on => TypeError) do
|
||||
system("which nfsd > /dev/null 2>&1")
|
||||
end
|
||||
end
|
||||
|
||||
def self.nfs_prune(environment, ui, valid_ids)
|
||||
return if !File.exist?("/etc/exports")
|
||||
|
||||
logger = Log4r::Logger.new("vagrant::hosts::bsd")
|
||||
logger.info("Pruning invalid NFS entries...")
|
||||
|
||||
output = false
|
||||
user = Process.uid
|
||||
|
||||
File.read("/etc/exports").lines.each do |line|
|
||||
if id = line[/^# VAGRANT-BEGIN:( #{user})? ([A-Za-z0-9-]+?)$/, 2]
|
||||
if valid_ids.include?(id)
|
||||
logger.debug("Valid ID: #{id}")
|
||||
else
|
||||
if !output
|
||||
# We want to warn the user but we only want to output once
|
||||
ui.info I18n.t("vagrant.hosts.bsd.nfs_prune")
|
||||
output = true
|
||||
end
|
||||
|
||||
logger.info("Invalid ID, pruning: #{id}")
|
||||
nfs_cleanup(id)
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue Errno::EACCES
|
||||
raise Vagrant::Errors::NFSCantReadExports
|
||||
end
|
||||
|
||||
def self.nfs_restart_command(environment)
|
||||
"sudo nfsd restart"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def self.nfs_cleanup(id)
|
||||
return if !File.exist?("/etc/exports")
|
||||
|
||||
# Escape sed-sensitive characters:
|
||||
id = id.gsub("/", "\\/")
|
||||
id = id.gsub(".", "\\.")
|
||||
|
||||
user = Process.uid
|
||||
|
||||
# Use sed to just strip out the block of code which was inserted
|
||||
# by Vagrant, and restart NFS.
|
||||
system("sudo sed -E -e '/^# VAGRANT-BEGIN:( #{user})? #{id}/,/^# VAGRANT-END:( #{user})? #{id}/ d' -ibak /etc/exports")
|
||||
end
|
||||
|
||||
def self.nfs_checkexports!
|
||||
r = Vagrant::Util::Subprocess.execute("nfsd", "checkexports")
|
||||
if r.exit_code != 0
|
||||
raise Vagrant::Errors::NFSBadExports, output: r.stderr
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,190 +1,12 @@
|
|||
require 'log4r'
|
||||
|
||||
require "vagrant"
|
||||
require 'vagrant/util/platform'
|
||||
require "vagrant/util/subprocess"
|
||||
|
||||
module VagrantPlugins
|
||||
module HostBSD
|
||||
# Represents a BSD host, such as FreeBSD and Darwin (Mac OS X).
|
||||
class Host < Vagrant.plugin("2", :host)
|
||||
include Vagrant::Util
|
||||
include Vagrant::Util::Retryable
|
||||
|
||||
def self.match?
|
||||
def detect?(env)
|
||||
Vagrant::Util::Platform.darwin? || Vagrant::Util::Platform.bsd?
|
||||
end
|
||||
|
||||
def self.precedence
|
||||
# Set a lower precedence because this is a generic OS. We
|
||||
# want specific distros to match first.
|
||||
2
|
||||
end
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
|
||||
@logger = Log4r::Logger.new("vagrant::hosts::bsd")
|
||||
@nfs_restart_command = "sudo nfsd restart"
|
||||
@nfs_exports_template = "nfs/exports"
|
||||
end
|
||||
|
||||
def nfs?
|
||||
retryable(:tries => 10, :on => TypeError) do
|
||||
system("which nfsd > /dev/null 2>&1")
|
||||
end
|
||||
end
|
||||
|
||||
def nfs_export(id, ips, folders)
|
||||
nfs_checkexports! if File.file?("/etc/exports")
|
||||
|
||||
# We need to build up mapping of directories that are enclosed
|
||||
# within each other because the exports file has to have subdirectories
|
||||
# of an exported directory on the same line. e.g.:
|
||||
#
|
||||
# "/foo" "/foo/bar" ...
|
||||
# "/bar"
|
||||
#
|
||||
# We build up this mapping within the following hash.
|
||||
@logger.debug("Compiling map of sub-directories for NFS exports...")
|
||||
dirmap = {}
|
||||
folders.each do |_, opts|
|
||||
hostpath = opts[:hostpath].dup
|
||||
hostpath.gsub!('"', '\"')
|
||||
|
||||
found = false
|
||||
dirmap.each do |dirs, diropts|
|
||||
dirs.each do |dir|
|
||||
if dir.start_with?(hostpath) || hostpath.start_with?(dir)
|
||||
# TODO: verify opts and diropts are _identical_, raise an error
|
||||
# if not. NFS mandates subdirectories have identical options.
|
||||
dirs << hostpath
|
||||
found = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
break if found
|
||||
end
|
||||
|
||||
if !found
|
||||
dirmap[[hostpath]] = opts.dup
|
||||
end
|
||||
end
|
||||
|
||||
# Sort all the keys by length so that the directory closest to
|
||||
# the root is exported first.
|
||||
dirmap.each do |dirs, _|
|
||||
dirs.sort_by! { |d| d.length }
|
||||
end
|
||||
|
||||
# Setup the NFS options
|
||||
dirmap.each do |dirs, opts|
|
||||
if !opts[:bsd__nfs_options]
|
||||
opts[:bsd__nfs_options] = ["alldirs"]
|
||||
end
|
||||
|
||||
hasmapall = false
|
||||
opts[:bsd__nfs_options].each do |opt|
|
||||
# mapall/maproot are mutually exclusive, so we have to check
|
||||
# for both here.
|
||||
if opt =~ /^mapall=/ || opt =~ /^maproot=/
|
||||
hasmapall = true
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if !hasmapall
|
||||
opts[:bsd__nfs_options] << "mapall=#{opts[:map_uid]}:#{opts[:map_gid]}"
|
||||
end
|
||||
|
||||
opts[:bsd__compiled_nfs_options] = opts[:bsd__nfs_options].map do |opt|
|
||||
"-#{opt}"
|
||||
end.join(" ")
|
||||
end
|
||||
|
||||
@logger.info("Exporting the following for NFS...")
|
||||
dirmap.each do |dirs, opts|
|
||||
@logger.info("NFS DIR: #{dirs.inspect}")
|
||||
@logger.info("NFS OPTS: #{opts.inspect}")
|
||||
end
|
||||
|
||||
output = TemplateRenderer.render(@nfs_exports_template,
|
||||
:uuid => id,
|
||||
:ips => ips,
|
||||
:folders => dirmap,
|
||||
:user => Process.uid)
|
||||
|
||||
# The sleep ensures that the output is truly flushed before any `sudo`
|
||||
# commands are issued.
|
||||
@ui.info I18n.t("vagrant.hosts.bsd.nfs_export")
|
||||
sleep 0.5
|
||||
|
||||
# First, clean up the old entry
|
||||
nfs_cleanup(id)
|
||||
|
||||
# Output the rendered template into the exports
|
||||
output.split("\n").each do |line|
|
||||
line.gsub!('"', '\"')
|
||||
line.gsub!("'", "'\\\\''")
|
||||
system(%Q[sudo -s -- "echo '#{line}' >> /etc/exports"])
|
||||
end
|
||||
|
||||
# We run restart here instead of "update" just in case nfsd
|
||||
# is not starting
|
||||
system(@nfs_restart_command)
|
||||
end
|
||||
|
||||
def nfs_prune(valid_ids)
|
||||
return if !File.exist?("/etc/exports")
|
||||
|
||||
@logger.info("Pruning invalid NFS entries...")
|
||||
|
||||
output = false
|
||||
user = Process.uid
|
||||
|
||||
File.read("/etc/exports").lines.each do |line|
|
||||
if id = line[/^# VAGRANT-BEGIN:( #{user})? ([A-Za-z0-9-]+?)$/, 2]
|
||||
if valid_ids.include?(id)
|
||||
@logger.debug("Valid ID: #{id}")
|
||||
else
|
||||
if !output
|
||||
# We want to warn the user but we only want to output once
|
||||
@ui.info I18n.t("vagrant.hosts.bsd.nfs_prune")
|
||||
output = true
|
||||
end
|
||||
|
||||
@logger.info("Invalid ID, pruning: #{id}")
|
||||
nfs_cleanup(id)
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue Errno::EACCES
|
||||
raise Vagrant::Errors::NFSCantReadExports
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def nfs_checkexports!
|
||||
r = Subprocess.execute("nfsd", "checkexports")
|
||||
if r.exit_code != 0
|
||||
raise Vagrant::Errors::NFSBadExports, output: r.stderr
|
||||
end
|
||||
end
|
||||
|
||||
def nfs_cleanup(id)
|
||||
return if !File.exist?("/etc/exports")
|
||||
|
||||
# Escape sed-sensitive characters:
|
||||
id = id.gsub("/", "\\/")
|
||||
id = id.gsub(".", "\\.")
|
||||
|
||||
user = Process.uid
|
||||
|
||||
# Use sed to just strip out the block of code which was inserted
|
||||
# by Vagrant, and restart NFS.
|
||||
system("sudo sed -E -e '/^# VAGRANT-BEGIN:( #{user})? #{id}/,/^# VAGRANT-END:( #{user})? #{id}/ d' -ibak /etc/exports")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,6 +10,31 @@ module VagrantPlugins
|
|||
require File.expand_path("../host", __FILE__)
|
||||
Host
|
||||
end
|
||||
|
||||
host_capability("bsd", "nfs_export") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("bsd", "nfs_exports_template") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("bsd", "nfs_installed") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("bsd", "nfs_prune") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("bsd", "nfs_restart_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
require "vagrant/util"
|
||||
require "vagrant/util/retryable"
|
||||
|
||||
require Vagrant.source_root.join("plugins", "hosts", "bsd", "cap", "nfs")
|
||||
|
||||
module VagrantPlugins
|
||||
module HostFreeBSD
|
||||
module Cap
|
||||
class NFS
|
||||
def self.nfs_export(environment, ui, id, ips, folders)
|
||||
folders.each do |folder_name, folder_values|
|
||||
if folder_values[:hostpath] =~ /\s+/
|
||||
raise Vagrant::Errors::VagrantError,
|
||||
_key: :freebsd_nfs_whitespace
|
||||
end
|
||||
end
|
||||
|
||||
HostBSD::Cap::NFS.nfs_export(environment, ui, id, ips, folders)
|
||||
end
|
||||
|
||||
def self.nfs_exports_template(environment)
|
||||
"nfs/exports_freebsd"
|
||||
end
|
||||
|
||||
def self.nfs_restart_command(environment)
|
||||
"sudo /etc/rc.d/mountd onereload"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,43 +1,12 @@
|
|||
require "vagrant"
|
||||
require 'vagrant/util/platform'
|
||||
|
||||
require Vagrant.source_root.join("plugins/hosts/bsd/host")
|
||||
|
||||
module VagrantPlugins
|
||||
module HostFreeBSD
|
||||
class Host < VagrantPlugins::HostBSD::Host
|
||||
class FreeBSDHostError < Vagrant::Errors::VagrantError
|
||||
error_namespace("vagrant.hosts.freebsd")
|
||||
end
|
||||
|
||||
include Vagrant::Util
|
||||
include Vagrant::Util::Retryable
|
||||
|
||||
def self.match?
|
||||
class Host < Vagrant.plugin("2", :host)
|
||||
def detect?(env)
|
||||
Vagrant::Util::Platform.freebsd?
|
||||
end
|
||||
|
||||
# Normal, mid-range precedence.
|
||||
def self.precedence
|
||||
5
|
||||
end
|
||||
|
||||
def nfs_export(id, ip, folders)
|
||||
folders.each do |folder_name, folder_values|
|
||||
if folder_values[:hostpath] =~ /\s+/
|
||||
raise FreeBSDHostError, :_key => :nfs_whitespace
|
||||
end
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
|
||||
@nfs_restart_command = "sudo /etc/rc.d/mountd onereload"
|
||||
@nfs_exports_template = "nfs/exports_freebsd"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,10 +6,26 @@ module VagrantPlugins
|
|||
name "FreeBSD host"
|
||||
description "FreeBSD host support."
|
||||
|
||||
host("freebsd") do
|
||||
require File.expand_path("../host", __FILE__)
|
||||
host("freebsd", "bsd") do
|
||||
require_relative "host"
|
||||
Host
|
||||
end
|
||||
|
||||
host_capability("freebsd", "nfs_export") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
# BSD-specific helpers
|
||||
host_capability("freebsd", "nfs_exports_template") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("freebsd", "nfs_restart_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
require "vagrant/util/subprocess"
|
||||
|
||||
module VagrantPlugins
|
||||
module HostGentoo
|
||||
module Cap
|
||||
class NFS
|
||||
def self.nfs_check_command(env)
|
||||
if systemd?
|
||||
return "/usr/sbin/systemctl status nfsd"
|
||||
else
|
||||
return "/etc/init.d/nfs status"
|
||||
end
|
||||
end
|
||||
|
||||
def self.nfs_start_command(env)
|
||||
if systemd?
|
||||
return "/usr/sbin/systemctl start nfsd rpc-mountd rpcbind"
|
||||
else
|
||||
return "/etc/init.d/nfs restart"
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# This tests to see if systemd is used on the system. This is used
|
||||
# in newer versions of Arch, and requires a change in behavior.
|
||||
def self.systemd?
|
||||
result = Vagrant::Util::Subprocess.execute("ps", "-o", "comm=", "1")
|
||||
return result.stdout.chomp == "systemd"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,39 +1,10 @@
|
|||
require "vagrant"
|
||||
require "vagrant/util/subprocess"
|
||||
|
||||
require Vagrant.source_root.join("plugins/hosts/linux/host")
|
||||
|
||||
module VagrantPlugins
|
||||
module HostGentoo
|
||||
class Host < VagrantPlugins::HostLinux::Host
|
||||
def self.match?
|
||||
return File.exists?("/etc/gentoo-release")
|
||||
end
|
||||
|
||||
# Normal, mid-range precedence.
|
||||
def self.precedence
|
||||
5
|
||||
end
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
|
||||
@nfs_apply_command = "/usr/sbin/exportfs -r"
|
||||
if systemd?
|
||||
@nfs_check_command = "/usr/bin/systemctl status nfsd"
|
||||
@nfs_start_command = "/usr/bin/systemctl start nfsd rpc-mountd rpcbind"
|
||||
else
|
||||
@nfs_check_command = "/etc/init.d/nfs status"
|
||||
@nfs_start_command = "/etc/init.d/nfs restart"
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Check for systemd presence from current processes.
|
||||
def systemd?
|
||||
result = Vagrant::Util::Subprocess.execute("ps", "-o", "comm=", "1")
|
||||
return result.stdout.chomp == "systemd"
|
||||
class Host < Vagrant.plugin("2", :host)
|
||||
def detect?(env)
|
||||
File.exists?("/etc/gentoo-release")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,10 +6,23 @@ module VagrantPlugins
|
|||
name "Gentoo host"
|
||||
description "Gentoo host support."
|
||||
|
||||
host("gentoo") do
|
||||
require File.expand_path("../host", __FILE__)
|
||||
host("gentoo", "linux") do
|
||||
require_relative "host"
|
||||
Host
|
||||
end
|
||||
|
||||
# Linux-specific helpers we need to determine paths that can
|
||||
# be overriden.
|
||||
host_capability("gentoo", "nfs_check_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("gentoo", "nfs_start_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
require "vagrant/util"
|
||||
require "vagrant/util/retryable"
|
||||
|
||||
module VagrantPlugins
|
||||
module HostLinux
|
||||
module Cap
|
||||
class NFS
|
||||
extend Vagrant::Util::Retryabe
|
||||
|
||||
def self.nfs_apply_command(env)
|
||||
"/usr/bin/exportfs -r"
|
||||
end
|
||||
|
||||
def self.nfs_check_command(env)
|
||||
"/etc/init.d/nfs-kernel-server status"
|
||||
end
|
||||
|
||||
def self.nfs_start_command(env)
|
||||
"/etc/init.d/nfs-kernel-server start"
|
||||
end
|
||||
|
||||
def self.nfs_export(env, ui, id, ips, folders)
|
||||
# Get some values we need before we do anything
|
||||
nfs_apply_command = env.host.capability(:nfs_apply_command)
|
||||
nfs_check_command = env.host.capability(:nfs_check_command)
|
||||
nfs_start_command = env.host.capability(:nfs_start_command)
|
||||
|
||||
nfs_opts_setup(folders)
|
||||
output = Vagrant::Util::TemplateRenderer.render('nfs/exports_linux',
|
||||
:uuid => id,
|
||||
:ips => ips,
|
||||
:folders => folders,
|
||||
:user => Process.uid)
|
||||
|
||||
ui.info I18n.t("vagrant.hosts.linux.nfs_export")
|
||||
sleep 0.5
|
||||
|
||||
nfs_cleanup(id)
|
||||
|
||||
output.split("\n").each do |line|
|
||||
# This should only ask for administrative permission once, even
|
||||
# though its executed in multiple subshells.
|
||||
system(%Q[sudo su root -c "echo '#{line}' >> /etc/exports"])
|
||||
end
|
||||
|
||||
if nfs_running?(nfs_check_command)
|
||||
system("sudo #{nfs_apply_command}")
|
||||
else
|
||||
system("sudo #{nfs_start_command}")
|
||||
end
|
||||
end
|
||||
|
||||
def self.nfs_installed(environment)
|
||||
retryable(:tries => 10, :on => TypeError) do
|
||||
# Check procfs to see if NFSd is a supported filesystem
|
||||
system("cat /proc/filesystems | grep nfsd > /dev/null 2>&1")
|
||||
end
|
||||
end
|
||||
|
||||
def self.nfs_prune(environment, ui, valid_ids)
|
||||
return if !File.exist?("/etc/exports")
|
||||
|
||||
logger = Log4r::Logger.new("vagrant::hosts::linux")
|
||||
logger.info("Pruning invalid NFS entries...")
|
||||
|
||||
output = false
|
||||
user = Process.uid
|
||||
|
||||
File.read("/etc/exports").lines.each do |line|
|
||||
if id = line[/^# VAGRANT-BEGIN:( #{user})? ([A-Za-z0-9-]+?)$/, 2]
|
||||
if valid_ids.include?(id)
|
||||
logger.debug("Valid ID: #{id}")
|
||||
else
|
||||
if !output
|
||||
# We want to warn the user but we only want to output once
|
||||
ui.info I18n.t("vagrant.hosts.linux.nfs_prune")
|
||||
output = true
|
||||
end
|
||||
|
||||
logger.info("Invalid ID, pruning: #{id}")
|
||||
nfs_cleanup(id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def self.nfs_cleanup(id)
|
||||
return if !File.exist?("/etc/exports")
|
||||
|
||||
user = Process.uid
|
||||
|
||||
# Use sed to just strip out the block of code which was inserted
|
||||
# by Vagrant
|
||||
system("sudo sed -r -e '/^# VAGRANT-BEGIN:( #{user})? #{id}/,/^# VAGRANT-END:( #{user})? #{id}/ d' -ibak /etc/exports")
|
||||
end
|
||||
|
||||
def self.nfs_opts_setup(folders)
|
||||
folders.each do |k, opts|
|
||||
if !opts[:linux__nfs_options]
|
||||
opts[:linux__nfs_options] ||= ["rw", "no_subtree_check", "all_squash"]
|
||||
end
|
||||
|
||||
# Only automatically set anonuid/anongid if they weren't
|
||||
# explicitly set by the user.
|
||||
hasgid = false
|
||||
hasuid = false
|
||||
opts[:linux__nfs_options].each do |opt|
|
||||
hasgid = !!(opt =~ /^anongid=/) if !hasgid
|
||||
hasuid = !!(opt =~ /^anonuid=/) if !hasuid
|
||||
end
|
||||
|
||||
opts[:linux__nfs_options] << "anonuid=#{opts[:map_uid]}" if !hasuid
|
||||
opts[:linux__nfs_options] << "anongid=#{opts[:map_gid]}" if !hasgid
|
||||
opts[:linux__nfs_options] << "fsid=#{opts[:uuid]}"
|
||||
end
|
||||
end
|
||||
|
||||
def self.nfs_running?(check_command)
|
||||
system(check_command)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,128 +1,12 @@
|
|||
require 'log4r'
|
||||
|
||||
require "vagrant"
|
||||
require 'vagrant/util/platform'
|
||||
|
||||
module VagrantPlugins
|
||||
module HostLinux
|
||||
# Represents a Linux based host, such as Ubuntu.
|
||||
class Host < Vagrant.plugin("2", :host)
|
||||
include Vagrant::Util
|
||||
include Vagrant::Util::Retryable
|
||||
|
||||
def self.match?
|
||||
def detect?(env)
|
||||
Vagrant::Util::Platform.linux?
|
||||
end
|
||||
|
||||
def self.precedence
|
||||
# Set a lower precedence because this is a generic OS. We
|
||||
# want specific distros to match first.
|
||||
2
|
||||
end
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
|
||||
@logger = Log4r::Logger.new("vagrant::hosts::linux")
|
||||
@nfs_apply_command = "/usr/sbin/exportfs -r"
|
||||
@nfs_check_command = "/etc/init.d/nfs-kernel-server status"
|
||||
@nfs_start_command = "/etc/init.d/nfs-kernel-server start"
|
||||
end
|
||||
|
||||
def nfs?
|
||||
retryable(:tries => 10, :on => TypeError) do
|
||||
# Check procfs to see if NFSd is a supported filesystem
|
||||
system("cat /proc/filesystems | grep nfsd > /dev/null 2>&1")
|
||||
end
|
||||
end
|
||||
|
||||
def nfs_opts_setup(folders)
|
||||
folders.each do |k, opts|
|
||||
if !opts[:linux__nfs_options]
|
||||
opts[:linux__nfs_options] ||= ["rw", "no_subtree_check", "all_squash"]
|
||||
end
|
||||
|
||||
# Only automatically set anonuid/anongid if they weren't
|
||||
# explicitly set by the user.
|
||||
hasgid = false
|
||||
hasuid = false
|
||||
opts[:linux__nfs_options].each do |opt|
|
||||
hasgid = !!(opt =~ /^anongid=/) if !hasgid
|
||||
hasuid = !!(opt =~ /^anonuid=/) if !hasuid
|
||||
end
|
||||
|
||||
opts[:linux__nfs_options] << "anonuid=#{opts[:map_uid]}" if !hasuid
|
||||
opts[:linux__nfs_options] << "anongid=#{opts[:map_gid]}" if !hasgid
|
||||
opts[:linux__nfs_options] << "fsid=#{opts[:uuid]}"
|
||||
end
|
||||
end
|
||||
|
||||
def nfs_export(id, ips, folders)
|
||||
nfs_opts_setup(folders)
|
||||
output = TemplateRenderer.render('nfs/exports_linux',
|
||||
:uuid => id,
|
||||
:ips => ips,
|
||||
:folders => folders,
|
||||
:user => Process.uid)
|
||||
|
||||
@ui.info I18n.t("vagrant.hosts.linux.nfs_export")
|
||||
sleep 0.5
|
||||
|
||||
nfs_cleanup(id)
|
||||
|
||||
output.split("\n").each do |line|
|
||||
# This should only ask for administrative permission once, even
|
||||
# though its executed in multiple subshells.
|
||||
system(%Q[sudo su root -c "echo '#{line}' >> /etc/exports"])
|
||||
end
|
||||
|
||||
if nfs_running?
|
||||
system("sudo #{@nfs_apply_command}")
|
||||
else
|
||||
system("sudo #{@nfs_start_command}")
|
||||
end
|
||||
end
|
||||
|
||||
def nfs_prune(valid_ids)
|
||||
return if !File.exist?("/etc/exports")
|
||||
|
||||
@logger.info("Pruning invalid NFS entries...")
|
||||
|
||||
output = false
|
||||
user = Process.uid
|
||||
|
||||
File.read("/etc/exports").lines.each do |line|
|
||||
if id = line[/^# VAGRANT-BEGIN:( #{user})? ([A-Za-z0-9-]+?)$/, 2]
|
||||
if valid_ids.include?(id)
|
||||
@logger.debug("Valid ID: #{id}")
|
||||
else
|
||||
if !output
|
||||
# We want to warn the user but we only want to output once
|
||||
@ui.info I18n.t("vagrant.hosts.linux.nfs_prune")
|
||||
output = true
|
||||
end
|
||||
|
||||
@logger.info("Invalid ID, pruning: #{id}")
|
||||
nfs_cleanup(id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def nfs_running?
|
||||
system("#{@nfs_check_command}")
|
||||
end
|
||||
|
||||
def nfs_cleanup(id)
|
||||
return if !File.exist?("/etc/exports")
|
||||
|
||||
user = Process.uid
|
||||
# Use sed to just strip out the block of code which was inserted
|
||||
# by Vagrant
|
||||
system("sudo sed -r -e '/^# VAGRANT-BEGIN:( #{user})? #{id}/,/^# VAGRANT-END:( #{user})? #{id}/ d' -ibak /etc/exports")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,9 +7,41 @@ module VagrantPlugins
|
|||
description "Linux host support."
|
||||
|
||||
host("linux") do
|
||||
require File.expand_path("../host", __FILE__)
|
||||
require_relative "host"
|
||||
Host
|
||||
end
|
||||
|
||||
host_capability("linux", "nfs_export") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("linux", "nfs_installed") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("linux", "nfs_prune") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
# Linux-specific helpers we need to determine paths that can
|
||||
# be overriden.
|
||||
host_capability("linux", "nfs_apply_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("linux", "nfs_check_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("linux", "nfs_start_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
require "vagrant"
|
||||
|
||||
module VagrantPlugins
|
||||
module HostNull
|
||||
class Host < Vagrant.plugin("2", :host)
|
||||
def detect?(env)
|
||||
# This host can only be explicitly chosen.
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
require "vagrant"
|
||||
|
||||
module VagrantPlugins
|
||||
module HostNull
|
||||
class Plugin < Vagrant.plugin("2")
|
||||
name "null host"
|
||||
description "A host that implements no capabilities."
|
||||
|
||||
host("null") do
|
||||
require_relative "host"
|
||||
Host
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
module VagrantPlugins
|
||||
module HostOpenSUSE
|
||||
module Cap
|
||||
class NFS
|
||||
def self.nfs_check_command(env)
|
||||
"service nfsserver status"
|
||||
end
|
||||
|
||||
def self.nfs_start_command(env)
|
||||
"service nfsserver start"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,12 +2,10 @@ require "pathname"
|
|||
|
||||
require "vagrant"
|
||||
|
||||
require Vagrant.source_root.join("plugins/hosts/linux/host")
|
||||
|
||||
module VagrantPlugins
|
||||
module HostOpenSUSE
|
||||
class Host < VagrantPlugins::HostLinux::Host
|
||||
def self.match?
|
||||
class Host < Vagrant.plugin("2", :host)
|
||||
def detect?(env)
|
||||
release_file = Pathname.new("/etc/SuSE-release")
|
||||
|
||||
if release_file.exist?
|
||||
|
@ -18,19 +16,6 @@ module VagrantPlugins
|
|||
|
||||
false
|
||||
end
|
||||
|
||||
# Normal, mid-range precedence.
|
||||
def self.precedence
|
||||
5
|
||||
end
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
|
||||
@nfs_apply_command = "/usr/sbin/exportfs -r"
|
||||
@nfs_check_command = "service nfsserver status"
|
||||
@nfs_start_command = "service nfsserver start"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,10 +6,22 @@ module VagrantPlugins
|
|||
name "OpenSUSE host"
|
||||
description "OpenSUSE host support."
|
||||
|
||||
host("opensuse") do
|
||||
require File.expand_path("../host", __FILE__)
|
||||
host("opensuse", "linux") do
|
||||
require_relative "host"
|
||||
Host
|
||||
end
|
||||
|
||||
# Linux-specific helpers we need to determine paths that can
|
||||
# be overriden.
|
||||
host_capability("opensuse", "nfs_check_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("opensuse", "nfs_start_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
require "pathname"
|
||||
|
||||
module VagrantPlugins
|
||||
module HostRedHat
|
||||
module Cap
|
||||
class NFS
|
||||
def self.nfs_check_command(env)
|
||||
"#{nfs_server_binary} status"
|
||||
end
|
||||
|
||||
def self.nfs_start_command(env)
|
||||
"#{nfs_server_binary} start"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def self.nfs_server_binary
|
||||
nfs_server_binary = "/etc/init.d/nfs"
|
||||
|
||||
# On Fedora 16+, systemd replaced init.d, so we have to use the
|
||||
# proper NFS binary. This checks to see if we need to do that.
|
||||
release_file = Pathname.new("/etc/redhat-release")
|
||||
begin
|
||||
release_file.open("r:ISO-8859-1:UTF-8") do |f|
|
||||
fedora_match = /Fedora.* release ([0-9]+)/.match(f.gets)
|
||||
if fedora_match
|
||||
version_number = fedora_match[1].to_i
|
||||
if version_number >= 16
|
||||
# "service nfs-server" will redirect properly to systemctl
|
||||
# when "service nfs-server restart" is called.
|
||||
nfs_server_binary = "/usr/sbin/service nfs-server"
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue Errno::ENOENT
|
||||
# File doesn't exist, not a big deal, assume we're on a
|
||||
# lower version.
|
||||
end
|
||||
|
||||
nfs_server_binary
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,12 +2,10 @@ require "pathname"
|
|||
|
||||
require "vagrant"
|
||||
|
||||
require Vagrant.source_root.join("plugins/hosts/linux/host")
|
||||
|
||||
module VagrantPlugins
|
||||
module HostRedHat
|
||||
class Host < VagrantPlugins::HostLinux::Host
|
||||
def self.match?
|
||||
class Host < Vagrant.plugin("2", :host)
|
||||
def detect?(env)
|
||||
release_file = Pathname.new("/etc/redhat-release")
|
||||
|
||||
if release_file.exist?
|
||||
|
@ -15,47 +13,17 @@ module VagrantPlugins
|
|||
contents = f.gets
|
||||
return true if contents =~ /^Fedora/ # Fedora
|
||||
return true if contents =~ /^CentOS/ # CentOS
|
||||
return true if contents =~ /^Enterprise Linux Enterprise Linux/ # Oracle Linux < 5.3
|
||||
return true if contents =~ /^Red Hat Enterprise Linux/ # Red Hat Enterprise Linux and Oracle Linux >= 5.3
|
||||
|
||||
# Oracle Linux < 5.3
|
||||
return true if contents =~ /^Enterprise Linux Enterprise Linux/
|
||||
|
||||
# Red Hat Enterprise Linux and Oracle Linux >= 5.3
|
||||
return true if contents =~ /^Red Hat Enterprise Linux/
|
||||
end
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
# Normal, mid-range precedence.
|
||||
def self.precedence
|
||||
5
|
||||
end
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
|
||||
nfs_server_binary = "/etc/init.d/nfs"
|
||||
|
||||
# On Fedora 16+, systemd replaced init.d, so we have to use the
|
||||
# proper NFS binary. This checks to see if we need to do that.
|
||||
release_file = Pathname.new("/etc/redhat-release")
|
||||
begin
|
||||
release_file.open("r:ISO-8859-1:UTF-8") do |f|
|
||||
fedora_match = /Fedora.* release ([0-9]+)/.match(f.gets)
|
||||
if fedora_match
|
||||
version_number = fedora_match[1].to_i
|
||||
if version_number >= 16
|
||||
# "service nfs-server" will redirect properly to systemctl
|
||||
# when "service nfs-server restart" is called.
|
||||
nfs_server_binary = "/usr/sbin/service nfs-server"
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue Errno::ENOENT
|
||||
# File doesn't exist, not a big deal, assume we're on a
|
||||
# lower version.
|
||||
end
|
||||
@nfs_apply_command = "/usr/sbin/exportfs -r"
|
||||
@nfs_check_command = "#{nfs_server_binary} status"
|
||||
@nfs_start_command = "#{nfs_server_binary} start"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,10 +6,22 @@ module VagrantPlugins
|
|||
name "Red Hat host"
|
||||
description "Red Hat host support."
|
||||
|
||||
host("redhat") do
|
||||
host("redhat", "linux") do
|
||||
require File.expand_path("../host", __FILE__)
|
||||
Host
|
||||
end
|
||||
|
||||
# Linux-specific helpers we need to determine paths that can
|
||||
# be overriden.
|
||||
host_capability("redhat", "nfs_check_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("redhat", "nfs_start_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
module VagrantPlugins
|
||||
module HostSlackware
|
||||
module Cap
|
||||
class NFS
|
||||
def self.nfs_check_command(env)
|
||||
"pidof nfsd >/dev/null"
|
||||
end
|
||||
|
||||
def self.nfs_start_command(env)
|
||||
"/etc/rc.d/rc.nfsd start"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,25 +1,11 @@
|
|||
require "vagrant"
|
||||
|
||||
require Vagrant.source_root.join("plugins/hosts/linux/host")
|
||||
|
||||
module VagrantPlugins
|
||||
module HostSlackware
|
||||
class Host < VagrantPlugins::HostLinux::Host
|
||||
def self.match?
|
||||
return File.exists?("/etc/slackware-release") || Dir.glob("/usr/lib/setup/Plamo-*").length > 0
|
||||
end
|
||||
|
||||
# Normal, mid-range precedence.
|
||||
def self.precedence
|
||||
5
|
||||
end
|
||||
|
||||
def initialize(*args)
|
||||
super
|
||||
|
||||
@nfs_apply_command = "/usr/sbin/exportfs -r"
|
||||
@nfs_check_command = "pidof nfsd > /dev/null"
|
||||
@nfs_start_command = "/etc/rc.d/rc.nfsd start"
|
||||
class Host < Vagrant.plugin("2", :host)
|
||||
def detect?(env)
|
||||
return File.exists?("/etc/slackware-release") ||
|
||||
!Dir.glob("/usr/lib/setup/Plamo-*").empty?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,10 +6,22 @@ module VagrantPlugins
|
|||
name "Slackware host"
|
||||
description "Slackware and derivertives host support."
|
||||
|
||||
host("slackware") do
|
||||
host("slackware", "linux") do
|
||||
require File.expand_path("../host", __FILE__)
|
||||
Host
|
||||
end
|
||||
|
||||
# Linux-specific helpers we need to determine paths that can
|
||||
# be overriden.
|
||||
host_capability("slackware", "nfs_check_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
|
||||
host_capability("slackware", "nfs_start_command") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
module VagrantPlugins
|
||||
module HostWindows
|
||||
module Cap
|
||||
class NFS
|
||||
def self.nfs_installed(env)
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,17 +1,13 @@
|
|||
require "vagrant"
|
||||
|
||||
require 'vagrant/util/platform'
|
||||
|
||||
module VagrantPlugins
|
||||
module HostWindows
|
||||
class Host < Vagrant.plugin("2", :host)
|
||||
def self.match?
|
||||
def detect?(env)
|
||||
Vagrant::Util::Platform.windows?
|
||||
end
|
||||
|
||||
# Windows does not support NFS
|
||||
def nfs?
|
||||
false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,9 +7,14 @@ module VagrantPlugins
|
|||
description "Windows host support."
|
||||
|
||||
host("windows") do
|
||||
require File.expand_path("../host", __FILE__)
|
||||
require_relative "host"
|
||||
Host
|
||||
end
|
||||
|
||||
host_capability("windows", "nfs_installed") do
|
||||
require_relative "cap/nfs"
|
||||
Cap::NFS
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,6 +5,15 @@ module VagrantPlugins
|
|||
class VagrantConfig < Vagrant.plugin("2", :config)
|
||||
attr_accessor :host
|
||||
|
||||
def initialize
|
||||
@host = UNSET_VALUE
|
||||
end
|
||||
|
||||
def finalize!
|
||||
@host = nil if @host == UNSET_VALUE
|
||||
@host = @host.to_sym if @host
|
||||
end
|
||||
|
||||
def to_s
|
||||
"Vagrant"
|
||||
end
|
||||
|
|
|
@ -455,12 +455,8 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
if has_nfs
|
||||
if !machine.env.host
|
||||
errors << I18n.t("vagrant.config.vm.nfs_requires_host")
|
||||
else
|
||||
errors << I18n.t("vagrant.config.vm.nfs_not_supported") if \
|
||||
!machine.env.host.nfs?
|
||||
end
|
||||
!machine.env.host.capability(:nfs_installed)
|
||||
end
|
||||
|
||||
# Validate networks
|
||||
|
|
|
@ -49,7 +49,8 @@ module VagrantPlugins
|
|||
# overlapping input requests. [GH-2680]
|
||||
@@lock.synchronize do
|
||||
machine.ui.info I18n.t("vagrant.actions.vm.nfs.exporting")
|
||||
machine.env.host.nfs_export(machine.id, machine_ip, folders)
|
||||
machine.env.host.capability(:nfs_export,
|
||||
machine.ui, machine.id, machine_ip, folders)
|
||||
end
|
||||
|
||||
# Mount
|
||||
|
@ -72,7 +73,7 @@ module VagrantPlugins
|
|||
|
||||
# Prune any of the unused machines
|
||||
@logger.info("NFS pruning. Valid IDs: #{ids.inspect}")
|
||||
machine.env.host.nfs_prune(ids)
|
||||
machine.env.host.capability(:nfs_prune, machine.ui, ids)
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -379,6 +379,9 @@ en:
|
|||
Host port: %{host}
|
||||
Guest port: %{guest}
|
||||
Adapter: %{adapter}
|
||||
freebsd_nfs_whitespace: |-
|
||||
FreeBSD hosts do not support sharing directories with whitespace in
|
||||
their path. Please adjust your path accordingly.
|
||||
gem_command_in_bundler: |-
|
||||
You cannot run the `vagrant plugin` command while in a bundler environment.
|
||||
This should generally never happen unless Vagrant is installed outside
|
||||
|
@ -412,6 +415,11 @@ en:
|
|||
directory that Vagrant uses must be both readable and writable.
|
||||
|
||||
You specified: %{home_path}
|
||||
host_explicit_not_detected: |-
|
||||
The host implementation explicitly specified in your Vagrantfile
|
||||
("%{value}") could not be found. Please verify that the plugin is
|
||||
installed which implements this host and that the value you used
|
||||
for `config.vagrant.host` is correct.
|
||||
interrupted: |-
|
||||
Vagrant exited after cleanup due to external interrupt.
|
||||
local_data_dir_not_accessible: |-
|
||||
|
@ -869,9 +877,6 @@ en:
|
|||
that `nfsd` is installed on your machine, and try again. If you're
|
||||
on Windows, NFS isn't supported. If the problem persists, please
|
||||
contact Vagrant support.
|
||||
nfs_requires_host: |-
|
||||
Using NFS shared folders requires a host to be specified
|
||||
using `config.vagrant.host`.
|
||||
network_ip_ends_in_one: |-
|
||||
Static IPs cannot end in ".1" since that address is always
|
||||
reserved for the router. Please use another ending.
|
||||
|
@ -1340,10 +1345,6 @@ en:
|
|||
arch:
|
||||
nfs_export:
|
||||
prepare: "Preparing to edit /etc/exports. Administrator privileges will be required..."
|
||||
freebsd:
|
||||
nfs_whitespace: |-
|
||||
FreeBSD hosts do not support sharing directories with whitespace in
|
||||
their path. Please adjust your path accordingly.
|
||||
|
||||
provisioners:
|
||||
chef:
|
||||
|
|
|
@ -29,3 +29,11 @@ end
|
|||
# Configure VAGRANT_CWD so that the tests never find an actual
|
||||
# Vagrantfile anywhere, or at least this minimizes those chances.
|
||||
ENV["VAGRANT_CWD"] = Tempdir.new.path
|
||||
|
||||
# Unset all host plugins so that we aren't executing subprocess things
|
||||
# to detect a host for every test.
|
||||
Vagrant.plugin("2").manager.registered.dup.each do |plugin|
|
||||
if plugin.components.hosts.to_hash.length > 0
|
||||
Vagrant.plugin("2").manager.unregister(plugin)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
require_relative "../base"
|
||||
|
||||
describe VagrantPlugins::ProviderVirtualBox::Action::PrepareNFSSettings do
|
||||
include_context "unit"
|
||||
include_context "virtualbox"
|
||||
|
||||
let(:machine) {
|
||||
|
|
|
@ -8,6 +8,7 @@ require "vagrant/util/file_mode"
|
|||
|
||||
describe Vagrant::Environment do
|
||||
include_context "unit"
|
||||
include_context "capability_helpers"
|
||||
|
||||
let(:env) do
|
||||
isolated_environment.tap do |e|
|
||||
|
@ -21,9 +22,83 @@ describe Vagrant::Environment do
|
|||
end
|
||||
|
||||
let(:instance) { env.create_vagrant_env }
|
||||
|
||||
subject { instance }
|
||||
|
||||
describe "#host" do
|
||||
let(:plugin_hosts) { {} }
|
||||
let(:plugin_host_caps) { {} }
|
||||
|
||||
before do
|
||||
m = Vagrant.plugin("2").manager
|
||||
m.stub(hosts: plugin_hosts)
|
||||
m.stub(host_capabilities: plugin_host_caps)
|
||||
end
|
||||
|
||||
it "should default to some host even if there are none" do
|
||||
env.vagrantfile <<-VF
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vagrant.host = nil
|
||||
end
|
||||
VF
|
||||
|
||||
expect(subject.host).to be
|
||||
end
|
||||
|
||||
it "should attempt to detect a host if no host is set" do
|
||||
env.vagrantfile <<-VF
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vagrant.host = nil
|
||||
end
|
||||
VF
|
||||
|
||||
plugin_hosts[:foo] = [detect_class(true), nil]
|
||||
plugin_host_caps[:foo] = { bar: Class }
|
||||
|
||||
result = subject.host
|
||||
expect(result.capability?(:bar)).to be_true
|
||||
end
|
||||
|
||||
it "should attempt to detect a host if host is :detect" do
|
||||
env.vagrantfile <<-VF
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vagrant.host = :detect
|
||||
end
|
||||
VF
|
||||
|
||||
plugin_hosts[:foo] = [detect_class(true), nil]
|
||||
plugin_host_caps[:foo] = { bar: Class }
|
||||
|
||||
result = subject.host
|
||||
expect(result.capability?(:bar)).to be_true
|
||||
end
|
||||
|
||||
it "should use an exact host if specified" do
|
||||
env.vagrantfile <<-VF
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vagrant.host = "foo"
|
||||
end
|
||||
VF
|
||||
|
||||
plugin_hosts[:foo] = [detect_class(false), nil]
|
||||
plugin_hosts[:bar] = [detect_class(true), nil]
|
||||
plugin_host_caps[:foo] = { bar: Class }
|
||||
|
||||
result = subject.host
|
||||
expect(result.capability?(:bar)).to be_true
|
||||
end
|
||||
|
||||
it "should raise an error if an exact match was specified but not found" do
|
||||
env.vagrantfile <<-VF
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vagrant.host = "bar"
|
||||
end
|
||||
VF
|
||||
|
||||
expect { subject.host }.
|
||||
to raise_error(Vagrant::Errors::HostExplicitNotDetected)
|
||||
end
|
||||
end
|
||||
|
||||
describe "active machines" do
|
||||
it "should be empty if the machines folder doesn't exist" do
|
||||
folder = instance.local_data_path.join("machines")
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
require "pathname"
|
||||
|
||||
require File.expand_path("../../base", __FILE__)
|
||||
|
||||
describe Vagrant::Host do
|
||||
include_context "capability_helpers"
|
||||
|
||||
let(:capabilities) { {} }
|
||||
let(:hosts) { {} }
|
||||
let(:env) { Object.new }
|
||||
|
||||
it "initializes the capabilities" do
|
||||
described_class.any_instance.should_receive(:initialize_capabilities!).
|
||||
with(:foo, hosts, capabilities, env)
|
||||
|
||||
described_class.new(:foo, hosts, capabilities, env)
|
||||
end
|
||||
end
|
|
@ -1,37 +0,0 @@
|
|||
require File.expand_path("../../base", __FILE__)
|
||||
|
||||
describe Vagrant::Hosts do
|
||||
let(:registry) { Hash.new }
|
||||
let(:base_class) { Vagrant::Plugin::V1::Host }
|
||||
|
||||
it "detects the host that matches true" do
|
||||
foo_klass = Class.new(base_class) do
|
||||
def self.match?; false; end
|
||||
end
|
||||
|
||||
bar_klass = Class.new(base_class) do
|
||||
def self.match?; true; end
|
||||
end
|
||||
|
||||
registry[:foo] = foo_klass
|
||||
registry[:bar] = bar_klass
|
||||
|
||||
described_class.detect(registry).should == bar_klass
|
||||
end
|
||||
|
||||
it "detects the host that matches true with the highest precedence first" do
|
||||
foo_klass = Class.new(base_class) do
|
||||
def self.match?; true; end
|
||||
end
|
||||
|
||||
bar_klass = Class.new(base_class) do
|
||||
def self.match?; true; end
|
||||
def self.precedence; 9; end
|
||||
end
|
||||
|
||||
registry[:foo] = foo_klass
|
||||
registry[:bar] = bar_klass
|
||||
|
||||
described_class.detect(registry).should == bar_klass
|
||||
end
|
||||
end
|
|
@ -126,15 +126,32 @@ describe Vagrant::Plugin::V2::Manager do
|
|||
end
|
||||
|
||||
pB = plugin do |p|
|
||||
p.host("bar") { "baz" }
|
||||
p.host("bar", "foo") { "baz" }
|
||||
end
|
||||
|
||||
instance.register(pA)
|
||||
instance.register(pB)
|
||||
|
||||
instance.hosts.to_hash.length.should == 2
|
||||
instance.hosts[:foo].should == "bar"
|
||||
instance.hosts[:bar].should == "baz"
|
||||
instance.hosts[:foo].should == ["bar", nil]
|
||||
instance.hosts[:bar].should == ["baz", :foo]
|
||||
end
|
||||
|
||||
it "should enumerate registered host capabilities" do
|
||||
pA = plugin do |p|
|
||||
p.host_capability("foo", "foo") { "bar" }
|
||||
end
|
||||
|
||||
pB = plugin do |p|
|
||||
p.host_capability("bar", "foo") { "baz" }
|
||||
end
|
||||
|
||||
instance.register(pA)
|
||||
instance.register(pB)
|
||||
|
||||
instance.host_capabilities.length.should == 2
|
||||
instance.host_capabilities[:foo][:foo].should == "bar"
|
||||
instance.host_capabilities[:bar][:foo].should == "baz"
|
||||
end
|
||||
|
||||
it "should enumerate registered provider classes" do
|
||||
|
|
|
@ -192,7 +192,7 @@ describe Vagrant::Plugin::V2::Plugin do
|
|||
host("foo") { "bar" }
|
||||
end
|
||||
|
||||
plugin.host[:foo].should == "bar"
|
||||
plugin.components.hosts[:foo].should == ["bar", nil]
|
||||
end
|
||||
|
||||
it "should lazily register host classes" do
|
||||
|
@ -214,6 +214,16 @@ describe Vagrant::Plugin::V2::Plugin do
|
|||
end
|
||||
end
|
||||
|
||||
describe "host capabilities" do
|
||||
it "should register host capabilities" do
|
||||
plugin = Class.new(described_class) do
|
||||
host_capability("foo", "bar") { "baz" }
|
||||
end
|
||||
|
||||
plugin.components.host_capabilities[:foo][:bar].should == "baz"
|
||||
end
|
||||
end
|
||||
|
||||
describe "providers" do
|
||||
it "should register provider classes" do
|
||||
plugin = Class.new(described_class) do
|
||||
|
|
|
@ -219,6 +219,7 @@
|
|||
<li<%= sidebar_current("plugins-guests") %>><a href="/v2/plugins/guests.html">Guests</a></li>
|
||||
<li<%= sidebar_current("plugins-guestcapabilities") %>><a href="/v2/plugins/guest-capabilities.html">Guest Capabilities</a></li>
|
||||
<li<%= sidebar_current("plugins-hosts") %>><a href="/v2/plugins/hosts.html">Hosts</a></li>
|
||||
<li<%= sidebar_current("plugins-hostcapabilities") %>><a href="/v2/plugins/host-capabilities.html">Host Capabilities</a></li>
|
||||
<li<%= sidebar_current("plugins-providers") %>><a href="/v2/plugins/providers.html">Providers</a></li>
|
||||
<li<%= sidebar_current("plugins-provisioners") %>><a href="/v2/plugins/provisioners.html">Provisioners</a></li>
|
||||
<li<%= sidebar_current("plugins-packaging") %>><a href="/v2/plugins/packaging.html">Packaging & Distribution</a></li>
|
||||
|
|
|
@ -64,7 +64,7 @@ end
|
|||
```
|
||||
|
||||
After detecting an OS, that OS is used for various
|
||||
[guest capabilities](/v2/plugins/guest_capabilities.html) that may be
|
||||
[guest capabilities](/v2/plugins/guest-capabilities.html) that may be
|
||||
required.
|
||||
|
||||
## Guest Inheritance
|
||||
|
@ -76,7 +76,7 @@ Inheritance allows guests to share a lot of common behavior while allowing
|
|||
distro-specific overrides.
|
||||
|
||||
Inheritance is not done via standard Ruby class inheritance because Vagrant
|
||||
uses a custom [capability-based](/v2/plugins/guest_capabilities.html) system.
|
||||
uses a custom [capability-based](/v2/plugins/guest-capabilities.html) system.
|
||||
Vagrant handles inheritance dispatch for you.
|
||||
|
||||
To subclass another guest, specify that guest's name as a second parameter
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
page_title: "Host Capabilities - Plugin Development"
|
||||
sidebar_current: "plugins-hostcapabilities"
|
||||
---
|
||||
|
||||
# Plugin Development: Host Capabilities
|
||||
|
||||
This page documents how to add new capabilities for [hosts](/v2/plugins/hosts.html)
|
||||
to Vagrant, allowing Vagrant to perform new actions on specific host
|
||||
operating systems.
|
||||
Prior to reading this, you should be familiar
|
||||
with the [plugin development basics](/v2/plugins/development-basics.html).
|
||||
|
||||
<div class="alert alert-warn">
|
||||
<p>
|
||||
<strong>Warning: Advanced Topic!</strong> Developing plugins is an
|
||||
advanced topic that only experienced Vagrant users who are reasonably
|
||||
comfortable with Ruby should approach.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
Host capabilities augment [hosts](/v2/plugins/hosts.html) by attaching
|
||||
specific "capabilities" to the host, which are actions that can be performed
|
||||
in the context of that host operating system.
|
||||
|
||||
The power of capabilities is that plugins can add new capabilities to
|
||||
existing host operating systems without modifying the core of Vagrant.
|
||||
In earlier versions of Vagrant, all the host logic was contained in the
|
||||
core of Vagrant and wasn't easily augmented.
|
||||
|
||||
## Definition and Implementation
|
||||
|
||||
The definition and implementation of host capabilities is identical
|
||||
to [guest capabilities](/v2/plugins/guest-capabilities.html).
|
||||
|
||||
The main difference from guest capabilities, however, is that instead of
|
||||
taking a machine as the first argument, all host capabilities take an
|
||||
instance of `Vagrant::Environment` as their first argument.
|
||||
|
||||
Access to the environment allows host capabilities to access global state,
|
||||
specific machines, and also allows them to call other host capabilities.
|
||||
|
||||
## Calling Capabilities
|
||||
|
||||
Since you have access to the environment in every capability, capabilities can
|
||||
also call _other_ host capabilities. This is useful for using the inheritance
|
||||
mechanism of capabilities to potentially ask helpers for more information.
|
||||
For example, the "linux" guest has a "nfs\_check\_command" capability that
|
||||
returns the command to use to check if NFS is running.
|
||||
|
||||
Capabilities on child guests of Linux such as RedHat or Arch use this
|
||||
capability to mostly inherit the Linux behavior, except for this minor
|
||||
detail.
|
||||
|
||||
Capabilities can be called like so:
|
||||
|
||||
```ruby
|
||||
environment.host.capability(:capability_name)
|
||||
```
|
||||
|
||||
Any additional arguments given to the method will be passed on to the
|
||||
capability, and the capability will return the value that the actual
|
||||
capability returned.
|
|
@ -5,9 +5,9 @@ sidebar_current: "plugins-hosts"
|
|||
|
||||
# Plugin Development: Hosts
|
||||
|
||||
This page documents how to add new host OS implementations to Vagrant,
|
||||
allowing Vagrant to properly configure new host operating systems
|
||||
for features such as NFS shared folders. Prior to reading this, you should be familiar
|
||||
This page documents how to add new host OS detection to Vagrant, allowing
|
||||
Vagrant to properly execute host-specific operations on new operating systems.
|
||||
Prior to reading this, you should be familiar
|
||||
with the [plugin development basics](/v2/plugins/development-basics.html).
|
||||
|
||||
<div class="alert alert-warn">
|
||||
|
@ -18,29 +18,78 @@ with the [plugin development basics](/v2/plugins/development-basics.html).
|
|||
</p>
|
||||
</div>
|
||||
|
||||
Vagrant has some features that require host OS-specific actions, such as
|
||||
exporting NFS folders. These tasks vary from operating system to operating
|
||||
system. Vagrant uses host detection as well as
|
||||
[host capabilities](/v2/plugins/host-capabilities.html) to perform these
|
||||
host OS-specific operations.
|
||||
|
||||
## Definition Component
|
||||
|
||||
Within the context of a plugin definition, new hosts can be defined
|
||||
like so:
|
||||
|
||||
```ruby
|
||||
host "some_os" do
|
||||
host "ubuntu" do
|
||||
require_relative "host"
|
||||
Host
|
||||
end
|
||||
```
|
||||
|
||||
Guests are defined with the `host` method. The first argument is th
|
||||
name of the host. This name isn't actually used anywhere, but may in
|
||||
the future, so choose something helpful. Then, the block argument returns a
|
||||
Hosts are defined with the `host` method. The first argument is the
|
||||
name of the host. This name isn't actually used anywhere, but may in the
|
||||
future, so choose something helpful. Then, the block argument returns a
|
||||
class that implements the `Vagrant.plugin(2, :host)` interface.
|
||||
|
||||
## Implementation
|
||||
|
||||
Implementations of hosts subclass `Vagrant.plugin(2, :host)`. Within
|
||||
this implementation, various methods for different tasks must be implemented.
|
||||
Instead of going over each task, the easiest example would be to take a
|
||||
look at an existing host implementation.
|
||||
Implementations of hosts subclass `Vagrant.plugin("2", "host")`. Within
|
||||
this implementation, only the `detect?` method needs to be implemented.
|
||||
|
||||
There are [many host implementations](https://github.com/mitchellh/vagrant/tree/master/plugins/hosts),
|
||||
but you can view the [BSD host implementation](https://github.com/mitchellh/vagrant/blob/master/plugins/hosts/bsd/host.rb) as a starting point.
|
||||
The `detect?` method is called by Vagrant very early on in its initialization
|
||||
process to determine if the OS that Vagrant is running on is this hsot.
|
||||
If you detect that it is your operating system, return `true` from `detect?`.
|
||||
Otherwise, return `false`.
|
||||
|
||||
```
|
||||
class MyHost < Vagrant.plugin("2", "host")
|
||||
def detect?(environment)
|
||||
File.file?("/etc/arch-release")
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
After detecting an OS, that OS is used for various
|
||||
[host capabilities](/v2/plugins/host-capabilities.html) that may be
|
||||
required.
|
||||
|
||||
## Host Inheritance
|
||||
|
||||
Vagrant also supports a form of inheritance for hosts, since sometimes
|
||||
operating systems stem from a common root. A good example of this is Linux
|
||||
is the root of Debian, which further is the root of Ubuntu in many cases.
|
||||
Inheritance allows hosts to share a lot of common behavior while allowing
|
||||
distro-specific overrides.
|
||||
|
||||
Inheritance is not done via standard Ruby class inheritance because Vagrant
|
||||
uses a custom [capability-based](/v2/plugins/host-capabilities.html) system.
|
||||
Vagrant handles inheritance dispatch for you.
|
||||
|
||||
To subclass another host, specify that host's name as a second parameter
|
||||
in the host definition:
|
||||
|
||||
```ruby
|
||||
host "ubuntu", "debian" do
|
||||
require_relative "host"
|
||||
Host
|
||||
end
|
||||
```
|
||||
|
||||
With the above component, the "ubuntu" host inherits from "debian." When
|
||||
a capability is looked up for "ubuntu", all capabilities from "debian" are
|
||||
also available, and any capabilities in "ubuntu" override parent capabilities.
|
||||
|
||||
When detecting operating systems with `detect?`, Vagrant always does a
|
||||
depth-first search by searching the children operating systems before
|
||||
checking their parents. Therefore, it is guaranteed in the above example
|
||||
that the `detect?` method on "ubuntu" will be called before "debian."
|
||||
|
|
Loading…
Reference in New Issue