Merge pull request #1 from mitchellh/master

Update from official repo
This commit is contained in:
tomfanning 2016-01-11 15:49:19 +00:00
commit 6f5d1fc449
410 changed files with 13338 additions and 3725 deletions

4
.gitignore vendored
View File

@ -1,6 +1,10 @@
# OS-specific
.DS_Store
# Editor swapfiles
.*.sw?
*~
# Vagrant stuff
acceptance_config.yml
boxes/*

View File

@ -1,13 +1,187 @@
## Next Release
FEATURES:
IMPROVEMENTS:
## Next Version (unreleased)
BUG FIXES:
- provisioners/ansible_local: Fix error in `playbook` existence check when
running on a Windows host [GH-6740]
## 1.8.1 (December 21, 2015)
BUG FIXES:
- core: Don't create ".bundle" directory in pwd [GH-6717]
- core: Fix exception on installing VirtualBox [GH-6713]
- core: Do not convert standalone drive letters such as "D:" to
UNC paths [GH-6598]
- core: Fix a crash in parsing the config in some cases with network
configurations [GH-6730]
- commands/up: Smarter logic about what provider to install, avoiding
situations where VirtualBox was installed over the correct provider [GH-6731]
- guests/debian: Fix Docker install [GH-6722]
- provisioners/chef: convert chef version to a string before comparing for
the command builder [GH-6709, GH-6711]
- provisioners/shell: convert env var values to strings [GH-6714]
## 1.8.0 (December 21, 2015)
FEATURES:
- **New Command: `vagrant powershell`**: For machines that support it,
this will open a PowerShell prompt.
- **New Command: `vagrant port`**: For machines that support it, this will
display the list of forwarded ports from the guest to the host.
- **Linked Clones**: VirtualBox and VMware providers now support
linked clones for very fast (millisecond) imports on up. [GH-4484]
- **Snapshots**: The `vagrant snapshot` command can be used to checkpoint
and restore point-in-time snapshots.
- **IPv6 Private Networks**: Private networking now supports IPv6. This
only works with VirtualBox and VMware at this point. [GH-6342]
- New provisioner: `ansible_local` to execute Ansible from the guest
machine. [GH-2103]
BREAKING CHANGES:
- The `ansible` provisioner now can override the effective ansible remote user
(i.e. `ansible_ssh_user` setting) to always correspond to the vagrant ssh
username. This change is enabled by default, but we expect this to affect
only a tiny number of people as it corresponds to the common usage.
If you however use multiple remote usernames in your Ansible plays, tasks,
or custom inventories, you can simply set the option `force_remote_user` to
false to make Vagrant behave the same as before.
- provisioners/salt: the "config_dir" option has been removed. It has no
effect in Vagrant 1.8. [GH-6073]
IMPROVEMENTS:
- core: allow removal of all box versions with `--all` flag [GH-3462]
- core: prune entries from global status on non-existent cwd [GH-6535]
- core: networking: allow specifying a DHCP IP [GH-6325]
- core: run provisioner cleanup tasks before powering off the VM [GH-6553]
- core: only run provisioner cleanup tasks if they're implemented [GH-6603]
This improves UX, but wasn't a bug before.
- command/plugin: Add `--plugin-clean-sources` flag to reset plugin install
sources, primarily for corp firewalls. [GH-4738]
- command/rsync-auto: SSH connection is cached for faster sync times [GH-6399]
- command/up: provisioners are run on suspend resume [GH-5815]
- communicators/ssh: allow specifying host environment variables to forward
to guests [GH-4132, GH-6562]
- communicators/winrm: Configurable execution time limit [GH-6213]
- providers/virtualbox: cache version lookup, which caused significant
slowdown on some Windows hosts [GH-6552]
- providers/virtualbox: add `public_address` capability for virtualbox
[GH-6583, GH-5978]
- provisioners/chef: perform cleanup tasks on the guest instead of the host
- provisioners/chef: automatically generate a node_name if one was not given
[GH-6555]
- provisioners/chef: install Chef automatically on Windows [GH-6557]
- provisioners/chef: allow the user to specify the Chef product (such as
the Chef Development Kit) [GH-6557]
- provisioners/chef: allow data_bags_path to be an array [GH-5988, GH-6561]
- provisioners/shell: Support interactive mode for elevated PowerShell
scripts [GH-6185]
- provisioners/shell: add `env` option [GH-6588, GH-6516]
- provisioners/ansible+ansible_local: add support for ansible-galaxy [GH-2718]
- provisioners/ansible+ansible_local: add support for group and host variables
in the generated inventory [GH-6619]
- provisioners/ansible+ansible_local: add support for alphanumeric patterns
for groups in the generated inventory [GH-3539]
- provisioners/ansible: add support for WinRM settings [GH-5086]
- provisioners/ansible: add new `force_remote_user` option to control whether
`ansible_ssh_user` parameter should be applied or not [GH-6348]
- provisioners/ansible: show a warning when running from a Windows Host [GH-5292]
- pushes/local-exec: add support for specifying script args [GH-6661, GH-6660]
- guests/slackware: add support for networking [GH-6514]
BUG FIXES:
- core: Ctrl-C weirdness fixed where it would exit parent process
before Vagrant finished cleaning up [GH-6085]
- core: DHCP network configurations don't warn on IP addresses ending
in ".1" [GH-6150]
- core: only append `access_token` when it does not exist in the URL
[GH-6395, GH-6534]
- core: use the correct private key when packaging a box [GH-6406]
- core: fix crash when using invalid box checksum type [GH-6327]
- core: don't check for metadata if the download URL is not HTTP [GH-6540]
- core: don't make custom dotfile path if there is no Vagrantfile [GH-6542]
- core: more robust check for admin privs on Windows [GH-5616]
- core: properly detect when HTTP server doesn't support byte ranges and
retry from scratch [GH-4479]
- core: line numbers show properly in Vagrantfile syntax errors
on Windows [GH-6445]
- core: catch errors setting env vars on Windows [GH-6017]
- core: remove cached synced folders when they're removed from the
Vagrantfile [GH-6567]
- core: use case-insensitive comparison for box checksum validations
[GH-6648, GH-6650]
- commands/box: add command with `~` paths on Windows works [GH-5747]
- commands/box: the update command supports CA settings [GH-4473]
- commands/box: removing all versions and providers of a box will properly
clean all directories in `~/.vagrant.d/boxes` [GH-3570]
- commands/box: outdated global won't halt on metadata download failure [GH-6453]
- commands/login: respect environment variables in `vagrant login` command
[GH-6590, GH-6422]
- commands/package: when re-packaging a packaged box, preserve the
generated SSH key [GH-5780]
- commands/plugin: retry plugin install automatically a few times to
avoid network issues [GH-6097]
- commands/rdp: prefer `xfreerdp` if it is available on Linux [GH-6475]
- commands/up: the `--provision-with` flag works with provisioner names [GH-5981]
- communicator/ssh: fix potential crash case with PTY [GH-6225]
- communicator/ssh: escape IdentityFile path [GH-6428, GH-6589]
- communicator/winrm: respect `boot_timeout` setting [GH-6229]
- communicator/winrm: execute scheduled tasks immediately on Windows XP
since elevation isn't required [GH-6195]
- communicator/winrm: Decouple default port forwarding rules for "winrm" and
"winrm-ssl" [GH-6581]
- communicator/winrm: Hide progress bars from PowerShell v5 [GH-6309]
- guests/arch: enable network device after setting it up [GH-5737]
- guests/darwin: advanced networking works with more NICs [GH-6386]
- guests/debian: graceful shutdown works properly with newer releases [GH-5986]
- guests/fedora: Preserve `localhost` entry when changing hostname [GH-6203]
- guests/fedora: Use dnf if it is available [GH-6288]
- guests/linux: when replacing a public SSH key, use POSIX-compliant
sed flags [GH-6565]
- guests/suse: DHCP network interfaces properly configured [GH-6502]
- hosts/slackware: Better detection of NFS [GH-6367]
- providers/hyper-v: support generation 2 VMs [GH-6372]
- providers/hyper-v: support VMs with more than one NIC [GH-4346]
- providers/hyper-v: check if user is in the Hyper-V admin group if
they're not a Windows admin [GH-6662]
- providers/virtualbox: ignore "Unknown" status bridge interfaces [GH-6061]
- providers/virtualbox: only fix ipv6 interfaces that are in use
[GH-6586, GH-6552]
- provisioners/ansible: use quotes for the `ansible_ssh_private_key_file`
value in the generated inventory [GH-6209]
- provisioners/ansible: use quotes when passing the private key files via
OpenSSH `-i` command line arguments [GH-6671]
- provisioners/ansible: don't show the `ansible-playbook` command when verbose
option is an empty string
- provisioners/chef: fix `nodes_path` for Chef Zero [GH-6025, GH-6049]
- provisioners/chef: do not error when the `node_name` is unset
[GH-6005, GH-6064, GH-6541]
- provisioners/chef: only force the formatter on Chef 11 or higher
[GH-6278, GH-6556]
- provisioners/chef: require `nodes_path` to be set for Chef Zero
[GH-6110, GH-6559]
- provisioners/puppet: apply provisioner uses correct default manifests
with environments. [GH-5987]
- provisioners/puppet: remove broken backticks [GH-6404]
- provisioners/puppet: find Puppet binary properly on Windows [GH-6259]
- provisioners/puppet-server: works with Puppet Collection 1 [GH-6389]
- provisioners/salt: call correct executables on Windows [GH-5999]
- provisioners/salt: log level and colorize works for masterless [GH-6474]
- push/local-exec: use subprocess on windows when fork does not exist
[GH-5307, GH-6563]
- push/heroku: use current branch [GH-6554]
- synced\_folders/rsync: on Windows, replace all paths with Cygwin
paths since all rsync implementations require this [GH-6160]
- synced\_folders/smb: use credentials files to allow for more characters
in password [GH-4230]
PLUGIN AUTHOR CHANGES:
- installer: Upgrade to Ruby 2.2.3
## 1.7.4 (July 17, 2015)
@ -17,6 +191,7 @@ BUG FIXES:
- communicators/ssh: use the same SSH args for `vagrant ssh` with and without
a command [GH-4986, GH-5928]
- guests/fedora: networks can be configured without nmcli [GH-5931]
- guests/fedora: biosdevname can return 4 or 127 [GH-6139]
- guests/redhat: systemd detection should happen on guest [GH-5948]
- guests/ubuntu: setting hostname fixed in 12.04 [GH-5937]
- hosts/linux: NFS can be configured without `$TMP` set on the host [GH-5954]
@ -2681,4 +2856,3 @@ compatibility.
The changelog began with version 0.5.1 so any changes prior to that
can be seen by checking the tagged releases and reading git commit
messages.

View File

@ -1,6 +1,6 @@
The MIT License
Copyright (c) 2010-2015 Mitchell Hashimoto
Copyright (c) 2010-2016 Mitchell Hashimoto
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@ -1,6 +1,6 @@
# Vagrant
* Website: [http://www.vagrantup.com](http://www.vagrantup.com)
* Website: [https://www.vagrantup.com/](https://www.vagrantup.com/)
* Source: [https://github.com/mitchellh/vagrant](https://github.com/mitchellh/vagrant)
* IRC: `#vagrant` on Freenode
* Mailing list: [Google Groups](http://groups.google.com/group/vagrant-up)
@ -19,12 +19,12 @@ between Windows, Mac OS X, and Linux.
## Quick Start
For the quick-start, we'll bring up a development machine on
[VirtualBox](http://www.virtualbox.org) because it is free and works
[VirtualBox](https://www.virtualbox.org/) because it is free and works
on all major platforms. Vagrant can, however, work with almost any
system such as OpenStack, VMware, Docker, etc.
system such as [OpenStack] (https://www.openstack.org/), [VMware] (http://www.vmware.com/), [Docker] (https://docs.docker.com/), etc.
First, make sure your development machine has
[VirtualBox](http://www.virtualbox.org)
[VirtualBox](https://www.virtualbox.org/)
installed. After this,
[download and install the appropriate Vagrant package for your OS](http://www.vagrantup.com/downloads).
@ -53,13 +53,11 @@ Ruby 2.0 is needed.
## Contributing to Vagrant
### Dependencies and Unit Tests
To install Vagrant from source, please [follow the guide in the Wiki](https://github.com/mitchellh/vagrant/wiki/Installing-Vagrant-from-Source).
To hack on Vagrant, you'll need [bundler](http://github.com/carlhuda/bundler) which can
be installed with a simple `gem install bundler`. Afterwards, do the following:
You can run the test suite with:
bundle install
rake
bundle exec rake
This will run the unit test suite, which should come back all green! Then you're good to go!

View File

@ -10,5 +10,7 @@ for each item will be kept below.
* `emacs` - Contains a file for enabling Ruby syntax highlighting for `Vagrantfile`s in `emacs`.
* `st` - Contains a `.sublime-settings` file for enabling Ruby syntax highlighting
for `Vagrantfile`s in Sublime Text.
* `sudoers` - Contains the pieces of `/etc/sudoers` configuration to avoid password entry when
starting machines.
* `vim` - Contains a `.vim` file for enabling Ruby syntax highlighting
for `Vagrantfile`s in `vim`.
for `Vagrantfile`s in `vim`.

View File

@ -2,5 +2,6 @@ Cmnd_Alias VAGRANT_EXPORTS_ADD = /usr/bin/tee -a /etc/exports
Cmnd_Alias VAGRANT_NFSD_CHECK = /usr/bin/systemctl status nfs-server.service
Cmnd_Alias VAGRANT_NFSD_START = /usr/bin/systemctl start nfs-server.service
Cmnd_Alias VAGRANT_NFSD_APPLY = /usr/sbin/exportfs -ar
Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /etc/exports
%vagrant ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY, VAGRANT_EXPORTS_REMOVE
Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /*/exports
Cmnd_Alias VAGRANT_EXPORTS_REMOVE_2 = /bin/cp /*/exports /etc/exports
%vagrant ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY, VAGRANT_EXPORTS_REMOVE, VAGRANT_EXPORTS_REMOVE_2

View File

@ -0,0 +1,7 @@
Cmnd_Alias VAGRANT_EXPORTS_ADD = /usr/bin/tee -a /etc/exports
Cmnd_Alias VAGRANT_NFSD_CHECK = /sbin/service nfsserver status
Cmnd_Alias VAGRANT_NFSD_START = /sbin/service nfsserver start
Cmnd_Alias VAGRANT_NFSD_APPLY = /usr/sbin/exportfs -ar
Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /usr/bin/sed -r -e * d -ibak /*/exports
Cmnd_Alias VAGRANT_EXPORTS_REMOVE_2 = /usr/bin/cp /*/exports /etc/exports
%vagrant ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY, VAGRANT_EXPORTS_REMOVE, VAGRANT_EXPORTS_REMOVE_2

View File

@ -4,5 +4,6 @@ Cmnd_Alias VAGRANT_EXPORTS_ADD = /usr/bin/tee -a /etc/exports
Cmnd_Alias VAGRANT_NFSD_CHECK = /etc/init.d/nfs-kernel-server status
Cmnd_Alias VAGRANT_NFSD_START = /etc/init.d/nfs-kernel-server start
Cmnd_Alias VAGRANT_NFSD_APPLY = /usr/sbin/exportfs -ar
Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /etc/exports
%sudo ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY, VAGRANT_EXPORTS_REMOVE
Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /*/exports
Cmnd_Alias VAGRANT_EXPORTS_REMOVE_2 = /bin/cp /*/exports /etc/exports
%sudo ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY, VAGRANT_EXPORTS_REMOVE, VAGRANT_EXPORTS_REMOVE_2

View File

@ -20,9 +20,11 @@ module Vagrant
autoload :HandleBox, "vagrant/action/builtin/handle_box"
autoload :HandleBoxUrl, "vagrant/action/builtin/handle_box_url"
autoload :HandleForwardedPortCollisions, "vagrant/action/builtin/handle_forwarded_port_collisions"
autoload :IsEnvSet, "vagrant/action/builtin/is_env_set"
autoload :IsState, "vagrant/action/builtin/is_state"
autoload :Lock, "vagrant/action/builtin/lock"
autoload :Message, "vagrant/action/builtin/message"
autoload :PrepareClone, "vagrant/action/builtin/prepare_clone"
autoload :Provision, "vagrant/action/builtin/provision"
autoload :ProvisionerCleanup, "vagrant/action/builtin/provisioner_cleanup"
autoload :SetHostname, "vagrant/action/builtin/set_hostname"

View File

@ -381,7 +381,9 @@ module Vagrant
@logger.info("URL is a file or protocol not found and assuming file.")
file_path = File.expand_path(url)
file_path = Util::Platform.cygwin_windows_path(file_path)
url = "file:#{file_path}"
file_path = file_path.gsub("\\", "/")
file_path = "/#{file_path}" if !file_path.start_with?("/")
url = "file://#{file_path}"
end
# If the temporary path exists, verify it is not too old. If its
@ -491,6 +493,12 @@ module Vagrant
end
end
# If this isn't HTTP, then don't do the HEAD request
if !uri.scheme.downcase.start_with?("http")
@logger.info("not checking metadata since box URI isn't HTTP")
return false
end
output = d.head
match = output.scan(/^Content-Type: (.+?)$/i).last
return false if !match
@ -507,14 +515,14 @@ module Vagrant
Digest::SHA2
else
raise Errors::BoxChecksumInvalidType,
type: env[:box_checksum_type].to_s
type: checksum_type.to_s
end
@logger.info("Validating checksum with #{checksum_klass}")
@logger.info("Expected checksum: #{checksum}")
actual = FileChecksum.new(path, checksum_klass).checksum
if actual != checksum
if actual.casecmp(checksum) != 0
raise Errors::BoxChecksumMismatch,
actual: actual,
expected: checksum

View File

@ -37,13 +37,23 @@ module Vagrant
end
constraints = machine.config.vm.box_version
# Have download options specified in the environment override
# options specified for the machine.
download_options = {
ca_cert: env[:ca_cert] || machine.config.vm.box_download_ca_cert,
ca_path: env[:ca_path] || machine.config.vm.box_download_ca_path,
client_cert: env[:client_cert] ||
machine.config.vm.box_download_client_cert,
insecure: !env[:insecure].nil? ?
env[:insecure] : machine.config.vm.box_download_insecure
}
env[:ui].output(I18n.t(
"vagrant.box_outdated_checking_with_refresh",
name: box.name))
update = nil
begin
update = box.has_update?(constraints)
update = box.has_update?(constraints, download_options: download_options)
rescue Errors::BoxMetadataDownloadError => e
env[:ui].warn(I18n.t(
"vagrant.box_outdated_metadata_download_error",

View File

@ -15,6 +15,7 @@ module Vagrant
box_provider = env[:box_provider]
box_provider = box_provider.to_sym if box_provider
box_version = env[:box_version]
box_remove_all_versions = env[:box_remove_all_versions]
boxes = {}
env[:box_collection].all.each do |n, v, p|
@ -53,7 +54,7 @@ module Vagrant
if all_versions.length == 1
# There is only one version, just use that.
box_version = all_versions.first
else
elsif not box_remove_all_versions
# There are multiple versions, we can't choose.
raise Errors::BoxRemoveMultiVersion,
name: box_name,
@ -68,48 +69,54 @@ module Vagrant
versions: all_versions.sort.map { |k| " * #{k}" }.join("\n")
end
box = env[:box_collection].find(
box_name, box_provider, box_version)
versions_to_remove = [box_version]
versions_to_remove = all_versions if box_remove_all_versions
# Verify that this box is not in use by an active machine,
# otherwise warn the user.
users = box.in_use?(env[:machine_index]) || []
users = users.find_all { |u| u.valid?(env[:home_path]) }
if !users.empty?
# Build up the output to show the user.
users = users.map do |entry|
"#{entry.name} (ID: #{entry.id})"
end.join("\n")
versions_to_remove.sort.each do |version_to_remove|
box = env[:box_collection].find(
box_name, box_provider, box_version)
force_key = :force_confirm_box_remove
message = I18n.t(
"vagrant.commands.box.remove_in_use_query",
name: box.name,
provider: box.provider,
version: box.version,
users: users) + " "
# Verify that this box is not in use by an active machine,
# otherwise warn the user.
users = box.in_use?(env[:machine_index]) || []
users = users.find_all { |u| u.valid?(env[:home_path]) }
if !users.empty?
# Build up the output to show the user.
users = users.map do |entry|
"#{entry.name} (ID: #{entry.id})"
end.join("\n")
# Ask the user if we should do this
stack = Builder.new.tap do |b|
b.use Confirm, message, force_key
force_key = :force_confirm_box_remove
message = I18n.t(
"vagrant.commands.box.remove_in_use_query",
name: box.name,
provider: box.provider,
version: box.version,
users: users) + " "
# Ask the user if we should do this
stack = Builder.new.tap do |b|
b.use Confirm, message, force_key
end
result = env[:action_runner].run(stack, env)
if !result[:result]
# They said "no", so continue with the next box
next
end
end
result = env[:action_runner].run(stack, env)
if !result[:result]
# They said "no", so just return
return @app.call(env)
end
env[:ui].info(I18n.t("vagrant.commands.box.removing",
name: box.name,
provider: box.provider,
version: box.version))
box.destroy!
env[:box_collection].clean(box.name)
# Passes on the removed box to the rest of the middleware chain
env[:box_removed] = box
end
env[:ui].info(I18n.t("vagrant.commands.box.removing",
name: box.name,
provider: box.provider,
version: box.version))
box.destroy!
# Passes on the removed box to the rest of the middleware chain
env[:box_removed] = box
@app.call(env)
end
end

View File

@ -128,7 +128,7 @@ module Vagrant
port_checker[repaired_port] ||
lease_check(repaired_port)
if in_use
@logger.info("Reparied port also in use: #{repaired_port}. Trying another...")
@logger.info("Repaired port also in use: #{repaired_port}. Trying another...")
next
end
@ -156,8 +156,6 @@ module Vagrant
new_port: repaired_port.to_s))
end
end
@app.call(env)
end
def lease_check(port)

View File

@ -0,0 +1,22 @@
module Vagrant
module Action
module Builtin
# This middleware is meant to be used with Call and can check if
# a variable in env is set.
class IsEnvSet
def initialize(app, env, key, **opts)
@app = app
@logger = Log4r::Logger.new("vagrant::action::builtin::is_env_set")
@key = key
end
def call(env)
@logger.debug("Checking if env is set: '#{@key}'")
env[:result] = !!env[@key]
@logger.debug(" - Result: #{env[:result].inspect}")
@app.call(env)
end
end
end
end
end

View File

@ -76,6 +76,16 @@ module Vagrant
if opts[:merge]
existing = cached_synced_folders(machine)
if existing
if opts[:vagrantfile]
# Go through and find any cached that were from the
# Vagrantfile itself. We remove those if it was requested.
existing.each do |impl, fs|
fs.each do |id, data|
fs.delete(id) if data[:__vagrantfile]
end
end
end
folders.each do |impl, fs|
existing[impl] ||= {}
fs.each do |id, data|
@ -101,7 +111,12 @@ module Vagrant
return cached_synced_folders(machine) if opts[:cached]
config = opts[:config]
config ||= machine.config.vm
root = false
if !config
config = machine.config.vm
root = true
end
config_folders = config.synced_folders
folders = {}
@ -131,9 +146,17 @@ module Vagrant
end
end
# Get the data to store
data = data.dup
if root
# If these are the root synced folders (attached directly)
# to the Vagrantfile, then we mark it as such.
data[:__vagrantfile] = true
end
# Keep track of this shared folder by the implementation.
folders[impl] ||= {}
folders[impl][id] = data.dup
folders[impl][id] = data
end
# If we have folders with the "default" key, then determine the

View File

@ -0,0 +1,38 @@
require "log4r"
module Vagrant
module Action
module Builtin
class PrepareClone
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::action::vm::prepare_clone")
end
def call(env)
# If we aren't cloning, then do nothing
if !env[:machine].config.vm.clone
return @app.call(env)
end
# We need to get the machine ID from this Vagrant environment
clone_env = env[:machine].env.environment(
env[:machine].config.vm.clone)
raise Errors::CloneNotFound if !clone_env.root_path
# Get the machine itself
clone_machine = clone_env.machine(
clone_env.primary_machine_name, env[:machine].provider_name)
raise Errors::CloneMachineNotFound if !clone_machine.id
# Set the ID of the master so we know what to clone from
env[:clone_id] = clone_machine.id
env[:clone_machine] = clone_machine
# Continue
@app.call(env)
end
end
end
end
end

View File

@ -13,8 +13,9 @@ module Vagrant
def initialize(app, env, place=nil)
@app = app
@logger = Log4r::Logger.new("vagrant::action::builtin::provision_cleanup")
@place ||= :after
@place = @place.to_sym
place ||= :after
@place = place.to_sym
end
def call(env)
@ -31,10 +32,20 @@ module Vagrant
# Ask the provisioners to modify the configuration if needed
provisioner_instances(env).each do |p, _|
env[:ui].info(I18n.t(
"vagrant.provisioner_cleanup",
name: type_map[p].to_s))
p.cleanup
name = type_map[p].to_s
# Check if the subclass defined a cleanup method. The parent
# provisioning class defines a `cleanup` method, so we cannot use
# `respond_to?` here. Instead, we have to check if _this_ instance
# defines a cleanup task.
if p.public_methods(false).include?(:cleanup)
env[:ui].info(I18n.t("vagrant.provisioner_cleanup",
name: name,
))
p.cleanup
else
@logger.debug("Skipping cleanup tasks for `#{name}' - not defined")
end
end
end
end

View File

@ -23,6 +23,7 @@ module Vagrant
config: env[:synced_folders_config],
}
@logger.info("SyncedFolders loading from cache: #{opts[:cached]}")
folders = synced_folders(env[:machine], **opts)
original_folders = folders
@ -121,8 +122,11 @@ module Vagrant
save_synced_folders(env[:machine], all)
else
save_opts = { merge: true }
save_opts[:vagrantfile] = true if !opts[:config]
# Save the synced folders
save_synced_folders(env[:machine], original_folders, merge: true)
save_synced_folders(env[:machine], original_folders, **save_opts)
end
end
end

View File

@ -30,7 +30,7 @@ module Vagrant
def call(env)
@env = env
file_name = File.basename(@env["package.output"].to_s)
raise Errors::PackageOutputDirectory if File.directory?(tar_path)
raise Errors::PackageOutputExists, file_name:file_name if File.exist?(tar_path)
raise Errors::PackageRequiresDirectory if !env["package.directory"] ||
@ -113,6 +113,21 @@ module Vagrant
# If we don't have a generated private key, we do nothing
path = @env[:machine].data_dir.join("private_key")
if !path.file?
# If we have a private key that was copied into this box,
# then we copy that. This is a bit of a heuristic and can be a
# security risk if the key is named the correct thing, but
# we'll take that risk for dev environments.
(@env[:machine].config.ssh.private_key_path || []).each do |p|
# If we have the correctly named key, copy it
if File.basename(p) == "vagrant_private_key"
path = Pathname.new(p)
break
end
end
end
# If we still have no matching key, do nothing
return if !path.file?
# Copy it into our box directory

View File

@ -115,8 +115,9 @@ module Vagrant
# Loads the metadata URL and returns the latest metadata associated
# with this box.
#
# @param [Hash] download_options Options to pass to the downloader.
# @return [BoxMetadata]
def load_metadata
def load_metadata(**download_options)
tf = Tempfile.new("vagrant")
tf.close
@ -127,7 +128,7 @@ module Vagrant
url = "file:#{url}"
end
opts = { headers: ["Accept: application/json"] }
opts = { headers: ["Accept: application/json"] }.merge(download_options)
Util::Downloader.new(url, tf.path, **opts).download!
BoxMetadata.new(File.open(tf.path, "r"))
rescue Errors::DownloaderError => e
@ -148,7 +149,7 @@ module Vagrant
# satisfy. If nil, the version constrain defaults to being a
# larger version than this box.
# @return [Array]
def has_update?(version=nil)
def has_update?(version=nil, download_options: {})
if !@metadata_url
raise Errors::BoxUpdateNoMetadata, name: @name
end
@ -156,7 +157,7 @@ module Vagrant
version += ", " if version
version ||= ""
version += "> #{@version}"
md = self.load_metadata
md = self.load_metadata(download_options)
newer = md.version(version, provider: @provider)
return nil if !newer

View File

@ -12,7 +12,9 @@ module Vagrant
# for accessing/finding individual boxes, adding new boxes, or deleting
# boxes.
class BoxCollection
TEMP_PREFIX = "vagrant-box-add-temp-"
TEMP_PREFIX = "vagrant-box-add-temp-".freeze
VAGRANT_SLASH = "-VAGRANTSLASH-".freeze
VAGRANT_COLON = "-VAGRANTCOLON-".freeze
# The directory where the boxes in this collection are stored.
#
@ -346,6 +348,14 @@ module Vagrant
end
end
# Cleans the directory for a box by removing the folders that are
# empty.
def clean(name)
return false if exists?(name)
path = File.join(directory, dir_name(name))
FileUtils.rm_rf(path)
end
protected
# Returns the directory name for the box of the given name.
@ -354,16 +364,16 @@ module Vagrant
# @return [String]
def dir_name(name)
name = name.dup
name.gsub!(":", "-VAGRANTCOLON-") if Util::Platform.windows?
name.gsub!("/", "-VAGRANTSLASH-")
name.gsub!(":", VAGRANT_COLON) if Util::Platform.windows?
name.gsub!("/", VAGRANT_SLASH)
name
end
# Returns the directory name for the box cleaned up
def undir_name(name)
name = name.dup
name.gsub!("-VAGRANTCOLON-", ":")
name.gsub!("-VAGRANTSLASH-", "/")
name.gsub!(VAGRANT_COLON, ":")
name.gsub!(VAGRANT_SLASH, "/")
name
end
@ -440,5 +450,10 @@ module Vagrant
ensure
dir.rmtree if dir.exist?
end
# Checks if a box with a given name exists.
def exists?(box_name)
all.any? { |box| box.first.eql?(box_name) }
end
end
end

View File

@ -7,6 +7,7 @@ require "bundler"
require_relative "shared_helpers"
require_relative "version"
require_relative "util/safe_env"
module Vagrant
# This class manages Vagrant's interaction with Bundler. Vagrant uses
@ -68,12 +69,16 @@ module Vagrant
# we add all our plugin dependencies.
@gemfile = build_gemfile(plugins)
# Set the environmental variables for Bundler
ENV["BUNDLE_APP_CONFIG"] = @appconfigpath
ENV["BUNDLE_CONFIG"] = @configfile.path
ENV["BUNDLE_GEMFILE"] = @gemfile.path
ENV["GEM_PATH"] =
"#{bundle_path}#{::File::PATH_SEPARATOR}#{@gem_path}"
Util::SafeEnv.change_env do |env|
# Set the environmental variables for Bundler
env["BUNDLE_APP_CONFIG"] = @appconfigpath
env["BUNDLE_CONFIG"] = @configfile.path
env["BUNDLE_GEMFILE"] = @gemfile.path
env["BUNDLE_RETRY"] = "3"
env["GEM_PATH"] =
"#{bundle_path}#{::File::PATH_SEPARATOR}#{@gem_path}"
end
Gem.clear_paths
end
@ -178,11 +183,6 @@ module Vagrant
f = File.open(Tempfile.new("vagrant").path + "2", "w+")
f.tap do |gemfile|
if !sources.include?("http://rubygems.org")
gemfile.puts(%Q[source "https://rubygems.org"])
end
gemfile.puts(%Q[source "http://gems.hashicorp.com"])
sources.each do |source|
next if source == ""
gemfile.puts(%Q[source "#{source}"])

View File

@ -221,7 +221,12 @@ module Vagrant
line = "(unknown)"
if e.backtrace && e.backtrace[0]
line = e.backtrace[0].split(":")[1]
e.backtrace[0].split(":").each do |part|
if part =~ /\d+/
line = part.to_i
break
end
end
end
# Report the generic exception

View File

@ -9,6 +9,7 @@ require 'log4r'
require 'vagrant/util/file_mode'
require 'vagrant/util/platform'
require "vagrant/util/silence_warnings"
require "vagrant/vagrantfile"
require "vagrant/version"
@ -158,7 +159,7 @@ module Vagrant
# Setup the local data directory. If a configuration path is given,
# then it is expanded relative to the working directory. Otherwise,
# we use the default which is expanded relative to the root path.
opts[:local_data_path] ||= ENV["VAGRANT_DOTFILE_PATH"]
opts[:local_data_path] ||= ENV["VAGRANT_DOTFILE_PATH"] if !opts[:child]
opts[:local_data_path] ||= root_path.join(DEFAULT_LOCAL_DATA) if !root_path.nil?
if opts[:local_data_path]
@local_data_path = Pathname.new(File.expand_path(opts[:local_data_path], @cwd))
@ -308,6 +309,7 @@ module Vagrant
def default_provider(**opts)
opts[:exclude] = Set.new(opts[:exclude]) if opts[:exclude]
opts[:force_default] = true if !opts.key?(:force_default)
opts[:check_usable] = true if !opts.key?(:check_usable)
default = ENV["VAGRANT_DEFAULT_PROVIDER"]
default = nil if default == ""
@ -375,6 +377,7 @@ module Vagrant
# Find the matching implementation
ordered.each do |_, key, impl, _|
return key if !opts[:check_usable]
return key if impl.usable?(false)
end
@ -382,6 +385,26 @@ module Vagrant
raise Errors::NoDefaultProvider
end
# Returns whether or not we know how to install the provider with
# the given name.
#
# @return [Boolean]
def can_install_provider?(name)
host.capability?(provider_install_key(name))
end
# Installs the provider with the given name.
#
# This will raise an exception if we don't know how to install the
# provider with the given name. You should guard this call with
# `can_install_provider?` for added safety.
#
# An exception will be raised if there are any failures installing
# the provider.
def install_provider(name)
host.capability(provider_install_key(name))
end
# Returns the collection of boxes for the environment.
#
# @return [BoxCollection]
@ -413,6 +436,28 @@ module Vagrant
@config_loader
end
# Loads another environment for the given Vagrantfile, sharing as much
# useful state from this Environment as possible (such as UI and paths).
# Any initialization options can be overidden using the opts hash.
#
# @param [String] vagrantfile Path to a Vagrantfile
# @return [Environment]
def environment(vagrantfile, **opts)
path = File.expand_path(vagrantfile, root_path)
file = File.basename(path)
path = File.dirname(path)
Util::SilenceWarnings.silence! do
Environment.new({
child: true,
cwd: path,
home_path: home_path,
ui_class: ui_class,
vagrantfile_name: file,
}.merge(opts))
end
end
# This defines a hook point where plugin action hooks that are registered
# against the given name will be run in the context of this environment.
#
@ -526,7 +571,13 @@ module Vagrant
if name != "dotlock"
lock("dotlock", retry: true) do
f.close
File.delete(lock_path)
begin
File.delete(lock_path)
rescue
@logger.error(
"Failed to delete lock file #{lock_path} - some other thread " +
"might be trying to acquire it. ignoring this error")
end
end
end
@ -781,7 +832,7 @@ module Vagrant
# This creates the local data directory and show an error if it
# couldn't properly be created.
def setup_local_data_path
def setup_local_data_path(force=false)
if @local_data_path.nil?
@logger.warn("No local data path is set. Local data cannot be stored.")
return
@ -796,6 +847,9 @@ module Vagrant
upgrade_v1_dotfile(@local_data_path)
end
# If we don't have a root path, we don't setup anything
return if !force && root_path.nil?
begin
@logger.debug("Creating: #{@local_data_path}")
FileUtils.mkdir_p(@local_data_path)
@ -854,6 +908,12 @@ module Vagrant
nil
end
# Returns the key used for the host capability for provider installs
# of the given name.
def provider_install_key(name)
"provider_install_#{name}".to_sym
end
# This upgrades a home directory that was in the v1.1 format to the
# v1.5 format. It will raise exceptions if anything fails.
def upgrade_home_path_v1_1
@ -908,7 +968,7 @@ module Vagrant
# Now, we create the actual local data directory. This should succeed
# this time since we renamed the old conflicting V1.
setup_local_data_path
setup_local_data_path(true)
if json_data["active"]
@logger.debug("Upgrading to V2 style for each active VM")

View File

@ -108,14 +108,6 @@ module Vagrant
error_key(:active_machine_with_different_provider)
end
class AnsibleFailed < VagrantError
error_key(:ansible_failed)
end
class AnsiblePlaybookAppNotFound < VagrantError
error_key(:ansible_playbook_app_not_found)
end
class BatchMultiError < VagrantError
error_key(:batch_multi_error)
end
@ -248,6 +240,10 @@ module Vagrant
error_key(:bundler_error)
end
class CantReadMACAddresses < VagrantError
error_key(:cant_read_mac_addresses)
end
class CapabilityHostExplicitNotDetected < VagrantError
error_key(:capability_host_explicit_not_detected)
end
@ -288,6 +284,14 @@ module Vagrant
error_key(:cli_invalid_options)
end
class CloneNotFound < VagrantError
error_key(:clone_not_found)
end
class CloneMachineNotFound < VagrantError
error_key(:clone_machine_not_found)
end
class CommandUnavailable < VagrantError
error_key(:command_unavailable)
end
@ -340,6 +344,10 @@ module Vagrant
error_key(:downloader_interrupted)
end
class EnvInval < VagrantError
error_key(:env_inval)
end
class EnvironmentNonExistentCWD < VagrantError
error_key(:environment_non_existent_cwd)
end
@ -400,8 +408,8 @@ module Vagrant
error_key(:linux_nfs_mount_failed)
end
class LinuxRDesktopNotFound < VagrantError
error_key(:linux_rdesktop_not_found)
class LinuxRDPClientNotFound < VagrantError
error_key(:linux_rdp_client_not_found)
end
class LocalDataDirectoryNotAccessible < VagrantError
@ -512,6 +520,18 @@ module Vagrant
error_key(:requires_directory, "vagrant.actions.general.package")
end
class ProviderCantInstall < VagrantError
error_key(:provider_cant_install)
end
class ProviderChecksumMismatch < VagrantError
error_key(:provider_checksum_mismatch)
end
class ProviderInstallFailed < VagrantError
error_key(:provider_install_failed)
end
class ProviderNotFound < VagrantError
error_key(:provider_not_found)
end
@ -784,6 +804,14 @@ module Vagrant
error_key(:boot_timeout)
end
class VMCloneFailure < VagrantError
error_key(:failure, "vagrant.actions.vm.clone")
end
class VMCreateMasterFailure < VagrantError
error_key(:failure, "vagrant.actions.vm.clone.create_master")
end
class VMCustomizationFailed < VagrantError
error_key(:failure, "vagrant.actions.vm.customize")
end

View File

@ -116,6 +116,9 @@ module Vagrant
# XXX: This is temporary. This will be removed very soon.
if base
@id = name
# For base setups, we don't want to insert the key
@config.ssh.insert_key = false
else
reload
end
@ -141,6 +144,10 @@ module Vagrant
if state.id == MachineState::NOT_CREATED_ID
self.id = nil
end
# Output a bunch of information about this machine in
# machine-readable format in case someone is listening.
@ui.machine("metadata", "provider", provider_name)
end
# This calls an action on the provider. The provider may or may not
@ -188,7 +195,10 @@ module Vagrant
end
# Call the action
action_raw(name, callable, extra_env)
ui.machine("action", name.to_s, "start")
action_result = action_raw(name, callable, extra_env)
ui.machine("action", name.to_s, "end")
action_result
end
rescue Errors::EnvironmentLockedError
raise Errors::MachineActionLockedError,
@ -436,6 +446,7 @@ module Vagrant
# We also set some fields that are purely controlled by Varant
info[:forward_agent] = @config.ssh.forward_agent
info[:forward_x11] = @config.ssh.forward_x11
info[:forward_env] = @config.ssh.forward_env
info[:ssh_command] = @config.ssh.ssh_command if @config.ssh.ssh_command

View File

@ -78,7 +78,7 @@ module Vagrant
@machines.delete(entry.id)
unlocked_save
# Release acccess on this machine
# Release access on this machine
unlocked_release(entry.id)
end
end

View File

@ -133,8 +133,17 @@ module Vagrant
# machine in that environment. We silence warnings here because
# Vagrantfiles often have constants, so people would otherwise
# constantly (heh) get "already initialized constant" warnings.
env = entry.vagrant_env(
@env.home_path, ui_class: @env.ui_class)
begin
env = entry.vagrant_env(
@env.home_path, ui_class: @env.ui_class)
rescue Vagrant::Errors::EnvironmentNonExistentCWD
# This means that this environment working directory
# no longer exists, so delete this entry.
entry = @env.machine_index.get(name.to_s)
@env.machine_index.delete(entry) if entry
raise
end
next env.machine(entry.name.to_sym, entry.provider.to_sym)
end

View File

@ -24,6 +24,21 @@ module Vagrant
true
end
# This is called early, before a machine is instantiated, to check
# if this provider is installed. This should return true or false.
#
# If the provider is not installed and Vagrant determines it is
# able to install this provider, then it will do so. Installation
# is done by calling Environment.install_provider.
#
# If Environment.can_install_provider? returns false, then an error
# will be shown to the user.
def self.installed?
# By default return true for backwards compat so all providers
# continue to work.
true
end
# Initialize the provider to represent the given machine.
#
# @param [Vagrant::Machine] machine The machine that this provider

View File

@ -103,6 +103,12 @@ module Vagrant
raise Errors::UIExpectsTTY
end
[:detail, :warn, :error, :info, :output, :success].each do |method|
define_method(method) do |message, *args, **opts|
machine("ui", method.to_s, message, *args, **opts)
end
end
def machine(type, *data)
opts = {}
opts = data.pop if data.last.kind_of?(Hash)
@ -117,9 +123,12 @@ module Vagrant
data[i].gsub!("\r", "\\r")
end
@lock.synchronize do
safe_puts("#{Time.now.utc.to_i},#{target},#{type},#{data.join(",")}")
end
# Avoid locks in a trap context introduced from Ruby 2.0
Thread.new do
@lock.synchronize do
safe_puts("#{Time.now.utc.to_i},#{target},#{type},#{data.join(",")}")
end
end.join
end
end
@ -150,7 +159,7 @@ module Vagrant
super(message)
# We can't ask questions when the output isn't a TTY.
raise Errors::UIExpectsTTY if !@stdin.tty? && !Vagrant::Util::Platform.cygwin?
raise Errors::UIExpectsTTY if !@stdin.tty? && !Vagrant::Util::Platform.windows?
# Setup the options so that the new line is suppressed
opts ||= {}
@ -271,6 +280,9 @@ module Vagrant
opts[:bold] = #{method.inspect} != :detail && \
#{method.inspect} != :ask
end
if !opts.key?(:target)
opts[:target] = @prefix
end
@ui.#{method}(format_message(#{method.inspect}, message, **opts), *args, **opts)
end
CODE
@ -313,6 +325,7 @@ module Vagrant
target = @prefix
target = opts[:target] if opts.key?(:target)
target = "#{target}:" if target != ""
# Get the lines. The first default is because if the message
# is an empty string, then we want to still use the empty string.
@ -321,7 +334,7 @@ module Vagrant
# Otherwise, make sure to prefix every line properly
lines.map do |line|
"#{prefix}#{target}: #{line}"
"#{prefix}#{target} #{line}"
end.join("\n")
end
end

View File

@ -138,11 +138,14 @@ module Vagrant
# If we already retried, raise it.
raise if retried
@logger.error("Exit code: #{e.extra_data[:code]}")
# If its any error other than 33, it is an error.
raise if e.extra_data[:exit_code].to_i != 33
raise if e.extra_data[:code].to_i != 33
# Exit code 33 means that the server doesn't support ranges.
# In this case, try again without resume.
@logger.error("Error is server doesn't support byte ranges. Retrying from scratch.")
@continue = false
retried = true
retry

View File

@ -1,10 +1,21 @@
require "ipaddr"
module Vagrant
module Util
module NetworkIP
# Returns the network address of the given IP and subnet.
#
# If the IP address is an IPv6 address, subnet should be a prefix
# length such as "64".
#
# @return [String]
def network_address(ip, subnet)
# If this is an IPv6 address, then just mask it
if subnet.to_s =~ /^\d+$/
ip = IPAddr.new(ip)
return ip.mask(subnet.to_i).to_s
end
ip = ip_parts(ip)
netmask = ip_parts(subnet)

View File

@ -10,10 +10,17 @@ module Vagrant
class Platform
class << self
def cygwin?
# Installer detects Cygwin
return true if ENV["VAGRANT_DETECTED_OS"] &&
ENV["VAGRANT_DETECTED_OS"].downcase.include?("cygwin")
platform.include?("cygwin")
# Ruby running in Cygwin
return true if platform.include?("cygwin")
# Heuristic. If the path contains Cygwin, we just assume we're
# in Cygwin. It is generally a safe bet.
path = ENV["PATH"] || ""
return path.include?("cygwin")
end
[:darwin, :bsd, :freebsd, :linux, :solaris].each do |type|
@ -45,10 +52,32 @@ module Vagrant
# detect-if-running-with-administrator-privileges-under-windows-xp
begin
Win32::Registry::HKEY_USERS.open("S-1-5-19") {}
return true
rescue Win32::Registry::Error
return false
end
# If we made it this far then we try a fallback approach
# since the above doesn't seem to be bullet proof. See GH-5616
(`reg query HKU\\S-1-5-19 2>&1` =~ /ERROR/).nil?
end
# Checks if the user running Vagrant on Windows is a member of the
# "Hyper-V Administrators" group.
#
# From: https://support.microsoft.com/en-us/kb/243330
# SID: S-1-5-32-578
# Name: BUILTIN\Hyper-V Administrators
#
# @return [Boolean]
def windows_hyperv_admin?
begin
username = ENV["USERNAME"]
process = Subprocess.execute("net", "localgroup", "Hyper-V Administrators")
output = process.stdout.chomp
return output.include?(username)
rescue Errors::CommandUnavailableWindows
return false
end
end
# This takes any path and converts it from a Windows path to a
@ -114,6 +143,14 @@ module Vagrant
path = Pathname.new(File.expand_path(path))
if path.exist? && !fs_case_sensitive?
# If the path contains a Windows short path, then we attempt to
# expand. The require below is embedded here since it requires
# windows to work.
if windows? && path.to_s =~ /~\d(\/|\\)/
require_relative "windows_path"
path = Pathname.new(WindowsPath.longname(path.to_s))
end
# Build up all the parts of the path
original = []
while !path.root?
@ -148,6 +185,12 @@ module Vagrant
# @param [String] path Path to convert to UNC for Windows
# @return [String]
def windows_unc_path(path)
path = path.gsub("/", "\\")
# If the path is just a drive letter, then return that as-is
return path if path =~ /^[a-zA-Z]:\\?$/
# Convert to UNC path
"\\\\?\\" + path.gsub("/", "\\")
end

View File

@ -0,0 +1,45 @@
module Vagrant
module Util
module Presence
extend self
# Determines if the given object is "present". A String is considered
# present if the stripped contents are not empty. An Array/Hash is
# considered present if they have a length of more than 1. "true" is
# always present and `false` and `nil` are always not present. Any other
# object is considered to be present.
#
# @return [true, false]
def present?(obj)
case obj
when String
!obj.strip.empty?
when Symbol
!obj.to_s.strip.empty?
when Array
!obj.compact.empty?
when Hash
!obj.empty?
when TrueClass, FalseClass
obj
when NilClass
false
when Object
true
end
end
# Returns the presence of the object. If the object is {present?}, it is
# returned. Otherwise `false` is returned.
#
# @return [Object, false]
def presence(obj)
if present?(obj)
obj
else
false
end
end
end
end
end

View File

@ -0,0 +1,14 @@
module Vagrant
module Util
class SafeEnv
# This yields an environment hash to change and catches any issues
# while changing the environment variables and raises a helpful error
# to end users.
def self.change_env
yield ENV
rescue Errno::EINVAL
raise Errors::EnvInval
end
end
end
end

View File

@ -138,6 +138,10 @@ module Vagrant
command_options += ["-o", "ProxyCommand=#{ssh_info[:proxy_command]}"]
end
if ssh_info[:forward_env]
command_options += ["-o", "SendEnv=#{ssh_info[:forward_env].join(" ")}"]
end
# Configurables -- extra_args should always be last due to the way the
# ssh args parser works. e.g. if the user wants to use the -t option,
# any shell command(s) she'd like to run on the remote server would
@ -172,6 +176,14 @@ module Vagrant
LOGGER.info("Executing SSH in subprocess: #{ssh} #{command_options.inspect}")
process = ChildProcess.build(ssh, *command_options)
process.io.inherit!
# Forward configured environment variables.
if ssh_info[:forward_env]
ssh_info[:forward_env].each do |key|
process.environment[key] = ENV[key]
end
end
process.start
process.wait
return process.exit_code

View File

@ -0,0 +1,38 @@
require "fiddle/import"
module Vagrant
module Util
module WindowsPath
module API
extend Fiddle::Importer
dlload 'kernel32.dll'
extern("int GetLongPathNameA(char*, char*, int)", :stdcall)
end
# Converts a Windows shortname to a long name. This only works
# for ASCII paths currently and doesn't use the wide character
# support.
def self.longname(name)
# We loop over the API call in case we didn't allocate enough
# buffer space. In general it is usually enough.
bufferlen = 250
buffer = nil
while true
buffer = ' ' * bufferlen
len = API.GetLongPathNameA(name.to_s, buffer, buffer.size)
if bufferlen < len
# If the length returned is larger than our buffer length,
# it is the API telling us it needs more space. Allocate it
# and retry.
bufferlen = len
continue
end
break
end
return buffer.rstrip.chomp("\0")
end
end
end
end

View File

@ -1,9 +1,13 @@
require 'optparse'
require_relative 'download_mixins'
module VagrantPlugins
module CommandBox
module Command
class Add < Vagrant.plugin("2", :command)
include DownloadMixins
def execute
options = {}
@ -21,22 +25,7 @@ module VagrantPlugins
options[:force] = f
end
o.on("--insecure", "Do not validate SSL certificates") do |i|
options[:insecure] = i
end
o.on("--cacert FILE", String, "CA certificate for SSL download") do |c|
options[:ca_cert] = c
end
o.on("--capath DIR", String, "CA certificate directory for SSL download") do |c|
options[:ca_path] = c
end
o.on("--cert FILE", String,
"A client SSL cert, if needed") do |c|
options[:client_cert] = c
end
build_download_options(o, options)
o.on("--location-trusted", "Trust 'Location' header from HTTP redirects and use the same credentials for subsequent urls as for the initial one") do |l|
options[:location_trusted] = l
@ -97,7 +86,7 @@ module VagrantPlugins
box_force: options[:force],
box_download_ca_cert: options[:ca_cert],
box_download_ca_path: options[:ca_path],
box_download_client_cert: options[:client_cert],
box_client_cert: options[:client_cert],
box_download_insecure: options[:insecure],
box_download_location_trusted: options[:location_trusted],
ui: Vagrant::UI::Prefixed.new(@env.ui, "box"),

View File

@ -0,0 +1,29 @@
module VagrantPlugins
module CommandBox
module DownloadMixins
# This adds common download command line flags to the given
# OptionParser, storing the result in the `options` dictionary.
#
# @param [OptionParser] parser
# @param [Hash] options
def build_download_options(parser, options)
# Add the options
parser.on("--insecure", "Do not validate SSL certificates") do |i|
options[:insecure] = i
end
parser.on("--cacert FILE", String, "CA certificate for SSL download") do |c|
options[:ca_cert] = c
end
parser.on("--capath DIR", String, "CA certificate directory for SSL download") do |c|
options[:ca_path] = c
end
parser.on("--cert FILE", String, "A client SSL cert, if needed") do |c|
options[:client_cert] = c
end
end
end
end
end

View File

@ -1,11 +1,16 @@
require 'optparse'
require_relative 'download_mixins'
module VagrantPlugins
module CommandBox
module Command
class Outdated < Vagrant.plugin("2", :command)
include DownloadMixins
def execute
options = {}
download_options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant box outdated [options]"
@ -20,6 +25,8 @@ module VagrantPlugins
o.on("--global", "Check all boxes installed") do |g|
options[:global] = g
end
build_download_options(o, download_options)
end
argv = parse_options(opts)
@ -27,7 +34,7 @@ module VagrantPlugins
# If we're checking the boxes globally, then do that.
if options[:global]
outdated_global
outdated_global(download_options)
return 0
end
@ -37,11 +44,11 @@ module VagrantPlugins
box_outdated_refresh: true,
box_outdated_success_ui: true,
machine: machine,
})
}.merge(download_options))
end
end
def outdated_global
def outdated_global(download_options)
boxes = {}
@env.boxes.all.reverse.each do |name, version, provider|
next if boxes[name]
@ -58,8 +65,8 @@ module VagrantPlugins
md = nil
begin
md = box.load_metadata
rescue Vagrant::Errors::DownloaderError => e
md = box.load_metadata(download_options)
rescue Vagrant::Errors::BoxMetadataDownloadError => e
@env.ui.error(I18n.t(
"vagrant.box_outdated_metadata_error",
name: box.name,

View File

@ -27,6 +27,10 @@ module VagrantPlugins
"The specific version of the box to remove") do |v|
options[:version] = v
end
o.on("--all", "Remove all available versions of the box") do |a|
options[:all] = a
end
end
# Parse the options
@ -50,6 +54,7 @@ module VagrantPlugins
box_provider: options[:provider],
box_version: options[:version],
force_confirm_box_remove: options[:force],
box_remove_all_versions: options[:all],
})
# Success, exit status 0

View File

@ -1,11 +1,16 @@
require 'optparse'
require_relative 'download_mixins'
module VagrantPlugins
module CommandBox
module Command
class Update < Vagrant.plugin("2", :command)
include DownloadMixins
def execute
options = {}
download_options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant box update [options]"
@ -27,21 +32,23 @@ module VagrantPlugins
o.on("--provider PROVIDER", String, "Update box with specific provider") do |p|
options[:provider] = p.to_sym
end
build_download_options(o, download_options)
end
argv = parse_options(opts)
return if !argv
if options[:box]
update_specific(options[:box], options[:provider])
update_specific(options[:box], options[:provider], download_options)
else
update_vms(argv, options[:provider])
update_vms(argv, options[:provider], download_options)
end
0
end
def update_specific(name, provider)
def update_specific(name, provider, download_options)
boxes = {}
@env.boxes.all.each do |n, v, p|
boxes[n] ||= {}
@ -74,11 +81,11 @@ module VagrantPlugins
to_update.each do |n, p, v|
box = @env.boxes.find(n, p, v)
box_update(box, "> #{v}", @env.ui)
box_update(box, "> #{v}", @env.ui, download_options)
end
end
def update_vms(argv, provider)
def update_vms(argv, provider, download_options)
with_target_vms(argv, provider: provider) do |machine|
if !machine.config.vm.box
machine.ui.output(I18n.t(
@ -95,17 +102,25 @@ module VagrantPlugins
box = machine.box
version = machine.config.vm.box_version
box_update(box, version, machine.ui)
# Get download options from machine configuration if not specified
# on the command line.
download_options[:ca_cert] ||= machine.config.vm.box_download_ca_cert
download_options[:ca_path] ||= machine.config.vm.box_download_ca_path
download_options[:client_cert] ||= machine.config.vm.box_download_client_cert
if download_options[:insecure].nil?
download_options[:insecure] = machine.config.vm.box_download_insecure
end
box_update(box, version, machine.ui, download_options)
end
end
def box_update(box, version, ui)
def box_update(box, version, ui, download_options)
ui.output(I18n.t("vagrant.box_update_checking", name: box.name))
ui.detail("Latest installed version: #{box.version}")
ui.detail("Version constraints: #{version}")
ui.detail("Provider: #{box.provider}")
update = box.has_update?(version)
update = box.has_update?(version, download_options: download_options)
if !update
ui.success(I18n.t(
"vagrant.box_up_to_date_single",
@ -124,6 +139,10 @@ module VagrantPlugins
box_provider: update[2].name,
box_version: update[1].version,
ui: ui,
box_client_cert: download_options[:client_cert],
box_download_ca_cert: download_options[:ca_cert],
box_download_ca_path: download_options[:ca_path],
box_download_insecure: download_options[:insecure]
})
end
end

View File

@ -0,0 +1,75 @@
require 'optparse'
module VagrantPlugins
module CommandCap
class Command < Vagrant.plugin("2", :command)
def self.synopsis
"checks and executes capability"
end
def execute
options = {}
options[:check] = false
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cap [options] TYPE NAME [args]"
o.separator ""
o.separator "This is an advanced command. If you don't know what this"
o.separator "does and you aren't explicitly trying to use it, you probably"
o.separator "don't want to use this."
o.separator ""
o.separator "This command checks or executes arbitrary capabilities that"
o.separator "Vagrant has for hosts, guests, and providers."
o.separator ""
o.separator "Options:"
o.separator ""
o.on("--check", "Only checks for a capability, does not execute") do |f|
options[:check] = f
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.length < 2
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
type = argv.shift.to_sym
name = argv.shift.to_sym
# Get the proper capability host to check
cap_host = nil
if type == :host
cap_host = @env.host
else
with_target_vms([]) do |vm|
cap_host = case type
when :provider
vm.provider
when :guest
vm.guest
else
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
end
end
# If we're just checking, then just return exit codes
if options[:check]
return 0 if cap_host.capability?(name)
return 1
end
# Otherwise, call it
cap_host.capability(name, *argv)
# Success, exit status 0
0
end
end
end
end

View File

@ -0,0 +1,17 @@
require "vagrant"
module VagrantPlugins
module CommandCap
class Plugin < Vagrant.plugin("2")
name "cap command"
description <<-DESC
The `cap` command checks and executes arbitrary capabilities.
DESC
command("cap", primary: false) do
require_relative "command"
Command
end
end
end
end

View File

@ -1,4 +1,5 @@
require "rest_client"
require "vagrant/util/downloader"
module VagrantPlugins
module LoginCommand
@ -45,8 +46,23 @@ module VagrantPlugins
with_error_handling do
url = "#{Vagrant.server_url}/api/v1/authenticate"
request = { "user" => { "login" => user, "password" => pass } }
response = RestClient.post(
url, JSON.dump(request), content_type: :json)
proxy = nil
proxy ||= ENV["HTTPS_PROXY"] || ENV["https_proxy"]
proxy ||= ENV["HTTP_PROXY"] || ENV["http_proxy"]
response = RestClient::Request.execute(
method: :post,
url: url,
payload: JSON.dump(request),
proxy: proxy,
headers: {
accept: :json,
content_type: :json,
user_agent: Vagrant::Util::Downloader::USER_AGENT,
},
)
data = JSON.load(response.to_s)
data["token"]
end

View File

@ -1,3 +1,4 @@
require "cgi"
require "uri"
require_relative "../client"
@ -5,6 +6,9 @@ require_relative "../client"
module VagrantPlugins
module LoginCommand
class AddAuthentication
VCLOUD = "vagrantcloud.com".freeze
ATLAS = "atlas.hashicorp.com".freeze
def initialize(app, env)
@app = app
end
@ -19,19 +23,26 @@ module VagrantPlugins
env[:box_urls].map! do |url|
u = URI.parse(url)
replace = u.host == server_uri.host
if !replace
# We need this in here for the transition we made from
# Vagrant Cloud to Atlas. This preserves access tokens
# appending to both without leaking access tokens to
# unsavory URLs.
replace = u.host == "vagrantcloud.com" &&
server_uri.host == "atlas.hashicorp.com"
if u.host == VCLOUD && server_uri.host == ATLAS
replace = true
end
end
if replace
u.query ||= ""
u.query += "&" if u.query != ""
u.query += "access_token=#{token}"
q = CGI.parse(u.query || "")
current = q["access_token"]
if current && current.empty?
q["access_token"] = token
end
u.query = URI.encode_www_form(q)
end
u.to_s

View File

@ -3,6 +3,11 @@ module VagrantPlugins
module Command
module MixinInstallOpts
def build_install_opts(o, options)
options[:plugin_sources] = [
"https://rubygems.org",
"http://gems.hashicorp.com",
]
o.on("--entry-point NAME", String,
"The name of the entry point file for loading the plugin.") do |entry_point|
options[:entry_point] = entry_point
@ -17,9 +22,13 @@ module VagrantPlugins
puts
end
o.on("--plugin-clean-sources",
"Remove all plugin sources defined so far (including defaults)") do |clean|
options[:plugin_sources] = [] if clean
end
o.on("--plugin-source PLUGIN_SOURCE", String,
"Add a RubyGems repository source") do |plugin_source|
options[:plugin_sources] ||= []
options[:plugin_sources] << plugin_source
end

View File

@ -0,0 +1,90 @@
require "vagrant/util/presence"
require "optparse"
module VagrantPlugins
module CommandPort
class Command < Vagrant.plugin("2", :command)
include Vagrant::Util::Presence
def self.synopsis
"displays information about guest port mappings"
end
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant port [options] [name]"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("--guest PORT", "Output the host port that maps to the given guest port") do |port|
options[:guest] = port
end
o.on("--machine-readable", "Display machine-readable output")
end
# Parse the options
argv = parse_options(opts)
return if !argv
with_target_vms(argv, single_target: true) do |vm|
vm.action_raw(:config_validate,
Vagrant::Action::Builtin::ConfigValidate)
if !vm.provider.capability?(:forwarded_ports)
@env.ui.error(I18n.t("port_command.missing_capability",
provider: vm.provider_name,
))
return 1
end
ports = vm.provider.capability(:forwarded_ports)
if !present?(ports)
@env.ui.info(I18n.t("port_command.empty_ports"))
return 0
end
if present?(options[:guest])
return print_single(vm, ports, options[:guest])
else
return print_all(vm, ports)
end
end
end
private
# Print all the guest <=> host port mappings.
# @return [0] the exit code
def print_all(vm, ports)
@env.ui.info(I18n.t("port_command.details"))
@env.ui.info("")
ports.each do |host, guest|
@env.ui.info("#{guest.to_s.rjust(6)} (guest) => #{host} (host)")
@env.ui.machine("forwarded_port", guest, host, target: vm.name.to_s)
end
return 0
end
# Print the host mapping that matches the given guest target.
# @return [0,1] the exit code
def print_single(vm, ports, target)
map = ports.find { |_, guest| "#{guest}" == "#{target}" }
if !present?(map)
@env.ui.error(I18n.t("port_command.no_matching_port",
port: target,
))
return 1
end
@env.ui.info("#{map[0]}")
return 0
end
end
end
end

View File

@ -0,0 +1,20 @@
en:
port_command:
details: |-
The forwarded ports for the machine are listed below. Please note that
these values may differ from values configured in the Vagrantfile if the
provider supports automatic port collision detection and resolution.
empty_ports: |-
The provider reported there are no forwarded ports for this virtual
machine. This can be caused if there are no ports specified in the
Vagrantfile or if the virtual machine is not currently running. Please
check that the virtual machine is running and try again.
missing_capability: |-
The %{provider} provider does not support listing forwarded ports. This is
most likely a limitation of the provider and not a bug in Vagrant. If you
believe this is a bug in Vagrant, please search existing issues before
opening a new one.
no_matching_port: |-
The guest is not currently mapping port %{port} to the host machine. Is
the port configured in the Vagrantfile? You may need to run `vagrant reload`
if changes were made to the port configuration in the Vagrantfile.

View File

@ -0,0 +1,27 @@
require "vagrant"
module VagrantPlugins
module CommandPort
class Plugin < Vagrant.plugin("2")
name "port command"
description <<-DESC
The `port` command displays guest port mappings.
DESC
command("port") do
require_relative "command"
self.init!
Command
end
protected
def self.init!
return if defined?(@_init)
I18n.load_path << File.expand_path("../locales/en.yml", __FILE__)
I18n.reload!
@_init = true
end
end
end
end

View File

@ -0,0 +1,119 @@
require "optparse"
require "vagrant/util/powershell"
require_relative "../../communicators/winrm/helper"
module VagrantPlugins
module CommandPS
class Command < Vagrant.plugin("2", :command)
def self.synopsis
"connects to machine via powershell remoting"
end
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant powershell [-- extra powershell args]"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-c", "--command COMMAND", "Execute a powershell command directly") do |c|
options[:command] = c
end
end
# Parse out the extra args to send to the ps session, which
# is everything after the "--"
split_index = @argv.index("--")
if split_index
options[:extra_args] = @argv.drop(split_index + 1)
@argv = @argv.take(split_index)
end
# Parse the options and return if we don't have any target.
argv = parse_options(opts)
return if !argv
# Check if the host even supports ps remoting
raise Errors::HostUnsupported if !@env.host.capability?(:ps_client)
# Execute ps session if we can
with_target_vms(argv, single_target: true) do |machine|
if !machine.communicate.ready?
raise Vagrant::Errors::VMNotCreatedError
end
if machine.config.vm.communicator != :winrm
raise VagrantPlugins::CommunicatorWinRM::Errors::WinRMNotReady
end
if !options[:command].nil?
out_code = machine.communicate.execute(options[:command].dup) do |type,data|
machine.ui.detail(data) if type == :stdout
end
if out_code == 0
machine.ui.success("Command: #{options[:command]} executed succesfully with output code #{out_code}.")
end
next
end
ps_info = VagrantPlugins::CommunicatorWinRM::Helper.winrm_info(machine)
ps_info[:username] = machine.config.winrm.username
ps_info[:password] = machine.config.winrm.password
# Extra arguments if we have any
ps_info[:extra_args] = options[:extra_args]
result = ready_ps_remoting_for(machine, ps_info)
machine.ui.detail(
"Creating powershell session to #{ps_info[:host]}:#{ps_info[:port]}")
machine.ui.detail("Username: #{ps_info[:username]}")
begin
@env.host.capability(:ps_client, ps_info)
ensure
if !result["PreviousTrustedHosts"].nil?
reset_ps_remoting_for(machine, ps_info)
end
end
end
end
def ready_ps_remoting_for(machine, ps_info)
machine.ui.output(I18n.t("vagrant_ps.detecting"))
script_path = File.expand_path("../scripts/enable_psremoting.ps1", __FILE__)
args = []
args << "-hostname" << ps_info[:host]
args << "-port" << ps_info[:port].to_s
args << "-username" << ps_info[:username]
args << "-password" << ps_info[:password]
result = Vagrant::Util::PowerShell.execute(script_path, *args)
if result.exit_code != 0
raise Errors::PowerShellError,
script: script_path,
stderr: result.stderr
end
result_output = JSON.parse(result.stdout)
raise Errors::PSRemotingUndetected if !result_output["Success"]
result_output
end
def reset_ps_remoting_for(machine, ps_info)
machine.ui.output(I18n.t("vagrant_ps.reseting"))
script_path = File.expand_path("../scripts/reset_trustedhosts.ps1", __FILE__)
args = []
args << "-hostname" << ps_info[:host]
result = Vagrant::Util::PowerShell.execute(script_path, *args)
if result.exit_code != 0
raise Errors::PowerShellError,
script: script_path,
stderr: result.stderr
end
end
end
end
end

View File

@ -0,0 +1,22 @@
module VagrantPlugins
module CommandPS
module Errors
# A convenient superclass for all our errors.
class PSCommandError < Vagrant::Errors::VagrantError
error_namespace("vagrant_ps.errors")
end
class HostUnsupported < PSCommandError
error_key(:host_unsupported)
end
class PSRemotingUndetected < PSCommandError
error_key(:ps_remoting_undetected)
end
class PowerShellError < PSCommandError
error_key(:powershell_error)
end
end
end
end

View File

@ -0,0 +1,30 @@
require "vagrant"
module VagrantPlugins
module CommandPS
autoload :Errors, File.expand_path("../errors", __FILE__)
class Plugin < Vagrant.plugin("2")
name "powershell command"
description <<-DESC
The powershell command opens a remote PowerShell session to the
machine if it supports powershell remoting.
DESC
command("powershell") do
require_relative "command"
init!
Command
end
protected
def self.init!
return if defined?(@_init)
I18n.load_path << File.expand_path("templates/locales/command_ps.yml", Vagrant.source_root)
I18n.reload!
@_init = true
end
end
end
end

View File

@ -0,0 +1,60 @@
Param(
[string]$hostname,
[string]$port,
[string]$username,
[string]$password
)
# If we are in this script, we know basic winrm is working
# If the user is not using a domain acount and chances are
# they are not, PS Remoting will not work if the guest is not
# listed in the trusted hosts.
$encrypted_password = ConvertTo-SecureString $password -asplaintext -force
$creds = New-Object System.Management.Automation.PSCredential (
"$hostname\\$username", $encrypted_password)
$result = @{
Success = $false
PreviousTrustedHosts = $null
}
try {
invoke-command -computername $hostname `
-Credential $creds `
-Port $port `
-ScriptBlock {} `
-ErrorAction Stop
$result.Success = $true
} catch{}
if(!$result.Success) {
$newHosts = @()
$result.PreviousTrustedHosts=(
Get-Item "wsman:\localhost\client\trustedhosts").Value
$hostArray=$result.PreviousTrustedHosts.Split(",").Trim()
if($hostArray -contains "*") {
$result.PreviousTrustedHosts = $null
}
elseif(!($hostArray -contains $hostname)) {
$strNewHosts = $hostname
if($result.PreviousTrustedHosts.Length -gt 0){
$strNewHosts = $result.PreviousTrustedHosts + "," + $strNewHosts
}
Set-Item -Path "wsman:\localhost\client\trustedhosts" `
-Value $strNewHosts -Force
try {
invoke-command -computername $hostname `
-Credential $creds `
-Port $port `
-ScriptBlock {} `
-ErrorAction Stop
$result.Success = $true
} catch{
Set-Item -Path "wsman:\localhost\client\trustedhosts" `
-Value $result.PreviousTrustedHosts -Force
$result.PreviousTrustedHosts = $null
}
}
}
Write-Output $(ConvertTo-Json $result)

View File

@ -0,0 +1,12 @@
Param(
[string]$hostname
)
$trustedHosts = (
Get-Item "wsman:\localhost\client\trustedhosts").Value.Replace(
$hostname, '')
$trustedHosts = $trustedHosts.Replace(",,","")
if($trustedHosts.EndsWith(",")){
$trustedHosts = $trustedHosts.Substring(0,$trustedHosts.length-1)
}
Set-Item "wsman:\localhost\client\trustedhosts" -Value $trustedHosts -Force

View File

@ -0,0 +1,74 @@
require 'optparse'
module VagrantPlugins
module CommandProvider
class Command < Vagrant.plugin("2", :command)
def self.synopsis
"show provider for this environment"
end
def execute
options = {}
options[:install] = false
options[:usable] = false
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant provider [options] [args]"
o.separator ""
o.separator "This command interacts with the provider for this environment."
o.separator "With no arguments, it'll output the default provider for this"
o.separator "environment."
o.separator ""
o.separator "Options:"
o.separator ""
o.on("--install", "Installs the provider if possible") do |f|
options[:install] = f
end
o.on("--usable", "Checks if the named provider is usable") do |f|
options[:usable] = f
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
# Get the machine
machine = nil
with_target_vms(argv, single_target: true) do |m|
machine = m
end
# Output some machine readable stuff
@env.ui.machine("provider-name", machine.provider_name, target: machine.name.to_s)
# Check if we're just doing a usability check
if options[:usable]
@env.ui.output(machine.provider_name.to_s)
return 0 if machine.provider.class.usable?(false)
return 1
end
# Check if we're requesting installation
if options[:install]
key = "provider_install_#{machine.provider_name}".to_sym
if !@env.host.capability?(key)
raise Vagrant::Errors::ProviderCantInstall,
provider: machine.provider_name.to_s
end
@env.host.capability(key)
return
end
# No subtask, just output the provider name
@env.ui.output(machine.provider_name.to_s)
# Success, exit status 0
0
end
end
end
end

View File

@ -0,0 +1,18 @@
require "vagrant"
module VagrantPlugins
module CommandProvider
class Plugin < Vagrant.plugin("2")
name "provider command"
description <<-DESC
The `provider` command is used to interact with the various providers
that are installed with Vagrant.
DESC
command("provider", primary: false) do
require_relative "command"
Command
end
end
end
end

View File

@ -15,7 +15,7 @@ module VagrantPlugins
o.banner = "Usage: vagrant provision [vm-name] [--provision-with x,y,z]"
o.on("--provision-with x,y,z", Array,
"Enable only certain provisioners, by type.") do |list|
"Enable only certain provisioners, by type or by name.") do |list|
options[:provision_types] = list.map { |type| type.to_sym }
end
end

View File

@ -30,7 +30,7 @@ module VagrantPlugins
return if !argv
# Validate the provisioners
validate_provisioner_flags!(options)
validate_provisioner_flags!(options, argv)
@logger.debug("'reload' each target VM...")
machines = []

View File

@ -0,0 +1,35 @@
require 'optparse'
module VagrantPlugins
module CommandSnapshot
module Command
class Delete < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant snapshot delete [options] [vm-name] <name>"
o.separator ""
o.separator "Delete a snapshot taken previously with snapshot save."
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 2
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
name = argv.pop
with_target_vms(argv) do |vm|
vm.action(:snapshot_delete, snapshot_name: name)
end
# Success, exit status 0
0
end
end
end
end
end

View File

@ -0,0 +1,47 @@
require 'optparse'
module VagrantPlugins
module CommandSnapshot
module Command
class List < Vagrant.plugin("2", :command)
def execute
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant snapshot list [options] [vm-name]"
o.separator ""
o.separator "List all snapshots taken for a machine."
end
# Parse the options
argv = parse_options(opts)
return if !argv
with_target_vms(argv) do |vm|
if !vm.id
vm.ui.info(I18n.t("vagrant.commands.common.vm_not_created"))
next
end
if !vm.provider.capability?(:snapshot_list)
vm.ui.info(I18n.t("vagrant.commands.snapshot.not_supported"))
next
end
snapshots = vm.provider.capability(:snapshot_list)
if snapshots.empty?
vm.ui.output(I18n.t("vagrant.actions.vm.snapshot.list_none"))
vm.ui.detail(I18n.t("vagrant.actions.vm.snapshot.list_none_detail"))
next
end
snapshots.each do |snapshot|
vm.ui.output(snapshot, prefix: false)
end
end
# Success, exit status 0
0
end
end
end
end
end

View File

@ -0,0 +1,28 @@
require 'json'
require 'optparse'
require_relative "push_shared"
module VagrantPlugins
module CommandSnapshot
module Command
class Pop < Vagrant.plugin("2", :command)
include PushShared
def execute
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant snapshot pop [options] [vm-name]"
o.separator ""
o.separator "Restore state that was pushed with `vagrant snapshot push`."
end
# Parse the options
argv = parse_options(opts)
return if !argv
return shared_exec(argv, method(:pop))
end
end
end
end
end

View File

@ -0,0 +1,33 @@
require 'json'
require 'optparse'
require_relative "push_shared"
module VagrantPlugins
module CommandSnapshot
module Command
class Push < Vagrant.plugin("2", :command)
include PushShared
def execute
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant snapshot push [options] [vm-name]"
o.separator ""
o.separator "Take a snapshot of the current state of the machine and 'push'"
o.separator "it onto the stack of states. You can use `vagrant snapshot pop`"
o.separator "to restore back to this state at any time."
o.separator ""
o.separator "If you use `vagrant snapshot save` or restore at any point after"
o.separator "a push, pop will still bring you back to this pushed state."
end
# Parse the options
argv = parse_options(opts)
return if !argv
return shared_exec(argv, method(:push))
end
end
end
end
end

View File

@ -0,0 +1,57 @@
require 'json'
module VagrantPlugins
module CommandSnapshot
module Command
module PushShared
def shared_exec(argv, m)
with_target_vms(argv) do |vm|
if !vm.id
vm.ui.info("Not created. Cannot push snapshot state.")
next
end
vm.env.lock("machine-snapshot-stack") do
m.call(vm)
end
end
# Success, exit with 0
0
end
def push(machine)
snapshot_name = "push_#{Time.now.to_i}_#{rand(10000)}"
# Save the snapshot. This will raise an exception if it fails.
machine.action(:snapshot_save, snapshot_name: snapshot_name)
end
def pop(machine)
# By reverse sorting, we should be able to find the first
# pushed snapshot.
name = nil
snapshots = machine.provider.capability(:snapshot_list)
snapshots.sort.reverse.each do |snapshot|
if snapshot =~ /^push_\d+_\d+$/
name = snapshot
break
end
end
# If no snapshot was found, we never pushed
if !name
machine.ui.info(I18n.t("vagrant.commands.snapshot.no_push_snapshot"))
return
end
# Restore the snapshot and tell the provider to delete it as well.
machine.action(
:snapshot_restore,
snapshot_name: name,
snapshot_delete: true)
end
end
end
end
end

View File

@ -0,0 +1,35 @@
require 'optparse'
module VagrantPlugins
module CommandSnapshot
module Command
class Restore < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant snapshot restore [options] [vm-name] <name>"
o.separator ""
o.separator "Restore a snapshot taken previously with snapshot save."
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 2
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
name = argv.pop
with_target_vms(argv) do |vm|
vm.action(:snapshot_restore, snapshot_name: name)
end
# Success, exit status 0
0
end
end
end
end
end

View File

@ -0,0 +1,89 @@
require 'optparse'
module VagrantPlugins
module CommandSnapshot
module Command
class Root < Vagrant.plugin("2", :command)
def self.synopsis
"manages snapshots: saving, restoring, etc."
end
def initialize(argv, env)
super
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
@subcommands = Vagrant::Registry.new
@subcommands.register(:save) do
require_relative "save"
Save
end
@subcommands.register(:restore) do
require_relative "restore"
Restore
end
@subcommands.register(:delete) do
require_relative "delete"
Delete
end
@subcommands.register(:list) do
require_relative "list"
List
end
@subcommands.register(:push) do
require_relative "push"
Push
end
@subcommands.register(:pop) do
require_relative "pop"
Pop
end
end
def execute
if @main_args.include?("-h") || @main_args.include?("--help")
# Print the help for all the commands.
return help
end
# If we reached this far then we must have a subcommand. If not,
# then we also just print the help and exit.
command_class = @subcommands.get(@sub_command.to_sym) if @sub_command
return help if !command_class || !@sub_command
@logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}")
# Initialize and execute the command class
command_class.new(@sub_args, @env).execute
end
# Prints the help out for this command
def help
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant snapshot <subcommand> [<args>]"
opts.separator ""
opts.separator "Available subcommands:"
# Add the available subcommands as separators in order to print them
# out as well.
keys = []
@subcommands.each { |key, value| keys << key.to_s }
keys.sort.each do |key|
opts.separator " #{key}"
end
opts.separator ""
opts.separator "For help on any individual subcommand run `vagrant snapshot <subcommand> -h`"
end
@env.ui.info(opts.help, prefix: false)
end
end
end
end
end

View File

@ -0,0 +1,40 @@
require 'optparse'
module VagrantPlugins
module CommandSnapshot
module Command
class Save < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant snapshot save [options] [vm-name] <name>"
o.separator ""
o.separator "Take a snapshot of the current state of the machine. The snapshot"
o.separator "can be restored via `vagrant snapshot restore` at any point in the"
o.separator "future to get back to this exact machine state."
o.separator ""
o.separator "Snapshots are useful for experimenting in a machine and being able"
o.separator "to rollback quickly."
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 2
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
name = argv.pop
with_target_vms(argv) do |vm|
vm.action(:snapshot_save, snapshot_name: name)
end
# Success, exit status 0
0
end
end
end
end
end

View File

@ -0,0 +1,15 @@
require "vagrant"
module VagrantPlugins
module CommandSnapshot
class Plugin < Vagrant.plugin("2")
name "snapshot command"
description "The `snapshot` command gives you a way to manage snapshots."
command("snapshot") do
require_relative "command/root"
Command::Root
end
end
end
end

View File

@ -41,12 +41,15 @@ module VagrantPlugins
forward_agent: ssh_info[:forward_agent],
forward_x11: ssh_info[:forward_x11],
proxy_command: ssh_info[:proxy_command],
ssh_command: ssh_info[:ssh_command]
ssh_command: ssh_info[:ssh_command],
forward_env: ssh_info[:forward_env],
}
# Render the template and output directly to STDOUT
template = "commands/ssh_config/config"
safe_puts(Vagrant::Util::TemplateRenderer.render(template, variables))
config = Vagrant::Util::TemplateRenderer.render(template, variables)
machine.ui.machine("ssh-config", config)
safe_puts(config)
safe_puts
end

View File

@ -1,4 +1,5 @@
require 'optparse'
require 'set'
require "vagrant"
@ -16,6 +17,7 @@ module VagrantPlugins
def execute
options = {}
options[:destroy_on_error] = true
options[:install_provider] = true
options[:parallel] = true
options[:provision_ignore_sentinel] = false
@ -41,6 +43,11 @@ module VagrantPlugins
"Back the machine with a specific provider") do |provider|
options[:provider] = provider
end
o.on("--[no-]install-provider",
"If possible, install the provider if it isn't installed") do |p|
options[:install_provider] = p
end
end
# Parse the options
@ -48,29 +55,37 @@ module VagrantPlugins
return if !argv
# Validate the provisioners
validate_provisioner_flags!(options)
validate_provisioner_flags!(options, argv)
# Go over each VM and bring it up
@logger.debug("'Up' each target VM...")
# Build up the batch job of what we'll do
machines = []
@env.batch(options[:parallel]) do |batch|
names = argv
if names.empty?
autostart = false
@env.vagrantfile.machine_names_and_options.each do |n, o|
autostart = true if o.key?(:autostart)
o[:autostart] = true if !o.key?(:autostart)
names << n.to_s if o[:autostart]
end
# If we have an autostart key but no names, it means that
# all machines are autostart: false and we don't start anything.
names = nil if autostart && names.empty?
# Get the names of the machines we want to bring up
names = argv
if names.empty?
autostart = false
@env.vagrantfile.machine_names_and_options.each do |n, o|
autostart = true if o.key?(:autostart)
o[:autostart] = true if !o.key?(:autostart)
names << n.to_s if o[:autostart]
end
if names
# If we have an autostart key but no names, it means that
# all machines are autostart: false and we don't start anything.
names = nil if autostart && names.empty?
end
# Build up the batch job of what we'll do
machines = []
if names
# If we're installing providers, then do that. We don't
# parallelize this step because it is likely the same provider
# anyways.
if options[:install_provider]
install_providers(names, provider: options[:provider])
end
@env.batch(options[:parallel]) do |batch|
with_target_vms(names, provider: options[:provider]) do |machine|
@env.ui.info(I18n.t(
"vagrant.commands.up.upping",
@ -106,6 +121,64 @@ module VagrantPlugins
# Success, exit status 0
0
end
protected
def install_providers(names, provider: nil)
# First create a set of all the providers we need to check for.
# Most likely this will be a set of one.
providers = Set.new
names.each do |name|
# Check if we have this machine in the index
entry = @env.machine_index.get(name.to_s)
# Get the provider for this machine. This logic isn't completely
# straightforward. If we have a forced provider, we always use
# that no matter what. If we have an entry in the index (meaning
# the machine may be created), we use that provider no matter
# what since that will be used by the core. If we have none, then
# we ask the Vagrant env what the default provider would be and use
# that.
#
# Note that this logic is a bit redundant if we have "provider"
# set but I think its probably cleaner to put this logic in one
# place.
p = provider
p = entry.provider.to_sym if !p && entry
p = @env.default_provider(
machine: name.to_sym, check_usable: false) if !p
# Add it to the set
providers.add(p)
end
# Go through and determine if we can install the providers
providers.delete_if do |name|
!@env.can_install_provider?(name)
end
# Install the providers if we have to
providers.each do |name|
# Find the provider. Ignore if we can't find it, this error
# will pop up later in the process.
parts = Vagrant.plugin("2").manager.providers[name]
next if !parts
# If the provider is already installed, then our work here is done
cls = parts[0]
next if cls.installed?
# Some human-friendly output
ui = Vagrant::UI::Prefixed.new(@env.ui, "")
ui.output(I18n.t(
"vagrant.installing_provider",
provider: name.to_s))
ui.detail(I18n.t("vagrant.installing_provider_detail"))
# Install the provider
@env.install_provider(name)
end
end
end
end
end

View File

@ -1,3 +1,5 @@
require "set"
module VagrantPlugins
module CommandUp
module StartMixins
@ -17,7 +19,7 @@ module VagrantPlugins
end
parser.on("--provision-with x,y,z", Array,
"Enable only certain provisioners, by type.") do |list|
"Enable only certain provisioners, by type or by name.") do |list|
options[:provision_types] = list.map { |type| type.to_sym }
options[:provision_enabled] = true
options[:provision_ignore_sentinel] = true
@ -26,13 +28,26 @@ module VagrantPlugins
# This validates the provisioner flags and raises an exception
# if there are invalid ones.
def validate_provisioner_flags!(options)
(options[:provision_types] || []).each do |type|
klass = Vagrant.plugin("2").manager.provisioners[type]
if !klass
raise Vagrant::Errors::ProvisionerFlagInvalid,
name: type.to_s
end
def validate_provisioner_flags!(options, argv)
if options[:provision_types].nil?
return
end
provisioner_names = Set.new
with_target_vms(argv) do |machine|
machine.config.vm.provisioners.map(&:name).each do |name|
provisioner_names.add(name)
end
end
if (provisioner_names & options[:provision_types]).empty?
(options[:provision_types] || []).each do |type|
klass = Vagrant.plugin("2").manager.provisioners[type]
if !klass
raise Vagrant::Errors::ProvisionerFlagInvalid,
name: type.to_s
end
end
end
end
end

View File

@ -333,6 +333,7 @@ module VagrantPlugins
auth_methods: auth_methods,
config: false,
forward_agent: ssh_info[:forward_agent],
send_env: ssh_info[:forward_env],
keys: ssh_info[:private_key_path],
keys_only: true,
paranoid: false,
@ -420,7 +421,7 @@ module VagrantPlugins
rescue Errno::EHOSTDOWN
# This is raised if we get an ICMP DestinationUnknown error.
raise Vagrant::Errors::SSHHostDown
rescue Errno::EHOSTUNREACH
rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH
# This is raised if we can't work out how to route traffic.
raise Vagrant::Errors::SSHNoRoute
rescue Net::SSH::Exception => e
@ -611,6 +612,7 @@ module VagrantPlugins
end
data = pty_stdout[/.*#{PTY_DELIM_START}(.*?)#{PTY_DELIM_END}/m, 1]
data ||= ""
@logger.debug("PTY stdout parsed: #{data}")
yield :stdout, data if block_given?
end

View File

@ -30,7 +30,12 @@ module VagrantPlugins
# Wait for winrm_info to be ready
winrm_info = nil
while true
winrm_info = Helper.winrm_info(@machine)
winrm_info = nil
begin
winrm_info = Helper.winrm_info(@machine)
rescue Errors::WinRMNotReady
@logger.debug("WinRM not ready yet; retrying until boot_timeout is reached.")
end
break if winrm_info
sleep 0.5
end
@ -38,6 +43,7 @@ module VagrantPlugins
# Got it! Let the user know what we're connecting to.
@machine.ui.detail("WinRM address: #{shell.host}:#{shell.port}")
@machine.ui.detail("WinRM username: #{shell.username}")
@machine.ui.detail("WinRM execution_time_limit: #{shell.execution_time_limit}")
@machine.ui.detail("WinRM transport: #{shell.config.transport}")
last_message = nil
@ -131,10 +137,11 @@ module VagrantPlugins
error_key: nil, # use the error_class message key
good_exit: 0,
shell: :powershell,
interactive: false,
}.merge(opts || {})
opts[:good_exit] = Array(opts[:good_exit])
command = wrap_in_scheduled_task(command) if opts[:elevated]
command = wrap_in_scheduled_task(command, opts[:interactive]) if opts[:elevated]
output = shell.send(opts[:shell], command, &block)
execution_output(output, opts)
end
@ -188,9 +195,11 @@ module VagrantPlugins
# in place.
#
# @return The wrapper command to execute
def wrap_in_scheduled_task(command)
def wrap_in_scheduled_task(command, interactive)
path = File.expand_path("../scripts/elevated_shell.ps1", __FILE__)
script = Vagrant::Util::TemplateRenderer.render(path)
script = Vagrant::Util::TemplateRenderer.render(path, options: {
interactive: interactive,
})
guest_script_path = "c:/tmp/vagrant-elevated-shell.ps1"
file = Tempfile.new(["vagrant-elevated-shell", "ps1"])
begin
@ -203,14 +212,16 @@ module VagrantPlugins
file.unlink
end
# convert to double byte unicode string then base64 encode
# just like PowerShell -EncodedCommand expects
# Convert to double byte unicode string then base64 encode
# just like PowerShell -EncodedCommand expects.
# Suppress the progress stream from leaking to stderr.
wrapped_encoded_command = Base64.strict_encode64(
"#{command}; exit $LASTEXITCODE".encode('UTF-16LE', 'UTF-8'))
"$ProgressPreference='SilentlyContinue'; #{command}; exit $LASTEXITCODE".encode('UTF-16LE', 'UTF-8'))
"powershell -executionpolicy bypass -file \"#{guest_script_path}\" " +
"-username \"#{shell.username}\" -password \"#{shell.password}\" " +
"-encoded_command \"#{wrapped_encoded_command}\""
"powershell -executionpolicy bypass -file '#{guest_script_path}' " +
"-username '#{shell.username}' -password '#{shell.password}' " +
"-encoded_command '#{wrapped_encoded_command}' " +
"-execution_time_limit '#{shell.execution_time_limit}'"
end
# Handles the raw WinRM shell result and converts it to a

View File

@ -11,6 +11,7 @@ module VagrantPlugins
attr_accessor :timeout
attr_accessor :transport
attr_accessor :ssl_peer_verification
attr_accessor :execution_time_limit
def initialize
@username = UNSET_VALUE
@ -23,6 +24,7 @@ module VagrantPlugins
@timeout = UNSET_VALUE
@transport = UNSET_VALUE
@ssl_peer_verification = UNSET_VALUE
@execution_time_limit = UNSET_VALUE
end
def finalize!
@ -37,6 +39,7 @@ module VagrantPlugins
@retry_delay = 2 if @retry_delay == UNSET_VALUE
@timeout = 1800 if @timeout == UNSET_VALUE
@ssl_peer_verification = true if @ssl_peer_verification == UNSET_VALUE
@execution_time_limit = "PT2H" if @execution_time_limit == UNSET_VALUE
end
def validate(machine)
@ -49,6 +52,7 @@ module VagrantPlugins
errors << "winrm.max_tries cannot be nil." if @max_tries.nil?
errors << "winrm.retry_delay cannot be nil." if @max_tries.nil?
errors << "winrm.timeout cannot be nil." if @timeout.nil?
errors << "winrm.execution_time_limit cannot be nil." if @execution_time_limit.nil?
unless @ssl_peer_verification == true || @ssl_peer_verification == false
errors << "winrm.ssl_peer_verification must be a boolean."
end

View File

@ -1,5 +1,17 @@
param([String]$username, [String]$password, [String]$encoded_command)
param([String]$username, [String]$password, [String]$encoded_command, [String]$execution_time_limit)
# Try to get the Schedule.Service object. If it fails, we are probably
# on an older version of Windows. On old versions, we can just execute
# directly since priv. escalation isn't a thing.
$schedule = $null
Try {
$schedule = New-Object -ComObject "Schedule.Service"
} Catch [System.Management.Automation.PSArgumentException] {
powershell.exe -EncodedCommand $encoded_command
exit $LASTEXITCODE
}
$ProgressPreference = "SilentlyContinue"
$task_name = "WinRM_Elevated_Shell"
$out_file = "$env:SystemRoot\Temp\WinRM_Elevated_Shell.log"
@ -13,7 +25,7 @@ $task_xml = @'
<Principals>
<Principal id="Author">
<UserId>{username}</UserId>
<LogonType>Password</LogonType>
<LogonType><%= options[:interactive] ? 'InteractiveTokenOrPassword' : 'Password' %></LogonType>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
@ -33,7 +45,7 @@ $task_xml = @'
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT2H</ExecutionTimeLimit>
<ExecutionTimeLimit>{execution_time_limit}</ExecutionTimeLimit>
<Priority>4</Priority>
</Settings>
<Actions Context="Author">
@ -49,13 +61,13 @@ $arguments = "/c powershell.exe -EncodedCommand $encoded_command &gt; $out_file
$task_xml = $task_xml.Replace("{arguments}", $arguments)
$task_xml = $task_xml.Replace("{username}", $username)
$task_xml = $task_xml.Replace("{execution_time_limit}", $execution_time_limit)
$schedule = New-Object -ComObject "Schedule.Service"
$schedule.Connect()
$task = $schedule.NewTask($null)
$task.XmlText = $task_xml
$folder = $schedule.GetFolder("\")
$folder.RegisterTaskDefinition($task_name, $task, 6, $username, $password, 1, $null) | Out-Null
$folder.RegisterTaskDefinition($task_name, $task, 6, $username, $password, <%= options[:interactive] ? 3 : 1 %>, $null) | Out-Null
$registered_task = $folder.GetTask("\$task_name")
$registered_task.Run($null) | Out-Null
@ -71,7 +83,7 @@ function SlurpOutput($out_file, $cur_line) {
if (Test-Path $out_file) {
get-content $out_file | select -skip $cur_line | ForEach {
$cur_line += 1
Write-Host "$_"
Write-Host "$_"
}
}
return $cur_line

View File

@ -9,7 +9,7 @@ Vagrant::Util::SilenceWarnings.silence! do
require "winrm"
end
require "winrm-fs/file_manager"
require "winrm-fs"
module VagrantPlugins
module CommunicatorWinRM
@ -23,6 +23,7 @@ module VagrantPlugins
HTTPClient::KeepAliveDisconnected,
WinRM::WinRMHTTPTransportError,
WinRM::WinRMAuthorizationError,
WinRM::WinRMWSManFault,
Errno::EACCES,
Errno::EADDRINUSE,
Errno::ECONNREFUSED,
@ -37,6 +38,7 @@ module VagrantPlugins
attr_reader :port
attr_reader :username
attr_reader :password
attr_reader :execution_time_limit
attr_reader :config
def initialize(host, port, config)
@ -47,12 +49,15 @@ module VagrantPlugins
@port = port
@username = config.username
@password = config.password
@execution_time_limit = config.execution_time_limit
@config = config
end
def powershell(command, &block)
# ensure an exit code
# Suppress the progress stream from leaking to stderr
command = "$ProgressPreference='SilentlyContinue';\r\n" + command
command << "\r\n"
# Ensure an exit code
command << "if ($?) { exit 0 } else { if($LASTEXITCODE) { exit $LASTEXITCODE } else { exit 1 } }"
execute_shell(command, :powershell, &block)
end

View File

@ -27,7 +27,7 @@ module VagrantPlugins
machine.communicate.upload(temp.path, "/tmp/vagrant_network")
machine.communicate.sudo("mv /tmp/vagrant_network /etc/netctl/#{network[:device]}")
machine.communicate.sudo("ip link set #{network[:device]} down && netctl start #{network[:device]}")
machine.communicate.sudo("ip link set #{network[:device]} down && netctl start #{network[:device]} && netctl enable #{network[:device]}")
end
end
end

View File

@ -6,45 +6,109 @@ module VagrantPlugins
module GuestDarwin
module Cap
class ConfigureNetworks
@@logger = Log4r::Logger.new("vagrant::guest::darwin::configure_networks")
include Vagrant::Util
def self.configure_networks(machine, networks)
# Slightly different than other plugins, using the template to build commands
# rather than templating the files.
if !machine.provider.capability?(:nic_mac_addresses)
raise Vagrant::Errors::CantReadMACAddresses,
provider: machine.provider_name.to_s
end
machine.communicate.sudo("networksetup -detectnewhardware")
machine.communicate.sudo("networksetup -listnetworkserviceorder > /tmp/vagrant.interfaces")
tmpints = File.join(Dir.tmpdir, File.basename("#{machine.id}.interfaces"))
machine.communicate.download("/tmp/vagrant.interfaces",tmpints)
nic_mac_addresses = machine.provider.capability(:nic_mac_addresses)
@@logger.debug("mac addresses: #{nic_mac_addresses.inspect}")
devlist = []
ints = ::IO.read(tmpints)
mac_service_map = create_mac_service_map(machine)
networks.each do |network|
mac_address = nic_mac_addresses[network[:interface]+1]
if mac_address.nil?
@@logger.warn("Could not find mac address for network #{network.inspect}")
next
end
service_name = mac_service_map[mac_address]
if service_name.nil?
@@logger.warn("Could not find network service for mac address #{mac_address}")
next
end
network_type = network[:type].to_sym
if network_type == :static
command = "networksetup -setmanual \"#{service_name}\" #{network[:ip]} #{network[:netmask]}"
elsif network_type == :dhcp
command = "networksetup -setdhcp \"#{service_name}\""
else
raise "#{network_type} network type is not supported, try static or dhcp"
end
machine.communicate.sudo(command)
end
end
# Creates a hash mapping MAC addresses to network service name
# Example: { "00C100A1B2C3" => "Thunderbolt Ethernet" }
def self.create_mac_service_map(machine)
tmp_ints = File.join(Dir.tmpdir, File.basename("#{machine.id}.interfaces"))
tmp_hw = File.join(Dir.tmpdir, File.basename("#{machine.id}.hardware"))
machine.communicate.tap do |comm|
comm.sudo("networksetup -detectnewhardware")
comm.sudo("networksetup -listnetworkserviceorder > /tmp/vagrant.interfaces")
comm.sudo("networksetup -listallhardwareports > /tmp/vagrant.hardware")
comm.download("/tmp/vagrant.interfaces", tmp_ints)
comm.download("/tmp/vagrant.hardware", tmp_hw)
end
interface_map = {}
ints = ::IO.read(tmp_ints)
ints.split(/\n\n/m).each do |i|
if i.match(/Hardware/) and not i.match(/Ethernet/).nil?
devmap = {}
if i.match(/Hardware/) && i.match(/Ethernet/)
# Ethernet, should be 2 lines,
# (3) Thunderbolt Ethernet
# (Hardware Port: Thunderbolt Ethernet, Device: en1)
# multiline, should match "Thunderbolt Ethernet", "en1"
devicearry = i.match(/\([0-9]+\) (.+)\n.*Device: (.+)\)/m)
devmap[:interface] = devicearry[2]
devmap[:service] = devicearry[1]
devlist << devmap
service = devicearry[1]
interface = devicearry[2]
# Should map interface to service { "en1" => "Thunderbolt Ethernet" }
interface_map[interface] = service
end
end
File.delete(tmpints)
File.delete(tmp_ints)
networks.each do |network|
service_name = devlist[network[:interface]][:service]
if network[:type].to_sym == :static
command = "networksetup -setmanual \"#{service_name}\" #{network[:ip]} #{network[:netmask]}"
elsif network[:type].to_sym == :dhcp
command = "networksetup -setdhcp \"#{service_name}\""
mac_service_map = {}
macs = ::IO.read(tmp_hw)
macs.split(/\n\n/m).each do |i|
if i.match(/Hardware/) && i.match(/Ethernet/)
# Ethernet, should be 3 lines,
# Hardware Port: Thunderbolt 1
# Device: en1
# Ethernet Address: a1:b2:c3:d4:e5:f6
# multiline, should match "en1", "00:c1:00:a1:b2:c3"
devicearry = i.match(/Device: (.+)\nEthernet Address: (.+)/m)
interface = devicearry[1]
naked_mac = devicearry[2].gsub(':','').upcase
# Skip hardware ports without MAC (bridges, bluetooth, etc.)
next if naked_mac == "N/A"
if !interface_map[interface]
@@logger.warn("Could not find network service for interface #{interface}")
next
end
# Should map MAC to service, { "00C100A1B2C3" => "Thunderbolt Ethernet" }
mac_service_map[naked_mac] = interface_map[interface]
end
machine.communicate.sudo(command)
end
File.delete(tmp_hw)
mac_service_map
end
end
end

View File

@ -0,0 +1,18 @@
module VagrantPlugins
module GuestDebian
module Cap
class SMB
def self.smb_install(machine)
# Deb/Ubuntu require mount.cifs which doesn't come by default.
machine.communicate.tap do |comm|
if !comm.test("test -f /sbin/mount.cifs")
machine.ui.detail(I18n.t("vagrant.guest_deb_installing_smb"))
comm.sudo("apt-get -y update")
comm.sudo("apt-get -y install cifs-utils")
end
end
end
end
end
end
end

View File

@ -2,7 +2,7 @@ module VagrantPlugins
module GuestDebian
class Guest < Vagrant.plugin("2", :guest)
def detect?(machine)
machine.communicate.test("cat /etc/issue | grep 'Debian' | grep -v '8'")
machine.communicate.test("cat /etc/issue | grep 'Debian'")
end
end
end

View File

@ -30,6 +30,11 @@ module VagrantPlugins
require_relative "cap/rsync"
Cap::RSync
end
guest_capability("debian", "smb_install") do
require_relative "cap/smb"
Cap::SMB
end
end
end
end

View File

@ -1,16 +0,0 @@
module VagrantPlugins
module GuestDebian8
module Cap
class Halt
def self.halt(machine)
begin
machine.communicate.sudo("shutdown -h -H")
rescue IOError
# Do nothing, because it probably means the machine shut down
# and SSH connection was lost.
end
end
end
end
end
end

View File

@ -1,21 +0,0 @@
require "vagrant"
module VagrantPlugins
module GuestDebian8
class Plugin < Vagrant.plugin("2")
name "Debian Jessie guest"
description "Debian Jessie guest support."
guest("debian8", "debian") do
require File.expand_path("../guest", __FILE__)
Guest
end
guest_capability("debian8", "halt") do
require_relative "cap/halt"
Cap::Halt
end
end
end
end

View File

@ -48,7 +48,7 @@ module VagrantPlugins
def update_etc_hosts
ip_address = '([0-9]{1,3}\.){3}[0-9]{1,3}'
search = "^(#{ip_address})\\s+#{Regexp.escape(current_hostname)}(\\s.*)?$"
replace = "\\1 #{fqdn} #{short_hostname}"
replace = "\\1 #{fqdn} #{short_hostname} \\3"
expression = ['s', search, replace, 'g'].join('@')
sudo("sed -ri '#{expression}' /etc/hosts")
@ -72,4 +72,4 @@ module VagrantPlugins
end
end
end
end
end

View File

@ -17,7 +17,10 @@ module VagrantPlugins
virtual = false
interface_names = Array.new
interface_names_by_slot = Array.new
machine.communicate.sudo("/usr/sbin/biosdevname; echo $?") do |_, result|
machine.communicate.sudo("/usr/sbin/biosdevname &>/dev/null; echo $?") do |_, result|
# The above command returns:
# - '4' if /usr/sbin/biosdevname detects it is running in a virtual machine
# - '127' if /usr/sbin/biosdevname doesn't exist
virtual = true if ['4', '127'].include? result.chomp
end
@ -106,13 +109,23 @@ module VagrantPlugins
# SSH never dies.
interfaces.each do |interface|
retryable(on: Vagrant::Errors::VagrantError, tries: 3, sleep: 2) do
machine.communicate.sudo("cat /tmp/vagrant-network-entry_#{interface} >> #{network_scripts_dir}/ifcfg-#{interface}")
machine.communicate.sudo("! which nmcli >/dev/null 2>&1 || nmcli c reload #{interface}")
machine.communicate.sudo("/sbin/ifdown #{interface}", error_check: true)
machine.communicate.sudo("/sbin/ifup #{interface}")
end
machine.communicate.sudo(<<-SCRIPT, error_check: true)
cat /tmp/vagrant-network-entry_#{interface} >> #{network_scripts_dir}/ifcfg-#{interface}
machine.communicate.sudo("rm -f /tmp/vagrant-network-entry_#{interface}")
if command -v nmcli &>/dev/null; then
if command -v systemctl &>/dev/null && systemctl -q is-enabled NetworkManager &>/dev/null; then
nmcli c reload #{interface}
elif command -v service &>/dev/null && service NetworkManager status &>/dev/null; then
nmcli c reload #{interface}
fi
fi
/sbin/ifdown #{interface}
/sbin/ifup #{interface}
rm -f /tmp/vagrant-network-entry_#{interface}
SCRIPT
end
end
end
end

View File

@ -4,7 +4,7 @@ module VagrantPlugins
module GuestFedora
class Guest < Vagrant.plugin("2", :guest)
def detect?(machine)
machine.communicate.test("grep 'Fedora release 1[6789]\\|Fedora release 2[0-9]' /etc/redhat-release")
machine.communicate.test("grep 'Fedora release' /etc/redhat-release")
end
end
end

View File

@ -33,10 +33,11 @@ module VagrantPlugins
end
# Emit an upstart event if we can
if machine.communicate.test("test -x /sbin/initctl && test 'upstart' = $(basename $(sudo readlink /proc/1/exe))")
machine.communicate.sudo(
"/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{expanded_guest_path}")
end
machine.communicate.sudo <<-SCRIPT
if command -v /sbin/init &>/dev/null && /sbin/init --version | grep upstart &>/dev/null; then
/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT='#{expanded_guest_path}'
fi
SCRIPT
end
end
end

View File

@ -25,16 +25,13 @@ module VagrantPlugins
mount_gid_old = "`id -g #{options[:group]}`"
end
smb_password = Shellwords.shellescape(options[:smb_password])
# If a domain is provided in the username, separate it
username, domain = (options[:smb_username] || '').split('@', 2)
smb_password = options[:smb_password]
options[:mount_options] ||= []
options[:mount_options] << "sec=ntlm"
options[:mount_options] << "username=#{username}"
options[:mount_options] << "password=#{smb_password}"
options[:mount_options] << "domain=#{domain}" if domain
options[:mount_options] << "credentials=/etc/smb_creds_#{name}"
# First mount command uses getent to get the group
mount_options = "-o uid=#{mount_uid},gid=#{mount_gid}"
@ -49,6 +46,16 @@ module VagrantPlugins
# Create the guest path if it doesn't exist
machine.communicate.sudo("mkdir -p #{expanded_guest_path}")
# Write the credentials file
machine.communicate.sudo(<<-SCRIPT)
cat <<EOF >/etc/smb_creds_#{name}
username=#{username}
password=#{smb_password}
#{domain ? "domain=#{domain}" : ""}
EOF
chmod 0600 /etc/smb_creds_#{name}
SCRIPT
# Attempt to mount the folder. We retry here a few times because
# it can fail early on.
attempts = 0
@ -86,10 +93,11 @@ module VagrantPlugins
end
# Emit an upstart event if we can
if machine.communicate.test("test -x /sbin/initctl && test 'upstart' = $(basename $(sudo readlink /proc/1/exe))")
machine.communicate.sudo(
"/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{expanded_guest_path}")
end
machine.communicate.sudo <<-SCRIPT
if command -v /sbin/init &>/dev/null && /sbin/init --version | grep upstart &>/dev/null; then
/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT='#{expanded_guest_path}'
fi
SCRIPT
end
end
end

View File

@ -80,10 +80,11 @@ module VagrantPlugins
end
# Emit an upstart event if we can
if machine.communicate.test("test -x /sbin/initctl && test 'upstart' = $(basename $(sudo readlink /proc/1/exe))")
machine.communicate.sudo(
"/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{expanded_guest_path}")
end
machine.communicate.sudo <<-SCRIPT
if command -v /sbin/init &>/dev/null && /sbin/init --version | grep upstart &>/dev/null; then
/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT='#{expanded_guest_path}'
fi
SCRIPT
end
def self.unmount_virtualbox_shared_folder(machine, guestpath, options)

View File

@ -10,8 +10,11 @@ module VagrantPlugins
machine.communicate.tap do |comm|
if comm.test("test -f ~/.ssh/authorized_keys")
comm.execute(
"sed -i '/^.*#{contents}.*$/d' ~/.ssh/authorized_keys")
comm.execute(<<SCRIPT)
sed -e '/^.*#{contents}.*$/d' ~/.ssh/authorized_keys > ~/.ssh/authorized_keys.new
mv ~/.ssh/authorized_keys.new ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
SCRIPT
end
end
end

View File

@ -0,0 +1,11 @@
module VagrantPlugins
module GuestPld
module Cap
class Flavor
def self.flavor(machine)
return :pld
end
end
end
end
end

View File

@ -20,6 +20,11 @@ module VagrantPlugins
require_relative "cap/network_scripts_dir"
Cap::NetworkScriptsDir
end
guest_capability("pld", "flavor") do
require_relative "cap/flavor"
Cap::Flavor
end
end
end
end

View File

@ -3,7 +3,11 @@ module VagrantPlugins
module Cap
class NFSClient
def self.nfs_client_install(machine)
machine.communicate.sudo("yum -y install nfs-utils nfs-utils-lib")
if VagrantPlugins::GuestRedHat::Plugin.dnf?(machine)
machine.communicate.sudo("dnf -y install nfs-utils nfs-utils-lib")
else
machine.communicate.sudo("yum -y install nfs-utils nfs-utils-lib")
end
restart_nfs(machine)
end

View File

@ -4,7 +4,11 @@ module VagrantPlugins
class RSync
def self.rsync_install(machine)
machine.communicate.tap do |comm|
comm.sudo("yum -y install rsync")
if VagrantPlugins::GuestRedHat::Plugin.dnf?(machine)
comm.sudo("dnf -y install rsync")
else
comm.sudo("yum -y install rsync")
end
end
end
end

View File

@ -45,6 +45,10 @@ module VagrantPlugins
require_relative "cap/rsync"
Cap::RSync
end
def self.dnf?(machine)
machine.communicate.test("/usr/bin/which -s dnf")
end
end
end
end

View File

@ -0,0 +1,19 @@
module VagrantPlugins
module GuestSlackware
module Cap
class ChangeHostName
def self.change_host_name(machine, name)
machine.communicate.tap do |comm|
# Only do this if the hostname is not already set
if !comm.test("sudo hostname | grep '#{name}'")
comm.sudo("chmod o+w /etc/hostname")
comm.sudo("echo #{name} > /etc/hostname")
comm.sudo("chmod o-w /etc/hostname")
comm.sudo("hostname -F /etc/hostname")
end
end
end
end
end
end
end

View File

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
require "tempfile"
require "vagrant/util/template_renderer"
module VagrantPlugins
module GuestSlackware
module Cap
class ConfigureNetworks
include Vagrant::Util
def self.configure_networks(machine, networks)
interfaces = Array.new
machine.communicate.sudo("ip -o -0 addr | grep -v LOOPBACK | awk '{print $2}' | sed 's/://'") do |_, result|
interfaces = result.split("\n")
end
networks.each do |network|
network[:device] = interfaces[network[:interface]]
entry = TemplateRenderer.render("guests/slackware/network_#{network[:type]}", options: network)
temp = Tempfile.new("vagrant")
temp.binmode
temp.write(entry)
temp.close
machine.communicate.upload(temp.path, "/tmp/vagrant_network")
machine.communicate.sudo("mv /tmp/vagrant_network /etc/rc.d/rc.inet1.conf")
machine.communicate.sudo("/etc/rc.d/rc.inet1")
end
end
end
end
end
end

View File

@ -1,8 +1,10 @@
require "vagrant"
module VagrantPlugins
module GuestDebian8
module GuestSlackware
class Guest < Vagrant.plugin("2", :guest)
def detect?(machine)
machine.communicate.test("cat /etc/issue | grep 'Debian' | grep '8'")
machine.communicate.test("cat /etc/slackware-version")
end
end
end

View File

@ -0,0 +1,25 @@
require "vagrant"
module VagrantPlugins
module GuestSlackware
class Plugin < Vagrant.plugin("2")
name "Slackware guest"
description "Slackware guest support."
guest("slackware", "linux") do
require File.expand_path("../guest", __FILE__)
Guest
end
guest_capability("slackware", "change_host_name") do
require_relative "cap/change_host_name"
Cap::ChangeHostName
end
guest_capability("slackware", "configure_networks") do
require_relative "cap/configure_networks"
Cap::ConfigureNetworks
end
end
end
end

View File

@ -7,7 +7,7 @@ module VagrantPlugins
end
def update_etc_hostname
return super unless vivid?
return super unless systemd?
sudo("hostnamectl set-hostname '#{short_hostname}'")
end
@ -15,7 +15,7 @@ module VagrantPlugins
if hardy?
# hostname.sh returns 1, so use `true` to get a 0 exitcode
sudo("/etc/init.d/hostname.sh start; true")
elsif vivid?
elsif systemd?
# Service runs via hostnamectl
else
sudo("service hostname start")
@ -26,19 +26,25 @@ module VagrantPlugins
os_version("hardy")
end
def vivid?
os_version("vivid")
end
def renew_dhcp
sudo("ifdown -a; ifup -a; ifup -a --allow=hotplug")
end
private
def init_package
machine.communicate.execute('cat /proc/1/comm') do |type, data|
return data.chomp if type == :stdout
end
end
def os_version(name)
machine.communicate.test("[ `lsb_release -c -s` = #{name} ]")
end
def systemd?
init_package == 'systemd'
end
end
end
end

Some files were not shown because too many files have changed in this diff Show More