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 # OS-specific
.DS_Store .DS_Store
# Editor swapfiles
.*.sw?
*~
# Vagrant stuff # Vagrant stuff
acceptance_config.yml acceptance_config.yml
boxes/* boxes/*

View File

@ -1,13 +1,187 @@
## Next Release ## Next Version (unreleased)
FEATURES:
IMPROVEMENTS:
BUG FIXES: 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` - provisioners/ansible: use quotes for the `ansible_ssh_private_key_file`
value in the generated inventory [GH-6209] 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) ## 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 - communicators/ssh: use the same SSH args for `vagrant ssh` with and without
a command [GH-4986, GH-5928] a command [GH-4986, GH-5928]
- guests/fedora: networks can be configured without nmcli [GH-5931] - 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/redhat: systemd detection should happen on guest [GH-5948]
- guests/ubuntu: setting hostname fixed in 12.04 [GH-5937] - guests/ubuntu: setting hostname fixed in 12.04 [GH-5937]
- hosts/linux: NFS can be configured without `$TMP` set on the host [GH-5954] - 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 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 can be seen by checking the tagged releases and reading git commit
messages. messages.

View File

@ -1,6 +1,6 @@
The MIT License 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 Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

View File

@ -1,6 +1,6 @@
# Vagrant # 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) * Source: [https://github.com/mitchellh/vagrant](https://github.com/mitchellh/vagrant)
* IRC: `#vagrant` on Freenode * IRC: `#vagrant` on Freenode
* Mailing list: [Google Groups](http://groups.google.com/group/vagrant-up) * Mailing list: [Google Groups](http://groups.google.com/group/vagrant-up)
@ -19,12 +19,12 @@ between Windows, Mac OS X, and Linux.
## Quick Start ## Quick Start
For the quick-start, we'll bring up a development machine on 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 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 First, make sure your development machine has
[VirtualBox](http://www.virtualbox.org) [VirtualBox](https://www.virtualbox.org/)
installed. After this, installed. After this,
[download and install the appropriate Vagrant package for your OS](http://www.vagrantup.com/downloads). [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 ## 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 You can run the test suite with:
be installed with a simple `gem install bundler`. Afterwards, do the following:
bundle install bundle exec rake
rake
This will run the unit test suite, which should come back all green! Then you're good to go! 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`. * `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 * `st` - Contains a `.sublime-settings` file for enabling Ruby syntax highlighting
for `Vagrantfile`s in Sublime Text. 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 * `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_CHECK = /usr/bin/systemctl status nfs-server.service
Cmnd_Alias VAGRANT_NFSD_START = /usr/bin/systemctl start 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_NFSD_APPLY = /usr/sbin/exportfs -ar
Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /etc/exports Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /*/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_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_CHECK = /etc/init.d/nfs-kernel-server status
Cmnd_Alias VAGRANT_NFSD_START = /etc/init.d/nfs-kernel-server start Cmnd_Alias VAGRANT_NFSD_START = /etc/init.d/nfs-kernel-server start
Cmnd_Alias VAGRANT_NFSD_APPLY = /usr/sbin/exportfs -ar Cmnd_Alias VAGRANT_NFSD_APPLY = /usr/sbin/exportfs -ar
Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /etc/exports Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /*/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_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 :HandleBox, "vagrant/action/builtin/handle_box"
autoload :HandleBoxUrl, "vagrant/action/builtin/handle_box_url" autoload :HandleBoxUrl, "vagrant/action/builtin/handle_box_url"
autoload :HandleForwardedPortCollisions, "vagrant/action/builtin/handle_forwarded_port_collisions" 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 :IsState, "vagrant/action/builtin/is_state"
autoload :Lock, "vagrant/action/builtin/lock" autoload :Lock, "vagrant/action/builtin/lock"
autoload :Message, "vagrant/action/builtin/message" autoload :Message, "vagrant/action/builtin/message"
autoload :PrepareClone, "vagrant/action/builtin/prepare_clone"
autoload :Provision, "vagrant/action/builtin/provision" autoload :Provision, "vagrant/action/builtin/provision"
autoload :ProvisionerCleanup, "vagrant/action/builtin/provisioner_cleanup" autoload :ProvisionerCleanup, "vagrant/action/builtin/provisioner_cleanup"
autoload :SetHostname, "vagrant/action/builtin/set_hostname" 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.") @logger.info("URL is a file or protocol not found and assuming file.")
file_path = File.expand_path(url) file_path = File.expand_path(url)
file_path = Util::Platform.cygwin_windows_path(file_path) 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 end
# If the temporary path exists, verify it is not too old. If its # If the temporary path exists, verify it is not too old. If its
@ -491,6 +493,12 @@ module Vagrant
end end
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 output = d.head
match = output.scan(/^Content-Type: (.+?)$/i).last match = output.scan(/^Content-Type: (.+?)$/i).last
return false if !match return false if !match
@ -507,14 +515,14 @@ module Vagrant
Digest::SHA2 Digest::SHA2
else else
raise Errors::BoxChecksumInvalidType, raise Errors::BoxChecksumInvalidType,
type: env[:box_checksum_type].to_s type: checksum_type.to_s
end end
@logger.info("Validating checksum with #{checksum_klass}") @logger.info("Validating checksum with #{checksum_klass}")
@logger.info("Expected checksum: #{checksum}") @logger.info("Expected checksum: #{checksum}")
actual = FileChecksum.new(path, checksum_klass).checksum actual = FileChecksum.new(path, checksum_klass).checksum
if actual != checksum if actual.casecmp(checksum) != 0
raise Errors::BoxChecksumMismatch, raise Errors::BoxChecksumMismatch,
actual: actual, actual: actual,
expected: checksum expected: checksum

View File

@ -37,13 +37,23 @@ module Vagrant
end end
constraints = machine.config.vm.box_version 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( env[:ui].output(I18n.t(
"vagrant.box_outdated_checking_with_refresh", "vagrant.box_outdated_checking_with_refresh",
name: box.name)) name: box.name))
update = nil update = nil
begin begin
update = box.has_update?(constraints) update = box.has_update?(constraints, download_options: download_options)
rescue Errors::BoxMetadataDownloadError => e rescue Errors::BoxMetadataDownloadError => e
env[:ui].warn(I18n.t( env[:ui].warn(I18n.t(
"vagrant.box_outdated_metadata_download_error", "vagrant.box_outdated_metadata_download_error",

View File

@ -15,6 +15,7 @@ module Vagrant
box_provider = env[:box_provider] box_provider = env[:box_provider]
box_provider = box_provider.to_sym if box_provider box_provider = box_provider.to_sym if box_provider
box_version = env[:box_version] box_version = env[:box_version]
box_remove_all_versions = env[:box_remove_all_versions]
boxes = {} boxes = {}
env[:box_collection].all.each do |n, v, p| env[:box_collection].all.each do |n, v, p|
@ -53,7 +54,7 @@ module Vagrant
if all_versions.length == 1 if all_versions.length == 1
# There is only one version, just use that. # There is only one version, just use that.
box_version = all_versions.first box_version = all_versions.first
else elsif not box_remove_all_versions
# There are multiple versions, we can't choose. # There are multiple versions, we can't choose.
raise Errors::BoxRemoveMultiVersion, raise Errors::BoxRemoveMultiVersion,
name: box_name, name: box_name,
@ -68,6 +69,10 @@ module Vagrant
versions: all_versions.sort.map { |k| " * #{k}" }.join("\n") versions: all_versions.sort.map { |k| " * #{k}" }.join("\n")
end end
versions_to_remove = [box_version]
versions_to_remove = all_versions if box_remove_all_versions
versions_to_remove.sort.each do |version_to_remove|
box = env[:box_collection].find( box = env[:box_collection].find(
box_name, box_provider, box_version) box_name, box_provider, box_version)
@ -96,8 +101,8 @@ module Vagrant
result = env[:action_runner].run(stack, env) result = env[:action_runner].run(stack, env)
if !result[:result] if !result[:result]
# They said "no", so just return # They said "no", so continue with the next box
return @app.call(env) next
end end
end end
@ -106,9 +111,11 @@ module Vagrant
provider: box.provider, provider: box.provider,
version: box.version)) version: box.version))
box.destroy! box.destroy!
env[:box_collection].clean(box.name)
# Passes on the removed box to the rest of the middleware chain # Passes on the removed box to the rest of the middleware chain
env[:box_removed] = box env[:box_removed] = box
end
@app.call(env) @app.call(env)
end end

View File

@ -128,7 +128,7 @@ module Vagrant
port_checker[repaired_port] || port_checker[repaired_port] ||
lease_check(repaired_port) lease_check(repaired_port)
if in_use 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 next
end end
@ -156,8 +156,6 @@ module Vagrant
new_port: repaired_port.to_s)) new_port: repaired_port.to_s))
end end
end end
@app.call(env)
end end
def lease_check(port) 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] if opts[:merge]
existing = cached_synced_folders(machine) existing = cached_synced_folders(machine)
if existing 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| folders.each do |impl, fs|
existing[impl] ||= {} existing[impl] ||= {}
fs.each do |id, data| fs.each do |id, data|
@ -101,7 +111,12 @@ module Vagrant
return cached_synced_folders(machine) if opts[:cached] return cached_synced_folders(machine) if opts[:cached]
config = opts[:config] config = opts[:config]
config ||= machine.config.vm root = false
if !config
config = machine.config.vm
root = true
end
config_folders = config.synced_folders config_folders = config.synced_folders
folders = {} folders = {}
@ -131,9 +146,17 @@ module Vagrant
end end
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. # Keep track of this shared folder by the implementation.
folders[impl] ||= {} folders[impl] ||= {}
folders[impl][id] = data.dup folders[impl][id] = data
end end
# If we have folders with the "default" key, then determine the # 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) def initialize(app, env, place=nil)
@app = app @app = app
@logger = Log4r::Logger.new("vagrant::action::builtin::provision_cleanup") @logger = Log4r::Logger.new("vagrant::action::builtin::provision_cleanup")
@place ||= :after
@place = @place.to_sym place ||= :after
@place = place.to_sym
end end
def call(env) def call(env)
@ -31,10 +32,20 @@ module Vagrant
# Ask the provisioners to modify the configuration if needed # Ask the provisioners to modify the configuration if needed
provisioner_instances(env).each do |p, _| provisioner_instances(env).each do |p, _|
env[:ui].info(I18n.t( name = type_map[p].to_s
"vagrant.provisioner_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 p.cleanup
else
@logger.debug("Skipping cleanup tasks for `#{name}' - not defined")
end
end end
end end
end end

View File

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

View File

@ -113,6 +113,21 @@ module Vagrant
# If we don't have a generated private key, we do nothing # If we don't have a generated private key, we do nothing
path = @env[:machine].data_dir.join("private_key") 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? return if !path.file?
# Copy it into our box directory # Copy it into our box directory

View File

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

View File

@ -12,7 +12,9 @@ module Vagrant
# for accessing/finding individual boxes, adding new boxes, or deleting # for accessing/finding individual boxes, adding new boxes, or deleting
# boxes. # boxes.
class BoxCollection 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. # The directory where the boxes in this collection are stored.
# #
@ -346,6 +348,14 @@ module Vagrant
end end
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 protected
# Returns the directory name for the box of the given name. # Returns the directory name for the box of the given name.
@ -354,16 +364,16 @@ module Vagrant
# @return [String] # @return [String]
def dir_name(name) def dir_name(name)
name = name.dup name = name.dup
name.gsub!(":", "-VAGRANTCOLON-") if Util::Platform.windows? name.gsub!(":", VAGRANT_COLON) if Util::Platform.windows?
name.gsub!("/", "-VAGRANTSLASH-") name.gsub!("/", VAGRANT_SLASH)
name name
end end
# Returns the directory name for the box cleaned up # Returns the directory name for the box cleaned up
def undir_name(name) def undir_name(name)
name = name.dup name = name.dup
name.gsub!("-VAGRANTCOLON-", ":") name.gsub!(VAGRANT_COLON, ":")
name.gsub!("-VAGRANTSLASH-", "/") name.gsub!(VAGRANT_SLASH, "/")
name name
end end
@ -440,5 +450,10 @@ module Vagrant
ensure ensure
dir.rmtree if dir.exist? dir.rmtree if dir.exist?
end 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
end end

View File

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

View File

@ -221,7 +221,12 @@ module Vagrant
line = "(unknown)" line = "(unknown)"
if e.backtrace && e.backtrace[0] 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 end
# Report the generic exception # Report the generic exception

View File

@ -9,6 +9,7 @@ require 'log4r'
require 'vagrant/util/file_mode' require 'vagrant/util/file_mode'
require 'vagrant/util/platform' require 'vagrant/util/platform'
require "vagrant/util/silence_warnings"
require "vagrant/vagrantfile" require "vagrant/vagrantfile"
require "vagrant/version" require "vagrant/version"
@ -158,7 +159,7 @@ module Vagrant
# Setup the local data directory. If a configuration path is given, # Setup the local data directory. If a configuration path is given,
# then it is expanded relative to the working directory. Otherwise, # then it is expanded relative to the working directory. Otherwise,
# we use the default which is expanded relative to the root path. # 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? opts[:local_data_path] ||= root_path.join(DEFAULT_LOCAL_DATA) if !root_path.nil?
if opts[:local_data_path] if opts[:local_data_path]
@local_data_path = Pathname.new(File.expand_path(opts[:local_data_path], @cwd)) @local_data_path = Pathname.new(File.expand_path(opts[:local_data_path], @cwd))
@ -308,6 +309,7 @@ module Vagrant
def default_provider(**opts) def default_provider(**opts)
opts[:exclude] = Set.new(opts[:exclude]) if opts[:exclude] opts[:exclude] = Set.new(opts[:exclude]) if opts[:exclude]
opts[:force_default] = true if !opts.key?(:force_default) opts[:force_default] = true if !opts.key?(:force_default)
opts[:check_usable] = true if !opts.key?(:check_usable)
default = ENV["VAGRANT_DEFAULT_PROVIDER"] default = ENV["VAGRANT_DEFAULT_PROVIDER"]
default = nil if default == "" default = nil if default == ""
@ -375,6 +377,7 @@ module Vagrant
# Find the matching implementation # Find the matching implementation
ordered.each do |_, key, impl, _| ordered.each do |_, key, impl, _|
return key if !opts[:check_usable]
return key if impl.usable?(false) return key if impl.usable?(false)
end end
@ -382,6 +385,26 @@ module Vagrant
raise Errors::NoDefaultProvider raise Errors::NoDefaultProvider
end 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. # Returns the collection of boxes for the environment.
# #
# @return [BoxCollection] # @return [BoxCollection]
@ -413,6 +436,28 @@ module Vagrant
@config_loader @config_loader
end 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 # 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. # against the given name will be run in the context of this environment.
# #
@ -526,7 +571,13 @@ module Vagrant
if name != "dotlock" if name != "dotlock"
lock("dotlock", retry: true) do lock("dotlock", retry: true) do
f.close f.close
begin
File.delete(lock_path) 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
end end
@ -781,7 +832,7 @@ module Vagrant
# This creates the local data directory and show an error if it # This creates the local data directory and show an error if it
# couldn't properly be created. # couldn't properly be created.
def setup_local_data_path def setup_local_data_path(force=false)
if @local_data_path.nil? if @local_data_path.nil?
@logger.warn("No local data path is set. Local data cannot be stored.") @logger.warn("No local data path is set. Local data cannot be stored.")
return return
@ -796,6 +847,9 @@ module Vagrant
upgrade_v1_dotfile(@local_data_path) upgrade_v1_dotfile(@local_data_path)
end end
# If we don't have a root path, we don't setup anything
return if !force && root_path.nil?
begin begin
@logger.debug("Creating: #{@local_data_path}") @logger.debug("Creating: #{@local_data_path}")
FileUtils.mkdir_p(@local_data_path) FileUtils.mkdir_p(@local_data_path)
@ -854,6 +908,12 @@ module Vagrant
nil nil
end 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 # This upgrades a home directory that was in the v1.1 format to the
# v1.5 format. It will raise exceptions if anything fails. # v1.5 format. It will raise exceptions if anything fails.
def upgrade_home_path_v1_1 def upgrade_home_path_v1_1
@ -908,7 +968,7 @@ module Vagrant
# Now, we create the actual local data directory. This should succeed # Now, we create the actual local data directory. This should succeed
# this time since we renamed the old conflicting V1. # this time since we renamed the old conflicting V1.
setup_local_data_path setup_local_data_path(true)
if json_data["active"] if json_data["active"]
@logger.debug("Upgrading to V2 style for each active VM") @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) error_key(:active_machine_with_different_provider)
end end
class AnsibleFailed < VagrantError
error_key(:ansible_failed)
end
class AnsiblePlaybookAppNotFound < VagrantError
error_key(:ansible_playbook_app_not_found)
end
class BatchMultiError < VagrantError class BatchMultiError < VagrantError
error_key(:batch_multi_error) error_key(:batch_multi_error)
end end
@ -248,6 +240,10 @@ module Vagrant
error_key(:bundler_error) error_key(:bundler_error)
end end
class CantReadMACAddresses < VagrantError
error_key(:cant_read_mac_addresses)
end
class CapabilityHostExplicitNotDetected < VagrantError class CapabilityHostExplicitNotDetected < VagrantError
error_key(:capability_host_explicit_not_detected) error_key(:capability_host_explicit_not_detected)
end end
@ -288,6 +284,14 @@ module Vagrant
error_key(:cli_invalid_options) error_key(:cli_invalid_options)
end end
class CloneNotFound < VagrantError
error_key(:clone_not_found)
end
class CloneMachineNotFound < VagrantError
error_key(:clone_machine_not_found)
end
class CommandUnavailable < VagrantError class CommandUnavailable < VagrantError
error_key(:command_unavailable) error_key(:command_unavailable)
end end
@ -340,6 +344,10 @@ module Vagrant
error_key(:downloader_interrupted) error_key(:downloader_interrupted)
end end
class EnvInval < VagrantError
error_key(:env_inval)
end
class EnvironmentNonExistentCWD < VagrantError class EnvironmentNonExistentCWD < VagrantError
error_key(:environment_non_existent_cwd) error_key(:environment_non_existent_cwd)
end end
@ -400,8 +408,8 @@ module Vagrant
error_key(:linux_nfs_mount_failed) error_key(:linux_nfs_mount_failed)
end end
class LinuxRDesktopNotFound < VagrantError class LinuxRDPClientNotFound < VagrantError
error_key(:linux_rdesktop_not_found) error_key(:linux_rdp_client_not_found)
end end
class LocalDataDirectoryNotAccessible < VagrantError class LocalDataDirectoryNotAccessible < VagrantError
@ -512,6 +520,18 @@ module Vagrant
error_key(:requires_directory, "vagrant.actions.general.package") error_key(:requires_directory, "vagrant.actions.general.package")
end 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 class ProviderNotFound < VagrantError
error_key(:provider_not_found) error_key(:provider_not_found)
end end
@ -784,6 +804,14 @@ module Vagrant
error_key(:boot_timeout) error_key(:boot_timeout)
end 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 class VMCustomizationFailed < VagrantError
error_key(:failure, "vagrant.actions.vm.customize") error_key(:failure, "vagrant.actions.vm.customize")
end end

View File

@ -116,6 +116,9 @@ module Vagrant
# XXX: This is temporary. This will be removed very soon. # XXX: This is temporary. This will be removed very soon.
if base if base
@id = name @id = name
# For base setups, we don't want to insert the key
@config.ssh.insert_key = false
else else
reload reload
end end
@ -141,6 +144,10 @@ module Vagrant
if state.id == MachineState::NOT_CREATED_ID if state.id == MachineState::NOT_CREATED_ID
self.id = nil self.id = nil
end 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 end
# This calls an action on the provider. The provider may or may not # This calls an action on the provider. The provider may or may not
@ -188,7 +195,10 @@ module Vagrant
end end
# Call the action # 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 end
rescue Errors::EnvironmentLockedError rescue Errors::EnvironmentLockedError
raise Errors::MachineActionLockedError, raise Errors::MachineActionLockedError,
@ -436,6 +446,7 @@ module Vagrant
# We also set some fields that are purely controlled by Varant # We also set some fields that are purely controlled by Varant
info[:forward_agent] = @config.ssh.forward_agent info[:forward_agent] = @config.ssh.forward_agent
info[:forward_x11] = @config.ssh.forward_x11 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 info[:ssh_command] = @config.ssh.ssh_command if @config.ssh.ssh_command

View File

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

View File

@ -133,8 +133,17 @@ module Vagrant
# machine in that environment. We silence warnings here because # machine in that environment. We silence warnings here because
# Vagrantfiles often have constants, so people would otherwise # Vagrantfiles often have constants, so people would otherwise
# constantly (heh) get "already initialized constant" warnings. # constantly (heh) get "already initialized constant" warnings.
begin
env = entry.vagrant_env( env = entry.vagrant_env(
@env.home_path, ui_class: @env.ui_class) @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) next env.machine(entry.name.to_sym, entry.provider.to_sym)
end end

View File

@ -24,6 +24,21 @@ module Vagrant
true true
end 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. # Initialize the provider to represent the given machine.
# #
# @param [Vagrant::Machine] machine The machine that this provider # @param [Vagrant::Machine] machine The machine that this provider

View File

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

View File

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

View File

@ -1,10 +1,21 @@
require "ipaddr"
module Vagrant module Vagrant
module Util module Util
module NetworkIP module NetworkIP
# Returns the network address of the given IP and subnet. # 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] # @return [String]
def network_address(ip, subnet) 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) ip = ip_parts(ip)
netmask = ip_parts(subnet) netmask = ip_parts(subnet)

View File

@ -10,10 +10,17 @@ module Vagrant
class Platform class Platform
class << self class << self
def cygwin? def cygwin?
# Installer detects Cygwin
return true if ENV["VAGRANT_DETECTED_OS"] && return true if ENV["VAGRANT_DETECTED_OS"] &&
ENV["VAGRANT_DETECTED_OS"].downcase.include?("cygwin") 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 end
[:darwin, :bsd, :freebsd, :linux, :solaris].each do |type| [:darwin, :bsd, :freebsd, :linux, :solaris].each do |type|
@ -45,10 +52,32 @@ module Vagrant
# detect-if-running-with-administrator-privileges-under-windows-xp # detect-if-running-with-administrator-privileges-under-windows-xp
begin begin
Win32::Registry::HKEY_USERS.open("S-1-5-19") {} Win32::Registry::HKEY_USERS.open("S-1-5-19") {}
return true
rescue Win32::Registry::Error rescue Win32::Registry::Error
return false return false
end 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 end
# This takes any path and converts it from a Windows path to a # 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)) path = Pathname.new(File.expand_path(path))
if path.exist? && !fs_case_sensitive? 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 # Build up all the parts of the path
original = [] original = []
while !path.root? while !path.root?
@ -148,6 +185,12 @@ module Vagrant
# @param [String] path Path to convert to UNC for Windows # @param [String] path Path to convert to UNC for Windows
# @return [String] # @return [String]
def windows_unc_path(path) 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("/", "\\") "\\\\?\\" + path.gsub("/", "\\")
end 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]}"] command_options += ["-o", "ProxyCommand=#{ssh_info[:proxy_command]}"]
end 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 # 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, # 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 # 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}") LOGGER.info("Executing SSH in subprocess: #{ssh} #{command_options.inspect}")
process = ChildProcess.build(ssh, *command_options) process = ChildProcess.build(ssh, *command_options)
process.io.inherit! 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.start
process.wait process.wait
return process.exit_code 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 'optparse'
require_relative 'download_mixins'
module VagrantPlugins module VagrantPlugins
module CommandBox module CommandBox
module Command module Command
class Add < Vagrant.plugin("2", :command) class Add < Vagrant.plugin("2", :command)
include DownloadMixins
def execute def execute
options = {} options = {}
@ -21,22 +25,7 @@ module VagrantPlugins
options[:force] = f options[:force] = f
end end
o.on("--insecure", "Do not validate SSL certificates") do |i| build_download_options(o, options)
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
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| 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 options[:location_trusted] = l
@ -97,7 +86,7 @@ module VagrantPlugins
box_force: options[:force], box_force: options[:force],
box_download_ca_cert: options[:ca_cert], box_download_ca_cert: options[:ca_cert],
box_download_ca_path: options[:ca_path], 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_insecure: options[:insecure],
box_download_location_trusted: options[:location_trusted], box_download_location_trusted: options[:location_trusted],
ui: Vagrant::UI::Prefixed.new(@env.ui, "box"), 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 'optparse'
require_relative 'download_mixins'
module VagrantPlugins module VagrantPlugins
module CommandBox module CommandBox
module Command module Command
class Outdated < Vagrant.plugin("2", :command) class Outdated < Vagrant.plugin("2", :command)
include DownloadMixins
def execute def execute
options = {} options = {}
download_options = {}
opts = OptionParser.new do |o| opts = OptionParser.new do |o|
o.banner = "Usage: vagrant box outdated [options]" o.banner = "Usage: vagrant box outdated [options]"
@ -20,6 +25,8 @@ module VagrantPlugins
o.on("--global", "Check all boxes installed") do |g| o.on("--global", "Check all boxes installed") do |g|
options[:global] = g options[:global] = g
end end
build_download_options(o, download_options)
end end
argv = parse_options(opts) argv = parse_options(opts)
@ -27,7 +34,7 @@ module VagrantPlugins
# If we're checking the boxes globally, then do that. # If we're checking the boxes globally, then do that.
if options[:global] if options[:global]
outdated_global outdated_global(download_options)
return 0 return 0
end end
@ -37,11 +44,11 @@ module VagrantPlugins
box_outdated_refresh: true, box_outdated_refresh: true,
box_outdated_success_ui: true, box_outdated_success_ui: true,
machine: machine, machine: machine,
}) }.merge(download_options))
end end
end end
def outdated_global def outdated_global(download_options)
boxes = {} boxes = {}
@env.boxes.all.reverse.each do |name, version, provider| @env.boxes.all.reverse.each do |name, version, provider|
next if boxes[name] next if boxes[name]
@ -58,8 +65,8 @@ module VagrantPlugins
md = nil md = nil
begin begin
md = box.load_metadata md = box.load_metadata(download_options)
rescue Vagrant::Errors::DownloaderError => e rescue Vagrant::Errors::BoxMetadataDownloadError => e
@env.ui.error(I18n.t( @env.ui.error(I18n.t(
"vagrant.box_outdated_metadata_error", "vagrant.box_outdated_metadata_error",
name: box.name, name: box.name,

View File

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

View File

@ -1,11 +1,16 @@
require 'optparse' require 'optparse'
require_relative 'download_mixins'
module VagrantPlugins module VagrantPlugins
module CommandBox module CommandBox
module Command module Command
class Update < Vagrant.plugin("2", :command) class Update < Vagrant.plugin("2", :command)
include DownloadMixins
def execute def execute
options = {} options = {}
download_options = {}
opts = OptionParser.new do |o| opts = OptionParser.new do |o|
o.banner = "Usage: vagrant box update [options]" 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| o.on("--provider PROVIDER", String, "Update box with specific provider") do |p|
options[:provider] = p.to_sym options[:provider] = p.to_sym
end end
build_download_options(o, download_options)
end end
argv = parse_options(opts) argv = parse_options(opts)
return if !argv return if !argv
if options[:box] if options[:box]
update_specific(options[:box], options[:provider]) update_specific(options[:box], options[:provider], download_options)
else else
update_vms(argv, options[:provider]) update_vms(argv, options[:provider], download_options)
end end
0 0
end end
def update_specific(name, provider) def update_specific(name, provider, download_options)
boxes = {} boxes = {}
@env.boxes.all.each do |n, v, p| @env.boxes.all.each do |n, v, p|
boxes[n] ||= {} boxes[n] ||= {}
@ -74,11 +81,11 @@ module VagrantPlugins
to_update.each do |n, p, v| to_update.each do |n, p, v|
box = @env.boxes.find(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
end end
def update_vms(argv, provider) def update_vms(argv, provider, download_options)
with_target_vms(argv, provider: provider) do |machine| with_target_vms(argv, provider: provider) do |machine|
if !machine.config.vm.box if !machine.config.vm.box
machine.ui.output(I18n.t( machine.ui.output(I18n.t(
@ -95,17 +102,25 @@ module VagrantPlugins
box = machine.box box = machine.box
version = machine.config.vm.box_version 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
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.output(I18n.t("vagrant.box_update_checking", name: box.name))
ui.detail("Latest installed version: #{box.version}") ui.detail("Latest installed version: #{box.version}")
ui.detail("Version constraints: #{version}") ui.detail("Version constraints: #{version}")
ui.detail("Provider: #{box.provider}") ui.detail("Provider: #{box.provider}")
update = box.has_update?(version) update = box.has_update?(version, download_options: download_options)
if !update if !update
ui.success(I18n.t( ui.success(I18n.t(
"vagrant.box_up_to_date_single", "vagrant.box_up_to_date_single",
@ -124,6 +139,10 @@ module VagrantPlugins
box_provider: update[2].name, box_provider: update[2].name,
box_version: update[1].version, box_version: update[1].version,
ui: ui, 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
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 "rest_client"
require "vagrant/util/downloader"
module VagrantPlugins module VagrantPlugins
module LoginCommand module LoginCommand
@ -45,8 +46,23 @@ module VagrantPlugins
with_error_handling do with_error_handling do
url = "#{Vagrant.server_url}/api/v1/authenticate" url = "#{Vagrant.server_url}/api/v1/authenticate"
request = { "user" => { "login" => user, "password" => pass } } 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 = JSON.load(response.to_s)
data["token"] data["token"]
end end

View File

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

View File

@ -3,6 +3,11 @@ module VagrantPlugins
module Command module Command
module MixinInstallOpts module MixinInstallOpts
def build_install_opts(o, options) def build_install_opts(o, options)
options[:plugin_sources] = [
"https://rubygems.org",
"http://gems.hashicorp.com",
]
o.on("--entry-point NAME", String, o.on("--entry-point NAME", String,
"The name of the entry point file for loading the plugin.") do |entry_point| "The name of the entry point file for loading the plugin.") do |entry_point|
options[:entry_point] = entry_point options[:entry_point] = entry_point
@ -17,9 +22,13 @@ module VagrantPlugins
puts puts
end 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, o.on("--plugin-source PLUGIN_SOURCE", String,
"Add a RubyGems repository source") do |plugin_source| "Add a RubyGems repository source") do |plugin_source|
options[:plugin_sources] ||= []
options[:plugin_sources] << plugin_source options[:plugin_sources] << plugin_source
end 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.banner = "Usage: vagrant provision [vm-name] [--provision-with x,y,z]"
o.on("--provision-with x,y,z", Array, 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 } options[:provision_types] = list.map { |type| type.to_sym }
end end
end end

View File

@ -30,7 +30,7 @@ module VagrantPlugins
return if !argv return if !argv
# Validate the provisioners # Validate the provisioners
validate_provisioner_flags!(options) validate_provisioner_flags!(options, argv)
@logger.debug("'reload' each target VM...") @logger.debug("'reload' each target VM...")
machines = [] 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_agent: ssh_info[:forward_agent],
forward_x11: ssh_info[:forward_x11], forward_x11: ssh_info[:forward_x11],
proxy_command: ssh_info[:proxy_command], 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 # Render the template and output directly to STDOUT
template = "commands/ssh_config/config" 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 safe_puts
end end

View File

@ -1,4 +1,5 @@
require 'optparse' require 'optparse'
require 'set'
require "vagrant" require "vagrant"
@ -16,6 +17,7 @@ module VagrantPlugins
def execute def execute
options = {} options = {}
options[:destroy_on_error] = true options[:destroy_on_error] = true
options[:install_provider] = true
options[:parallel] = true options[:parallel] = true
options[:provision_ignore_sentinel] = false options[:provision_ignore_sentinel] = false
@ -41,6 +43,11 @@ module VagrantPlugins
"Back the machine with a specific provider") do |provider| "Back the machine with a specific provider") do |provider|
options[:provider] = provider options[:provider] = provider
end end
o.on("--[no-]install-provider",
"If possible, install the provider if it isn't installed") do |p|
options[:install_provider] = p
end
end end
# Parse the options # Parse the options
@ -48,14 +55,12 @@ module VagrantPlugins
return if !argv return if !argv
# Validate the provisioners # Validate the provisioners
validate_provisioner_flags!(options) validate_provisioner_flags!(options, argv)
# Go over each VM and bring it up # Go over each VM and bring it up
@logger.debug("'Up' each target VM...") @logger.debug("'Up' each target VM...")
# Build up the batch job of what we'll do # Get the names of the machines we want to bring up
machines = []
@env.batch(options[:parallel]) do |batch|
names = argv names = argv
if names.empty? if names.empty?
autostart = false autostart = false
@ -70,7 +75,17 @@ module VagrantPlugins
names = nil if autostart && names.empty? names = nil if autostart && names.empty?
end end
# Build up the batch job of what we'll do
machines = []
if names 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| with_target_vms(names, provider: options[:provider]) do |machine|
@env.ui.info(I18n.t( @env.ui.info(I18n.t(
"vagrant.commands.up.upping", "vagrant.commands.up.upping",
@ -106,6 +121,64 @@ module VagrantPlugins
# Success, exit status 0 # Success, exit status 0
0 0
end 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 end
end end

View File

@ -1,3 +1,5 @@
require "set"
module VagrantPlugins module VagrantPlugins
module CommandUp module CommandUp
module StartMixins module StartMixins
@ -17,7 +19,7 @@ module VagrantPlugins
end end
parser.on("--provision-with x,y,z", Array, 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_types] = list.map { |type| type.to_sym }
options[:provision_enabled] = true options[:provision_enabled] = true
options[:provision_ignore_sentinel] = true options[:provision_ignore_sentinel] = true
@ -26,7 +28,19 @@ module VagrantPlugins
# This validates the provisioner flags and raises an exception # This validates the provisioner flags and raises an exception
# if there are invalid ones. # if there are invalid ones.
def validate_provisioner_flags!(options) 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| (options[:provision_types] || []).each do |type|
klass = Vagrant.plugin("2").manager.provisioners[type] klass = Vagrant.plugin("2").manager.provisioners[type]
if !klass if !klass
@ -38,3 +52,4 @@ module VagrantPlugins
end end
end end
end end
end

View File

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

View File

@ -30,7 +30,12 @@ module VagrantPlugins
# Wait for winrm_info to be ready # Wait for winrm_info to be ready
winrm_info = nil winrm_info = nil
while true while true
winrm_info = nil
begin
winrm_info = Helper.winrm_info(@machine) 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 break if winrm_info
sleep 0.5 sleep 0.5
end end
@ -38,6 +43,7 @@ module VagrantPlugins
# Got it! Let the user know what we're connecting to. # Got it! Let the user know what we're connecting to.
@machine.ui.detail("WinRM address: #{shell.host}:#{shell.port}") @machine.ui.detail("WinRM address: #{shell.host}:#{shell.port}")
@machine.ui.detail("WinRM username: #{shell.username}") @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}") @machine.ui.detail("WinRM transport: #{shell.config.transport}")
last_message = nil last_message = nil
@ -131,10 +137,11 @@ module VagrantPlugins
error_key: nil, # use the error_class message key error_key: nil, # use the error_class message key
good_exit: 0, good_exit: 0,
shell: :powershell, shell: :powershell,
interactive: false,
}.merge(opts || {}) }.merge(opts || {})
opts[:good_exit] = Array(opts[:good_exit]) 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) output = shell.send(opts[:shell], command, &block)
execution_output(output, opts) execution_output(output, opts)
end end
@ -188,9 +195,11 @@ module VagrantPlugins
# in place. # in place.
# #
# @return The wrapper command to execute # @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__) 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" guest_script_path = "c:/tmp/vagrant-elevated-shell.ps1"
file = Tempfile.new(["vagrant-elevated-shell", "ps1"]) file = Tempfile.new(["vagrant-elevated-shell", "ps1"])
begin begin
@ -203,14 +212,16 @@ module VagrantPlugins
file.unlink file.unlink
end end
# convert to double byte unicode string then base64 encode # Convert to double byte unicode string then base64 encode
# just like PowerShell -EncodedCommand expects # just like PowerShell -EncodedCommand expects.
# Suppress the progress stream from leaking to stderr.
wrapped_encoded_command = Base64.strict_encode64( 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}\" " + "powershell -executionpolicy bypass -file '#{guest_script_path}' " +
"-username \"#{shell.username}\" -password \"#{shell.password}\" " + "-username '#{shell.username}' -password '#{shell.password}' " +
"-encoded_command \"#{wrapped_encoded_command}\"" "-encoded_command '#{wrapped_encoded_command}' " +
"-execution_time_limit '#{shell.execution_time_limit}'"
end end
# Handles the raw WinRM shell result and converts it to a # Handles the raw WinRM shell result and converts it to a

View File

@ -11,6 +11,7 @@ module VagrantPlugins
attr_accessor :timeout attr_accessor :timeout
attr_accessor :transport attr_accessor :transport
attr_accessor :ssl_peer_verification attr_accessor :ssl_peer_verification
attr_accessor :execution_time_limit
def initialize def initialize
@username = UNSET_VALUE @username = UNSET_VALUE
@ -23,6 +24,7 @@ module VagrantPlugins
@timeout = UNSET_VALUE @timeout = UNSET_VALUE
@transport = UNSET_VALUE @transport = UNSET_VALUE
@ssl_peer_verification = UNSET_VALUE @ssl_peer_verification = UNSET_VALUE
@execution_time_limit = UNSET_VALUE
end end
def finalize! def finalize!
@ -37,6 +39,7 @@ module VagrantPlugins
@retry_delay = 2 if @retry_delay == UNSET_VALUE @retry_delay = 2 if @retry_delay == UNSET_VALUE
@timeout = 1800 if @timeout == UNSET_VALUE @timeout = 1800 if @timeout == UNSET_VALUE
@ssl_peer_verification = true if @ssl_peer_verification == UNSET_VALUE @ssl_peer_verification = true if @ssl_peer_verification == UNSET_VALUE
@execution_time_limit = "PT2H" if @execution_time_limit == UNSET_VALUE
end end
def validate(machine) def validate(machine)
@ -49,6 +52,7 @@ module VagrantPlugins
errors << "winrm.max_tries cannot be nil." if @max_tries.nil? errors << "winrm.max_tries cannot be nil." if @max_tries.nil?
errors << "winrm.retry_delay 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.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 unless @ssl_peer_verification == true || @ssl_peer_verification == false
errors << "winrm.ssl_peer_verification must be a boolean." errors << "winrm.ssl_peer_verification must be a boolean."
end 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" $task_name = "WinRM_Elevated_Shell"
$out_file = "$env:SystemRoot\Temp\WinRM_Elevated_Shell.log" $out_file = "$env:SystemRoot\Temp\WinRM_Elevated_Shell.log"
@ -13,7 +25,7 @@ $task_xml = @'
<Principals> <Principals>
<Principal id="Author"> <Principal id="Author">
<UserId>{username}</UserId> <UserId>{username}</UserId>
<LogonType>Password</LogonType> <LogonType><%= options[:interactive] ? 'InteractiveTokenOrPassword' : 'Password' %></LogonType>
<RunLevel>HighestAvailable</RunLevel> <RunLevel>HighestAvailable</RunLevel>
</Principal> </Principal>
</Principals> </Principals>
@ -33,7 +45,7 @@ $task_xml = @'
<Hidden>false</Hidden> <Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle> <RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>false</WakeToRun> <WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT2H</ExecutionTimeLimit> <ExecutionTimeLimit>{execution_time_limit}</ExecutionTimeLimit>
<Priority>4</Priority> <Priority>4</Priority>
</Settings> </Settings>
<Actions Context="Author"> <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("{arguments}", $arguments)
$task_xml = $task_xml.Replace("{username}", $username) $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() $schedule.Connect()
$task = $schedule.NewTask($null) $task = $schedule.NewTask($null)
$task.XmlText = $task_xml $task.XmlText = $task_xml
$folder = $schedule.GetFolder("\") $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 = $folder.GetTask("\$task_name")
$registered_task.Run($null) | Out-Null $registered_task.Run($null) | Out-Null

View File

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

View File

@ -27,7 +27,7 @@ module VagrantPlugins
machine.communicate.upload(temp.path, "/tmp/vagrant_network") machine.communicate.upload(temp.path, "/tmp/vagrant_network")
machine.communicate.sudo("mv /tmp/vagrant_network /etc/netctl/#{network[:device]}") 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 end
end end

View File

@ -6,45 +6,109 @@ module VagrantPlugins
module GuestDarwin module GuestDarwin
module Cap module Cap
class ConfigureNetworks class ConfigureNetworks
@@logger = Log4r::Logger.new("vagrant::guest::darwin::configure_networks")
include Vagrant::Util include Vagrant::Util
def self.configure_networks(machine, networks) def self.configure_networks(machine, networks)
# Slightly different than other plugins, using the template to build commands if !machine.provider.capability?(:nic_mac_addresses)
# rather than templating the files. raise Vagrant::Errors::CantReadMACAddresses,
provider: machine.provider_name.to_s
end
machine.communicate.sudo("networksetup -detectnewhardware") nic_mac_addresses = machine.provider.capability(:nic_mac_addresses)
machine.communicate.sudo("networksetup -listnetworkserviceorder > /tmp/vagrant.interfaces") @@logger.debug("mac addresses: #{nic_mac_addresses.inspect}")
tmpints = File.join(Dir.tmpdir, File.basename("#{machine.id}.interfaces"))
machine.communicate.download("/tmp/vagrant.interfaces",tmpints)
devlist = [] mac_service_map = create_mac_service_map(machine)
ints = ::IO.read(tmpints)
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| ints.split(/\n\n/m).each do |i|
if i.match(/Hardware/) and not i.match(/Ethernet/).nil? if i.match(/Hardware/) && i.match(/Ethernet/)
devmap = {}
# Ethernet, should be 2 lines, # Ethernet, should be 2 lines,
# (3) Thunderbolt Ethernet # (3) Thunderbolt Ethernet
# (Hardware Port: Thunderbolt Ethernet, Device: en1) # (Hardware Port: Thunderbolt Ethernet, Device: en1)
# multiline, should match "Thunderbolt Ethernet", "en1" # multiline, should match "Thunderbolt Ethernet", "en1"
devicearry = i.match(/\([0-9]+\) (.+)\n.*Device: (.+)\)/m) devicearry = i.match(/\([0-9]+\) (.+)\n.*Device: (.+)\)/m)
devmap[:interface] = devicearry[2] service = devicearry[1]
devmap[:service] = devicearry[1] interface = devicearry[2]
devlist << devmap
end
end
File.delete(tmpints)
networks.each do |network| # Should map interface to service { "en1" => "Thunderbolt Ethernet" }
service_name = devlist[network[:interface]][:service] interface_map[interface] = service
if network[:type].to_sym == :static end
command = "networksetup -setmanual \"#{service_name}\" #{network[:ip]} #{network[:netmask]}" end
elsif network[:type].to_sym == :dhcp File.delete(tmp_ints)
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 end
machine.communicate.sudo(command) # Should map MAC to service, { "00C100A1B2C3" => "Thunderbolt Ethernet" }
end mac_service_map[naked_mac] = interface_map[interface]
end
end
File.delete(tmp_hw)
mac_service_map
end end
end 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 module GuestDebian
class Guest < Vagrant.plugin("2", :guest) class Guest < Vagrant.plugin("2", :guest)
def detect?(machine) 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 end
end end

View File

@ -30,6 +30,11 @@ module VagrantPlugins
require_relative "cap/rsync" require_relative "cap/rsync"
Cap::RSync Cap::RSync
end end
guest_capability("debian", "smb_install") do
require_relative "cap/smb"
Cap::SMB
end
end end
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 def update_etc_hosts
ip_address = '([0-9]{1,3}\.){3}[0-9]{1,3}' ip_address = '([0-9]{1,3}\.){3}[0-9]{1,3}'
search = "^(#{ip_address})\\s+#{Regexp.escape(current_hostname)}(\\s.*)?$" 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('@') expression = ['s', search, replace, 'g'].join('@')
sudo("sed -ri '#{expression}' /etc/hosts") sudo("sed -ri '#{expression}' /etc/hosts")

View File

@ -17,7 +17,10 @@ module VagrantPlugins
virtual = false virtual = false
interface_names = Array.new interface_names = Array.new
interface_names_by_slot = 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 virtual = true if ['4', '127'].include? result.chomp
end end
@ -106,13 +109,23 @@ module VagrantPlugins
# SSH never dies. # SSH never dies.
interfaces.each do |interface| interfaces.each do |interface|
retryable(on: Vagrant::Errors::VagrantError, tries: 3, sleep: 2) do 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(<<-SCRIPT, error_check: true)
machine.communicate.sudo("! which nmcli >/dev/null 2>&1 || nmcli c reload #{interface}") cat /tmp/vagrant-network-entry_#{interface} >> #{network_scripts_dir}/ifcfg-#{interface}
machine.communicate.sudo("/sbin/ifdown #{interface}", error_check: true)
machine.communicate.sudo("/sbin/ifup #{interface}")
end
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 end
end end

View File

@ -4,7 +4,7 @@ module VagrantPlugins
module GuestFedora module GuestFedora
class Guest < Vagrant.plugin("2", :guest) class Guest < Vagrant.plugin("2", :guest)
def detect?(machine) 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 end
end end

View File

@ -33,10 +33,11 @@ module VagrantPlugins
end end
# Emit an upstart event if we can # 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 <<-SCRIPT
machine.communicate.sudo( 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}") /sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT='#{expanded_guest_path}'
end fi
SCRIPT
end end
end end
end end

View File

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

View File

@ -80,10 +80,11 @@ module VagrantPlugins
end end
# Emit an upstart event if we can # 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 <<-SCRIPT
machine.communicate.sudo( 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}") /sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT='#{expanded_guest_path}'
end fi
SCRIPT
end end
def self.unmount_virtualbox_shared_folder(machine, guestpath, options) def self.unmount_virtualbox_shared_folder(machine, guestpath, options)

View File

@ -10,8 +10,11 @@ module VagrantPlugins
machine.communicate.tap do |comm| machine.communicate.tap do |comm|
if comm.test("test -f ~/.ssh/authorized_keys") if comm.test("test -f ~/.ssh/authorized_keys")
comm.execute( comm.execute(<<SCRIPT)
"sed -i '/^.*#{contents}.*$/d' ~/.ssh/authorized_keys") 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 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" require_relative "cap/network_scripts_dir"
Cap::NetworkScriptsDir Cap::NetworkScriptsDir
end end
guest_capability("pld", "flavor") do
require_relative "cap/flavor"
Cap::Flavor
end
end end
end end
end end

View File

@ -3,7 +3,11 @@ module VagrantPlugins
module Cap module Cap
class NFSClient class NFSClient
def self.nfs_client_install(machine) def self.nfs_client_install(machine)
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") machine.communicate.sudo("yum -y install nfs-utils nfs-utils-lib")
end
restart_nfs(machine) restart_nfs(machine)
end end

View File

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

View File

@ -45,6 +45,10 @@ module VagrantPlugins
require_relative "cap/rsync" require_relative "cap/rsync"
Cap::RSync Cap::RSync
end end
def self.dnf?(machine)
machine.communicate.test("/usr/bin/which -s dnf")
end
end end
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 VagrantPlugins
module GuestDebian8 module GuestSlackware
class Guest < Vagrant.plugin("2", :guest) class Guest < Vagrant.plugin("2", :guest)
def detect?(machine) def detect?(machine)
machine.communicate.test("cat /etc/issue | grep 'Debian' | grep '8'") machine.communicate.test("cat /etc/slackware-version")
end end
end 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 end
def update_etc_hostname def update_etc_hostname
return super unless vivid? return super unless systemd?
sudo("hostnamectl set-hostname '#{short_hostname}'") sudo("hostnamectl set-hostname '#{short_hostname}'")
end end
@ -15,7 +15,7 @@ module VagrantPlugins
if hardy? if hardy?
# hostname.sh returns 1, so use `true` to get a 0 exitcode # hostname.sh returns 1, so use `true` to get a 0 exitcode
sudo("/etc/init.d/hostname.sh start; true") sudo("/etc/init.d/hostname.sh start; true")
elsif vivid? elsif systemd?
# Service runs via hostnamectl # Service runs via hostnamectl
else else
sudo("service hostname start") sudo("service hostname start")
@ -26,19 +26,25 @@ module VagrantPlugins
os_version("hardy") os_version("hardy")
end end
def vivid?
os_version("vivid")
end
def renew_dhcp def renew_dhcp
sudo("ifdown -a; ifup -a; ifup -a --allow=hotplug") sudo("ifdown -a; ifup -a; ifup -a --allow=hotplug")
end end
private 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) def os_version(name)
machine.communicate.test("[ `lsb_release -c -s` = #{name} ]") machine.communicate.test("[ `lsb_release -c -s` = #{name} ]")
end end
def systemd?
init_package == 'systemd'
end
end end
end end
end end

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