diff --git a/.gitignore b/.gitignore
index b1274f4d0..eedfe7d70 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,10 @@
# OS-specific
.DS_Store
+# Editor swapfiles
+.*.sw?
+*~
+
# Vagrant stuff
acceptance_config.yml
boxes/*
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c6d56fb98..892499cad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,22 +2,162 @@
FEATURES:
+ - **New Command: `vagrant powershell`**: For machines that support it,
+ this will open a PowerShell prompt.
+ - **New Command: `vagrant port`**: For machines that support it, this will
+ display the list of forwarded ports from the guest to the host.
- **Linked Clones**: VirtualBox and VMware providers now support
linked clones for very fast (millisecond) imports on up. [GH-4484]
- **Snapshots**: The `vagrant snapshot` command can be used to checkpoint
and restore point-in-time snapshots.
- **IPv6 Private Networks**: Private networking now supports IPv6. This
only works with VirtualBox and VMware at this point. [GH-6342]
+ - New provisioner: `ansible_local` to execute Ansible from the guest
+ machine. [GH-2103]
+
+BREAKING CHANGES:
+
+ - The `ansible` provisioner now can override the effective ansible remote user
+ (i.e. `ansible_ssh_user` setting) to always correspond to the vagrant ssh
+ username. This change is enabled by default, but we expect this to affect
+ only a tiny number of people as it corresponds to the common usage.
+ If you however use multiple remote usernames in your Ansible plays, tasks,
+ or custom inventories, you can simply set the option `force_remote_user` to
+ false to make Vagrant behave the same as before.
+ - provisioners/salt: the "config_dir" option has been removed. It has no
+ effect in Vagrant 1.8. [GH-6073]
IMPROVEMENTS:
- core: allow removal of all box versions with `--all` flag [GH-3462]
+ - core: prune entries from global status on non-existent cwd [GH-6535]
+ - core: networking: allow specifying a DHCP IP [GH-6325]
+ - core: run provisioner cleanup tasks before powering off the VM [GH-6553]
+ - core: only run provisioner cleanup tasks if they're implemented [GH-6603]
+ This improves UX, but wasn't a bug before.
+ - command/plugin: Add `--plugin-clean-sources` flag to reset plugin install
+ sources, primarily for corp firewalls. [GH-4738]
+ - command/rsync-auto: SSH connection is cached for faster sync times [GH-6399]
+ - command/up: provisioners are run on suspend resume [GH-5815]
+ - communicators/ssh: allow specifying host environment variables to forward
+ to guests [GH-4132, GH-6562]
+ - communicators/winrm: Configurable execution time limit [GH-6213]
+ - providers/virtualbox: cache version lookup, which caused significant
+ slowdown on some Windows hosts [GH-6552]
+ - providers/virtualbox: add `public_address` capability for virtualbox
+ [GH-6583, GH-5978]
+ - provisioners/chef: perform cleanup tasks on the guest instead of the host
+ - provisioners/chef: automatically generate a node_name if one was not given
+ [GH-6555]
+ - provisioners/chef: install Chef automatically on Windows [GH-6557]
+ - provisioners/chef: allow the user to specify the Chef product (such as
+ the Chef Development Kit) [GH-6557]
+ - provisioners/chef: allow data_bags_path to be an array [GH-5988, GH-6561]
+ - provisioners/shell: Support interactive mode for elevated PowerShell
+ scripts [GH-6185]
+ - provisioners/shell: add `env` option [GH-6588, GH-6516]
+ - provisioners/ansible+ansible_local: add support for ansible-galaxy [GH-2718]
+ - provisioners/ansible+ansible_local: add support for group and host variables
+ in the generated inventory [GH-6619]
+ - provisioners/ansible+ansible_local: add support for alphanumeric patterns
+ for groups in the generated inventory [GH-3539]
+ - provisioners/ansible: add support for WinRM settings [GH-5086]
+ - provisioners/ansible: add new `force_remote_user` option to control whether
+ `ansible_ssh_user` parameter should be applied or not [GH-6348]
+ - provisioners/ansible: show a warning when running from a Windows Host [GH-5292]
+ - pushes/local-exec: add support for specifying script args [GH-6661, GH-6660]
+ - guests/slackware: add support for networking [GH-6514]
BUG FIXES:
+ - core: Ctrl-C weirdness fixed where it would exit parent process
+ before Vagrant finished cleaning up [GH-6085]
+ - core: DHCP network configurations don't warn on IP addresses ending
+ in ".1" [GH-6150]
+ - core: only append `access_token` when it does not exist in the URL
+ [GH-6395, GH-6534]
+ - core: use the correct private key when packaging a box [GH-6406]
+ - core: fix crash when using invalid box checksum type [GH-6327]
+ - core: don't check for metadata if the download URL is not HTTP [GH-6540]
+ - core: don't make custom dotfile path if there is no Vagrantfile [GH-6542]
+ - core: more robust check for admin privs on Windows [GH-5616]
+ - core: properly detect when HTTP server doesn't support byte ranges and
+ retry from scratch [GH-4479]
+ - core: line numbers show properly in Vagrantfile syntax errors
+ on Windows [GH-6445]
+ - core: catch errors setting env vars on Windows [GH-6017]
+ - core: remove cached synced folders when they're removed from the
+ Vagrantfile [GH-6567]
+ - core: use case-insensitive comparison for box checksum validations
+ [GH-6648, GH-6650]
+ - commands/box: add command with `~` paths on Windows works [GH-5747]
+ - commands/box: the update command supports CA settings [GH-4473]
+ - commands/box: removing all versions and providers of a box will properly
+ clean all directories in `~/.vagrant.d/boxes` [GH-3570]
+ - commands/box: outdated global won't halt on metadata download failure [GH-6453]
+ - commands/login: respect environment variables in `vagrant login` command
+ [GH-6590, GH-6422]
+ - commands/package: when re-packaging a packaged box, preserve the
+ generated SSH key [GH-5780]
+ - commands/plugin: retry plugin install automatically a few times to
+ avoid network issues [GH-6097]
+ - commands/rdp: prefer `xfreerdp` if it is available on Linux [GH-6475]
+ - commands/up: the `--provision-with` flag works with provisioner names [GH-5981]
+ - communicator/ssh: fix potential crash case with PTY [GH-6225]
+ - communicator/ssh: escape IdentityFile path [GH-6428, GH-6589]
- communicator/winrm: respect `boot_timeout` setting [GH-6229]
+ - communicator/winrm: execute scheduled tasks immediately on Windows XP
+ since elevation isn't required [GH-6195]
+ - communicator/winrm: Decouple default port forwarding rules for "winrm" and
+ "winrm-ssl" [GH-6581]
+ - communicator/winrm: Hide progress bars from PowerShell v5 [GH-6309]
+ - guests/arch: enable network device after setting it up [GH-5737]
+ - guests/darwin: advanced networking works with more NICs [GH-6386]
+ - guests/debian: graceful shutdown works properly with newer releases [GH-5986]
+ - guests/fedora: Preserve `localhost` entry when changing hostname [GH-6203]
+ - guests/fedora: Use dnf if it is available [GH-6288]
+ - guests/linux: when replacing a public SSH key, use POSIX-compliant
+ sed flags [GH-6565]
+ - guests/suse: DHCP network interfaces properly configured [GH-6502]
+ - hosts/slackware: Better detection of NFS [GH-6367]
+ - providers/hyper-v: support generation 2 VMs [GH-6372]
+ - providers/hyper-v: support VMs with more than one NIC [GH-4346]
+ - providers/hyper-v: check if user is in the Hyper-V admin group if
+ they're not a Windows admin [GH-6662]
+ - providers/virtualbox: ignore "Unknown" status bridge interfaces [GH-6061]
+ - providers/virtualbox: only fix ipv6 interfaces that are in use
+ [GH-6586, GH-6552]
- provisioners/ansible: use quotes for the `ansible_ssh_private_key_file`
value in the generated inventory [GH-6209]
+ - provisioners/ansible: use quotes when passing the private key files via
+ OpenSSH `-i` command line arguments [GH-6671]
+ - provisioners/ansible: don't show the `ansible-playbook` command when verbose
+ option is an empty string
+ - provisioners/chef: fix `nodes_path` for Chef Zero [GH-6025, GH-6049]
+ - provisioners/chef: do not error when the `node_name` is unset
+ [GH-6005, GH-6064, GH-6541]
+ - provisioners/chef: only force the formatter on Chef 11 or higher
+ [GH-6278, GH-6556]
+ - provisioners/chef: require `nodes_path` to be set for Chef Zero
+ [GH-6110, GH-6559]
+ - provisioners/puppet: apply provisioner uses correct default manifests
+ with environments. [GH-5987]
+ - provisioners/puppet: remove broken backticks [GH-6404]
+ - provisioners/puppet: find Puppet binary properly on Windows [GH-6259]
+ - provisioners/puppet-server: works with Puppet Collection 1 [GH-6389]
+ - provisioners/salt: call correct executables on Windows [GH-5999]
+ - provisioners/salt: log level and colorize works for masterless [GH-6474]
+ - push/local-exec: use subprocess on windows when fork does not exist
+ [GH-5307, GH-6563]
+ - push/heroku: use current branch [GH-6554]
+ - synced\_folders/rsync: on Windows, replace all paths with Cygwin
+ paths since all rsync implementations require this [GH-6160]
+ - synced\_folders/smb: use credentials files to allow for more characters
+ in password [GH-4230]
+
+PLUGIN AUTHOR CHANGES:
+
+ - installer: Upgrade to Ruby 2.2.3
## 1.7.4 (July 17, 2015)
diff --git a/README.md b/README.md
index a361da9b0..b03ca1f31 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Vagrant
-* Website: [http://www.vagrantup.com](http://www.vagrantup.com)
+* Website: [https://www.vagrantup.com/](https://www.vagrantup.com/)
* Source: [https://github.com/mitchellh/vagrant](https://github.com/mitchellh/vagrant)
* IRC: `#vagrant` on Freenode
* Mailing list: [Google Groups](http://groups.google.com/group/vagrant-up)
@@ -19,12 +19,12 @@ between Windows, Mac OS X, and Linux.
## Quick Start
For the quick-start, we'll bring up a development machine on
-[VirtualBox](http://www.virtualbox.org) because it is free and works
+[VirtualBox](https://www.virtualbox.org/) because it is free and works
on all major platforms. Vagrant can, however, work with almost any
-system such as OpenStack, VMware, Docker, etc.
+system such as [OpenStack] (https://www.openstack.org/), [VMware] (http://www.vmware.com/), [Docker] (https://docs.docker.com/), etc.
First, make sure your development machine has
-[VirtualBox](http://www.virtualbox.org)
+[VirtualBox](https://www.virtualbox.org/)
installed. After this,
[download and install the appropriate Vagrant package for your OS](http://www.vagrantup.com/downloads).
@@ -55,7 +55,7 @@ Ruby 2.0 is needed.
### Dependencies and Unit Tests
-To hack on Vagrant, you'll need [bundler](http://github.com/carlhuda/bundler) which can
+To hack on Vagrant, you'll need [bundler](https://github.com/bundler/bundler) which can
be installed with a simple `gem install bundler`. Afterwards, do the following:
bundle install
diff --git a/contrib/README.md b/contrib/README.md
index c89c2ef56..bbdf84141 100644
--- a/contrib/README.md
+++ b/contrib/README.md
@@ -10,5 +10,7 @@ for each item will be kept below.
* `emacs` - Contains a file for enabling Ruby syntax highlighting for `Vagrantfile`s in `emacs`.
* `st` - Contains a `.sublime-settings` file for enabling Ruby syntax highlighting
for `Vagrantfile`s in Sublime Text.
+* `sudoers` - Contains the pieces of `/etc/sudoers` configuration to avoid password entry when
+ starting machines.
* `vim` - Contains a `.vim` file for enabling Ruby syntax highlighting
- for `Vagrantfile`s in `vim`.
\ No newline at end of file
+ for `Vagrantfile`s in `vim`.
diff --git a/contrib/sudoers/linux-fedora b/contrib/sudoers/linux-fedora
index f2d64b66d..5bec89189 100644
--- a/contrib/sudoers/linux-fedora
+++ b/contrib/sudoers/linux-fedora
@@ -2,5 +2,6 @@ Cmnd_Alias VAGRANT_EXPORTS_ADD = /usr/bin/tee -a /etc/exports
Cmnd_Alias VAGRANT_NFSD_CHECK = /usr/bin/systemctl status nfs-server.service
Cmnd_Alias VAGRANT_NFSD_START = /usr/bin/systemctl start nfs-server.service
Cmnd_Alias VAGRANT_NFSD_APPLY = /usr/sbin/exportfs -ar
-Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /etc/exports
-%vagrant ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY, VAGRANT_EXPORTS_REMOVE
+Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /*/exports
+Cmnd_Alias VAGRANT_EXPORTS_REMOVE_2 = /bin/cp /*/exports /etc/exports
+%vagrant ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY, VAGRANT_EXPORTS_REMOVE, VAGRANT_EXPORTS_REMOVE_2
diff --git a/contrib/sudoers/linux-suse b/contrib/sudoers/linux-suse
new file mode 100644
index 000000000..30658af3f
--- /dev/null
+++ b/contrib/sudoers/linux-suse
@@ -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
diff --git a/contrib/sudoers/linux-ubuntu b/contrib/sudoers/linux-ubuntu
index c4e786cf3..4e2cd8bde 100644
--- a/contrib/sudoers/linux-ubuntu
+++ b/contrib/sudoers/linux-ubuntu
@@ -4,5 +4,6 @@ Cmnd_Alias VAGRANT_EXPORTS_ADD = /usr/bin/tee -a /etc/exports
Cmnd_Alias VAGRANT_NFSD_CHECK = /etc/init.d/nfs-kernel-server status
Cmnd_Alias VAGRANT_NFSD_START = /etc/init.d/nfs-kernel-server start
Cmnd_Alias VAGRANT_NFSD_APPLY = /usr/sbin/exportfs -ar
-Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /etc/exports
-%sudo ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY, VAGRANT_EXPORTS_REMOVE
+Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /*/exports
+Cmnd_Alias VAGRANT_EXPORTS_REMOVE_2 = /bin/cp /*/exports /etc/exports
+%sudo ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY, VAGRANT_EXPORTS_REMOVE, VAGRANT_EXPORTS_REMOVE_2
diff --git a/lib/vagrant/action/builtin/box_add.rb b/lib/vagrant/action/builtin/box_add.rb
index 6aec780ee..b87bf46b6 100644
--- a/lib/vagrant/action/builtin/box_add.rb
+++ b/lib/vagrant/action/builtin/box_add.rb
@@ -381,7 +381,9 @@ module Vagrant
@logger.info("URL is a file or protocol not found and assuming file.")
file_path = File.expand_path(url)
file_path = Util::Platform.cygwin_windows_path(file_path)
- url = "file:#{file_path}"
+ file_path = file_path.gsub("\\", "/")
+ file_path = "/#{file_path}" if !file_path.start_with?("/")
+ url = "file://#{file_path}"
end
# If the temporary path exists, verify it is not too old. If its
@@ -491,6 +493,12 @@ module Vagrant
end
end
+ # If this isn't HTTP, then don't do the HEAD request
+ if !uri.scheme.downcase.start_with?("http")
+ @logger.info("not checking metadata since box URI isn't HTTP")
+ return false
+ end
+
output = d.head
match = output.scan(/^Content-Type: (.+?)$/i).last
return false if !match
@@ -507,14 +515,14 @@ module Vagrant
Digest::SHA2
else
raise Errors::BoxChecksumInvalidType,
- type: env[:box_checksum_type].to_s
+ type: checksum_type.to_s
end
@logger.info("Validating checksum with #{checksum_klass}")
@logger.info("Expected checksum: #{checksum}")
actual = FileChecksum.new(path, checksum_klass).checksum
- if actual != checksum
+ if actual.casecmp(checksum) != 0
raise Errors::BoxChecksumMismatch,
actual: actual,
expected: checksum
diff --git a/lib/vagrant/action/builtin/box_check_outdated.rb b/lib/vagrant/action/builtin/box_check_outdated.rb
index cee8c6877..d39afd272 100644
--- a/lib/vagrant/action/builtin/box_check_outdated.rb
+++ b/lib/vagrant/action/builtin/box_check_outdated.rb
@@ -37,13 +37,23 @@ module Vagrant
end
constraints = machine.config.vm.box_version
+ # Have download options specified in the environment override
+ # options specified for the machine.
+ download_options = {
+ ca_cert: env[:ca_cert] || machine.config.vm.box_download_ca_cert,
+ ca_path: env[:ca_path] || machine.config.vm.box_download_ca_path,
+ client_cert: env[:client_cert] ||
+ machine.config.vm.box_download_client_cert,
+ insecure: !env[:insecure].nil? ?
+ env[:insecure] : machine.config.vm.box_download_insecure
+ }
env[:ui].output(I18n.t(
"vagrant.box_outdated_checking_with_refresh",
name: box.name))
update = nil
begin
- update = box.has_update?(constraints)
+ update = box.has_update?(constraints, download_options: download_options)
rescue Errors::BoxMetadataDownloadError => e
env[:ui].warn(I18n.t(
"vagrant.box_outdated_metadata_download_error",
diff --git a/lib/vagrant/action/builtin/box_remove.rb b/lib/vagrant/action/builtin/box_remove.rb
index 5d18ed704..cf80542cb 100644
--- a/lib/vagrant/action/builtin/box_remove.rb
+++ b/lib/vagrant/action/builtin/box_remove.rb
@@ -111,6 +111,7 @@ module Vagrant
provider: box.provider,
version: box.version))
box.destroy!
+ env[:box_collection].clean(box.name)
# Passes on the removed box to the rest of the middleware chain
env[:box_removed] = box
diff --git a/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb b/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb
index b53ba35c2..05e8edfa3 100644
--- a/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb
+++ b/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb
@@ -128,7 +128,7 @@ module Vagrant
port_checker[repaired_port] ||
lease_check(repaired_port)
if in_use
- @logger.info("Reparied port also in use: #{repaired_port}. Trying another...")
+ @logger.info("Repaired port also in use: #{repaired_port}. Trying another...")
next
end
@@ -156,8 +156,6 @@ module Vagrant
new_port: repaired_port.to_s))
end
end
-
- @app.call(env)
end
def lease_check(port)
diff --git a/lib/vagrant/action/builtin/mixin_synced_folders.rb b/lib/vagrant/action/builtin/mixin_synced_folders.rb
index fb0abcce2..08fa867a9 100644
--- a/lib/vagrant/action/builtin/mixin_synced_folders.rb
+++ b/lib/vagrant/action/builtin/mixin_synced_folders.rb
@@ -76,6 +76,16 @@ module Vagrant
if opts[:merge]
existing = cached_synced_folders(machine)
if existing
+ if opts[:vagrantfile]
+ # Go through and find any cached that were from the
+ # Vagrantfile itself. We remove those if it was requested.
+ existing.each do |impl, fs|
+ fs.each do |id, data|
+ fs.delete(id) if data[:__vagrantfile]
+ end
+ end
+ end
+
folders.each do |impl, fs|
existing[impl] ||= {}
fs.each do |id, data|
@@ -101,7 +111,12 @@ module Vagrant
return cached_synced_folders(machine) if opts[:cached]
config = opts[:config]
- config ||= machine.config.vm
+ root = false
+ if !config
+ config = machine.config.vm
+ root = true
+ end
+
config_folders = config.synced_folders
folders = {}
@@ -131,9 +146,17 @@ module Vagrant
end
end
+ # Get the data to store
+ data = data.dup
+ if root
+ # If these are the root synced folders (attached directly)
+ # to the Vagrantfile, then we mark it as such.
+ data[:__vagrantfile] = true
+ end
+
# Keep track of this shared folder by the implementation.
folders[impl] ||= {}
- folders[impl][id] = data.dup
+ folders[impl][id] = data
end
# If we have folders with the "default" key, then determine the
diff --git a/lib/vagrant/action/builtin/provisioner_cleanup.rb b/lib/vagrant/action/builtin/provisioner_cleanup.rb
index 3aab56c6e..ead72268c 100644
--- a/lib/vagrant/action/builtin/provisioner_cleanup.rb
+++ b/lib/vagrant/action/builtin/provisioner_cleanup.rb
@@ -13,8 +13,9 @@ module Vagrant
def initialize(app, env, place=nil)
@app = app
@logger = Log4r::Logger.new("vagrant::action::builtin::provision_cleanup")
- @place ||= :after
- @place = @place.to_sym
+
+ place ||= :after
+ @place = place.to_sym
end
def call(env)
@@ -31,10 +32,20 @@ module Vagrant
# Ask the provisioners to modify the configuration if needed
provisioner_instances(env).each do |p, _|
- env[:ui].info(I18n.t(
- "vagrant.provisioner_cleanup",
- name: type_map[p].to_s))
- p.cleanup
+ name = type_map[p].to_s
+
+ # Check if the subclass defined a cleanup method. The parent
+ # provisioning class defines a `cleanup` method, so we cannot use
+ # `respond_to?` here. Instead, we have to check if _this_ instance
+ # defines a cleanup task.
+ if p.public_methods(false).include?(:cleanup)
+ env[:ui].info(I18n.t("vagrant.provisioner_cleanup",
+ name: name,
+ ))
+ p.cleanup
+ else
+ @logger.debug("Skipping cleanup tasks for `#{name}' - not defined")
+ end
end
end
end
diff --git a/lib/vagrant/action/builtin/synced_folders.rb b/lib/vagrant/action/builtin/synced_folders.rb
index 230676d66..b56c212db 100644
--- a/lib/vagrant/action/builtin/synced_folders.rb
+++ b/lib/vagrant/action/builtin/synced_folders.rb
@@ -23,6 +23,7 @@ module Vagrant
config: env[:synced_folders_config],
}
+ @logger.info("SyncedFolders loading from cache: #{opts[:cached]}")
folders = synced_folders(env[:machine], **opts)
original_folders = folders
@@ -121,8 +122,11 @@ module Vagrant
save_synced_folders(env[:machine], all)
else
+ save_opts = { merge: true }
+ save_opts[:vagrantfile] = true if !opts[:config]
+
# Save the synced folders
- save_synced_folders(env[:machine], original_folders, merge: true)
+ save_synced_folders(env[:machine], original_folders, **save_opts)
end
end
end
diff --git a/lib/vagrant/action/general/package.rb b/lib/vagrant/action/general/package.rb
index 58991222c..beb6f05fc 100644
--- a/lib/vagrant/action/general/package.rb
+++ b/lib/vagrant/action/general/package.rb
@@ -30,7 +30,7 @@ module Vagrant
def call(env)
@env = env
file_name = File.basename(@env["package.output"].to_s)
-
+
raise Errors::PackageOutputDirectory if File.directory?(tar_path)
raise Errors::PackageOutputExists, file_name:file_name if File.exist?(tar_path)
raise Errors::PackageRequiresDirectory if !env["package.directory"] ||
@@ -113,6 +113,21 @@ module Vagrant
# If we don't have a generated private key, we do nothing
path = @env[:machine].data_dir.join("private_key")
+ if !path.file?
+ # If we have a private key that was copied into this box,
+ # then we copy that. This is a bit of a heuristic and can be a
+ # security risk if the key is named the correct thing, but
+ # we'll take that risk for dev environments.
+ (@env[:machine].config.ssh.private_key_path || []).each do |p|
+ # If we have the correctly named key, copy it
+ if File.basename(p) == "vagrant_private_key"
+ path = Pathname.new(p)
+ break
+ end
+ end
+ end
+
+ # If we still have no matching key, do nothing
return if !path.file?
# Copy it into our box directory
diff --git a/lib/vagrant/box.rb b/lib/vagrant/box.rb
index ce829870a..7b0c7da3a 100644
--- a/lib/vagrant/box.rb
+++ b/lib/vagrant/box.rb
@@ -115,8 +115,9 @@ module Vagrant
# Loads the metadata URL and returns the latest metadata associated
# with this box.
#
+ # @param [Hash] download_options Options to pass to the downloader.
# @return [BoxMetadata]
- def load_metadata
+ def load_metadata(**download_options)
tf = Tempfile.new("vagrant")
tf.close
@@ -127,7 +128,7 @@ module Vagrant
url = "file:#{url}"
end
- opts = { headers: ["Accept: application/json"] }
+ opts = { headers: ["Accept: application/json"] }.merge(download_options)
Util::Downloader.new(url, tf.path, **opts).download!
BoxMetadata.new(File.open(tf.path, "r"))
rescue Errors::DownloaderError => e
@@ -148,7 +149,7 @@ module Vagrant
# satisfy. If nil, the version constrain defaults to being a
# larger version than this box.
# @return [Array]
- def has_update?(version=nil)
+ def has_update?(version=nil, download_options: {})
if !@metadata_url
raise Errors::BoxUpdateNoMetadata, name: @name
end
@@ -156,7 +157,7 @@ module Vagrant
version += ", " if version
version ||= ""
version += "> #{@version}"
- md = self.load_metadata
+ md = self.load_metadata(download_options)
newer = md.version(version, provider: @provider)
return nil if !newer
diff --git a/lib/vagrant/box_collection.rb b/lib/vagrant/box_collection.rb
index a7599f7c2..6e83ad4ee 100644
--- a/lib/vagrant/box_collection.rb
+++ b/lib/vagrant/box_collection.rb
@@ -12,7 +12,9 @@ module Vagrant
# for accessing/finding individual boxes, adding new boxes, or deleting
# boxes.
class BoxCollection
- TEMP_PREFIX = "vagrant-box-add-temp-"
+ TEMP_PREFIX = "vagrant-box-add-temp-".freeze
+ VAGRANT_SLASH = "-VAGRANTSLASH-".freeze
+ VAGRANT_COLON = "-VAGRANTCOLON-".freeze
# The directory where the boxes in this collection are stored.
#
@@ -346,6 +348,14 @@ module Vagrant
end
end
+ # Cleans the directory for a box by removing the folders that are
+ # empty.
+ def clean(name)
+ return false if exists?(name)
+ path = File.join(directory, dir_name(name))
+ FileUtils.rm_rf(path)
+ end
+
protected
# Returns the directory name for the box of the given name.
@@ -354,16 +364,16 @@ module Vagrant
# @return [String]
def dir_name(name)
name = name.dup
- name.gsub!(":", "-VAGRANTCOLON-") if Util::Platform.windows?
- name.gsub!("/", "-VAGRANTSLASH-")
+ name.gsub!(":", VAGRANT_COLON) if Util::Platform.windows?
+ name.gsub!("/", VAGRANT_SLASH)
name
end
# Returns the directory name for the box cleaned up
def undir_name(name)
name = name.dup
- name.gsub!("-VAGRANTCOLON-", ":")
- name.gsub!("-VAGRANTSLASH-", "/")
+ name.gsub!(VAGRANT_COLON, ":")
+ name.gsub!(VAGRANT_SLASH, "/")
name
end
@@ -440,5 +450,10 @@ module Vagrant
ensure
dir.rmtree if dir.exist?
end
+
+ # Checks if a box with a given name exists.
+ def exists?(box_name)
+ all.any? { |box| box.first.eql?(box_name) }
+ end
end
end
diff --git a/lib/vagrant/bundler.rb b/lib/vagrant/bundler.rb
index 05867da15..fe9699ef4 100644
--- a/lib/vagrant/bundler.rb
+++ b/lib/vagrant/bundler.rb
@@ -7,6 +7,7 @@ require "bundler"
require_relative "shared_helpers"
require_relative "version"
+require_relative "util/safe_env"
module Vagrant
# This class manages Vagrant's interaction with Bundler. Vagrant uses
@@ -42,6 +43,9 @@ module Vagrant
yield
end
end
+
+ # Configure Bundler to retry
+ ::Bundler.settings[:retry] = 3
end
# Initializes Bundler and the various gem paths so that we can begin
@@ -68,12 +72,15 @@ module Vagrant
# we add all our plugin dependencies.
@gemfile = build_gemfile(plugins)
- # Set the environmental variables for Bundler
- ENV["BUNDLE_APP_CONFIG"] = @appconfigpath
- ENV["BUNDLE_CONFIG"] = @configfile.path
- ENV["BUNDLE_GEMFILE"] = @gemfile.path
- ENV["GEM_PATH"] =
- "#{bundle_path}#{::File::PATH_SEPARATOR}#{@gem_path}"
+ Util::SafeEnv.change_env do |env|
+ # Set the environmental variables for Bundler
+ env["BUNDLE_APP_CONFIG"] = @appconfigpath
+ env["BUNDLE_CONFIG"] = @configfile.path
+ env["BUNDLE_GEMFILE"] = @gemfile.path
+ env["GEM_PATH"] =
+ "#{bundle_path}#{::File::PATH_SEPARATOR}#{@gem_path}"
+ end
+
Gem.clear_paths
end
@@ -178,11 +185,6 @@ module Vagrant
f = File.open(Tempfile.new("vagrant").path + "2", "w+")
f.tap do |gemfile|
- if !sources.include?("http://rubygems.org")
- gemfile.puts(%Q[source "https://rubygems.org"])
- end
-
- gemfile.puts(%Q[source "http://gems.hashicorp.com"])
sources.each do |source|
next if source == ""
gemfile.puts(%Q[source "#{source}"])
diff --git a/lib/vagrant/config/loader.rb b/lib/vagrant/config/loader.rb
index ef4abe4bd..2bf011231 100644
--- a/lib/vagrant/config/loader.rb
+++ b/lib/vagrant/config/loader.rb
@@ -221,7 +221,12 @@ module Vagrant
line = "(unknown)"
if e.backtrace && e.backtrace[0]
- line = e.backtrace[0].split(":")[1]
+ e.backtrace[0].split(":").each do |part|
+ if part =~ /\d+/
+ line = part.to_i
+ break
+ end
+ end
end
# Report the generic exception
diff --git a/lib/vagrant/environment.rb b/lib/vagrant/environment.rb
index 88779a2e9..e98c8f397 100644
--- a/lib/vagrant/environment.rb
+++ b/lib/vagrant/environment.rb
@@ -832,7 +832,7 @@ module Vagrant
# This creates the local data directory and show an error if it
# couldn't properly be created.
- def setup_local_data_path
+ def setup_local_data_path(force=false)
if @local_data_path.nil?
@logger.warn("No local data path is set. Local data cannot be stored.")
return
@@ -847,6 +847,9 @@ module Vagrant
upgrade_v1_dotfile(@local_data_path)
end
+ # If we don't have a root path, we don't setup anything
+ return if !force && root_path.nil?
+
begin
@logger.debug("Creating: #{@local_data_path}")
FileUtils.mkdir_p(@local_data_path)
@@ -965,7 +968,7 @@ module Vagrant
# Now, we create the actual local data directory. This should succeed
# this time since we renamed the old conflicting V1.
- setup_local_data_path
+ setup_local_data_path(true)
if json_data["active"]
@logger.debug("Upgrading to V2 style for each active VM")
diff --git a/lib/vagrant/errors.rb b/lib/vagrant/errors.rb
index c76f3cbe9..057d6a823 100644
--- a/lib/vagrant/errors.rb
+++ b/lib/vagrant/errors.rb
@@ -108,14 +108,6 @@ module Vagrant
error_key(:active_machine_with_different_provider)
end
- class AnsibleFailed < VagrantError
- error_key(:ansible_failed)
- end
-
- class AnsiblePlaybookAppNotFound < VagrantError
- error_key(:ansible_playbook_app_not_found)
- end
-
class BatchMultiError < VagrantError
error_key(:batch_multi_error)
end
@@ -248,6 +240,10 @@ module Vagrant
error_key(:bundler_error)
end
+ class CantReadMACAddresses < VagrantError
+ error_key(:cant_read_mac_addresses)
+ end
+
class CapabilityHostExplicitNotDetected < VagrantError
error_key(:capability_host_explicit_not_detected)
end
@@ -348,6 +344,10 @@ module Vagrant
error_key(:downloader_interrupted)
end
+ class EnvInval < VagrantError
+ error_key(:env_inval)
+ end
+
class EnvironmentNonExistentCWD < VagrantError
error_key(:environment_non_existent_cwd)
end
@@ -408,8 +408,8 @@ module Vagrant
error_key(:linux_nfs_mount_failed)
end
- class LinuxRDesktopNotFound < VagrantError
- error_key(:linux_rdesktop_not_found)
+ class LinuxRDPClientNotFound < VagrantError
+ error_key(:linux_rdp_client_not_found)
end
class LocalDataDirectoryNotAccessible < VagrantError
@@ -524,6 +524,10 @@ module Vagrant
error_key(:provider_cant_install)
end
+ class ProviderChecksumMismatch < VagrantError
+ error_key(:provider_checksum_mismatch)
+ end
+
class ProviderInstallFailed < VagrantError
error_key(:provider_install_failed)
end
diff --git a/lib/vagrant/machine.rb b/lib/vagrant/machine.rb
index 07ec0ac94..f9ba4bcdf 100644
--- a/lib/vagrant/machine.rb
+++ b/lib/vagrant/machine.rb
@@ -116,6 +116,9 @@ module Vagrant
# XXX: This is temporary. This will be removed very soon.
if base
@id = name
+
+ # For base setups, we don't want to insert the key
+ @config.ssh.insert_key = false
else
reload
end
@@ -141,6 +144,10 @@ module Vagrant
if state.id == MachineState::NOT_CREATED_ID
self.id = nil
end
+
+ # Output a bunch of information about this machine in
+ # machine-readable format in case someone is listening.
+ @ui.machine("metadata", "provider", provider_name)
end
# This calls an action on the provider. The provider may or may not
@@ -188,7 +195,10 @@ module Vagrant
end
# Call the action
- action_raw(name, callable, extra_env)
+ ui.machine("action", name.to_s, "start")
+ action_result = action_raw(name, callable, extra_env)
+ ui.machine("action", name.to_s, "end")
+ action_result
end
rescue Errors::EnvironmentLockedError
raise Errors::MachineActionLockedError,
@@ -436,6 +446,7 @@ module Vagrant
# We also set some fields that are purely controlled by Varant
info[:forward_agent] = @config.ssh.forward_agent
info[:forward_x11] = @config.ssh.forward_x11
+ info[:forward_env] = @config.ssh.forward_env
info[:ssh_command] = @config.ssh.ssh_command if @config.ssh.ssh_command
diff --git a/lib/vagrant/machine_index.rb b/lib/vagrant/machine_index.rb
index 6e8fe0e3a..1af574b33 100644
--- a/lib/vagrant/machine_index.rb
+++ b/lib/vagrant/machine_index.rb
@@ -78,7 +78,7 @@ module Vagrant
@machines.delete(entry.id)
unlocked_save
- # Release acccess on this machine
+ # Release access on this machine
unlocked_release(entry.id)
end
end
diff --git a/lib/vagrant/plugin/v2/command.rb b/lib/vagrant/plugin/v2/command.rb
index eeb580a29..f0b6c4415 100644
--- a/lib/vagrant/plugin/v2/command.rb
+++ b/lib/vagrant/plugin/v2/command.rb
@@ -133,8 +133,17 @@ module Vagrant
# machine in that environment. We silence warnings here because
# Vagrantfiles often have constants, so people would otherwise
# constantly (heh) get "already initialized constant" warnings.
- env = entry.vagrant_env(
- @env.home_path, ui_class: @env.ui_class)
+ begin
+ env = entry.vagrant_env(
+ @env.home_path, ui_class: @env.ui_class)
+ rescue Vagrant::Errors::EnvironmentNonExistentCWD
+ # This means that this environment working directory
+ # no longer exists, so delete this entry.
+ entry = @env.machine_index.get(name.to_s)
+ @env.machine_index.delete(entry) if entry
+ raise
+ end
+
next env.machine(entry.name.to_sym, entry.provider.to_sym)
end
diff --git a/lib/vagrant/ui.rb b/lib/vagrant/ui.rb
index c610643a9..75802352c 100644
--- a/lib/vagrant/ui.rb
+++ b/lib/vagrant/ui.rb
@@ -104,8 +104,8 @@ module Vagrant
end
[:detail, :warn, :error, :info, :output, :success].each do |method|
- define_method(method) do |message, *opts|
- machine("ui", method.to_s, message)
+ define_method(method) do |message, *args, **opts|
+ machine("ui", method.to_s, message, *args, **opts)
end
end
@@ -123,9 +123,12 @@ module Vagrant
data[i].gsub!("\r", "\\r")
end
- @lock.synchronize do
- safe_puts("#{Time.now.utc.to_i},#{target},#{type},#{data.join(",")}")
- end
+ # Avoid locks in a trap context introduced from Ruby 2.0
+ Thread.new do
+ @lock.synchronize do
+ safe_puts("#{Time.now.utc.to_i},#{target},#{type},#{data.join(",")}")
+ end
+ end.join
end
end
@@ -156,7 +159,7 @@ module Vagrant
super(message)
# We can't ask questions when the output isn't a TTY.
- raise Errors::UIExpectsTTY if !@stdin.tty? && !Vagrant::Util::Platform.cygwin?
+ raise Errors::UIExpectsTTY if !@stdin.tty? && !Vagrant::Util::Platform.windows?
# Setup the options so that the new line is suppressed
opts ||= {}
@@ -277,6 +280,9 @@ module Vagrant
opts[:bold] = #{method.inspect} != :detail && \
#{method.inspect} != :ask
end
+ if !opts.key?(:target)
+ opts[:target] = @prefix
+ end
@ui.#{method}(format_message(#{method.inspect}, message, **opts), *args, **opts)
end
CODE
diff --git a/lib/vagrant/util/downloader.rb b/lib/vagrant/util/downloader.rb
index edaf99e85..03e623897 100644
--- a/lib/vagrant/util/downloader.rb
+++ b/lib/vagrant/util/downloader.rb
@@ -138,11 +138,14 @@ module Vagrant
# If we already retried, raise it.
raise if retried
+ @logger.error("Exit code: #{e.extra_data[:code]}")
+
# If its any error other than 33, it is an error.
- raise if e.extra_data[:exit_code].to_i != 33
+ raise if e.extra_data[:code].to_i != 33
# Exit code 33 means that the server doesn't support ranges.
# In this case, try again without resume.
+ @logger.error("Error is server doesn't support byte ranges. Retrying from scratch.")
@continue = false
retried = true
retry
diff --git a/lib/vagrant/util/platform.rb b/lib/vagrant/util/platform.rb
index 706445c3b..017a6e558 100644
--- a/lib/vagrant/util/platform.rb
+++ b/lib/vagrant/util/platform.rb
@@ -10,10 +10,17 @@ module Vagrant
class Platform
class << self
def cygwin?
+ # Installer detects Cygwin
return true if ENV["VAGRANT_DETECTED_OS"] &&
ENV["VAGRANT_DETECTED_OS"].downcase.include?("cygwin")
- platform.include?("cygwin")
+ # Ruby running in Cygwin
+ return true if platform.include?("cygwin")
+
+ # Heuristic. If the path contains Cygwin, we just assume we're
+ # in Cygwin. It is generally a safe bet.
+ path = ENV["PATH"] || ""
+ return path.include?("cygwin")
end
[:darwin, :bsd, :freebsd, :linux, :solaris].each do |type|
@@ -45,10 +52,32 @@ module Vagrant
# detect-if-running-with-administrator-privileges-under-windows-xp
begin
Win32::Registry::HKEY_USERS.open("S-1-5-19") {}
- return true
rescue Win32::Registry::Error
return false
end
+
+ # If we made it this far then we try a fallback approach
+ # since the above doesn't seem to be bullet proof. See GH-5616
+ (`reg query HKU\\S-1-5-19 2>&1` =~ /ERROR/).nil?
+ end
+
+ # Checks if the user running Vagrant on Windows is a member of the
+ # "Hyper-V Administrators" group.
+ #
+ # From: https://support.microsoft.com/en-us/kb/243330
+ # SID: S-1-5-32-578
+ # Name: BUILTIN\Hyper-V Administrators
+ #
+ # @return [Boolean]
+ def windows_hyperv_admin?
+ begin
+ username = ENV["USERNAME"]
+ process = Subprocess.execute("net", "localgroup", "Hyper-V Administrators")
+ output = process.stdout.chomp
+ return output.include?(username)
+ rescue Errors::CommandUnavailableWindows
+ return false
+ end
end
# This takes any path and converts it from a Windows path to a
@@ -114,6 +143,14 @@ module Vagrant
path = Pathname.new(File.expand_path(path))
if path.exist? && !fs_case_sensitive?
+ # If the path contains a Windows short path, then we attempt to
+ # expand. The require below is embedded here since it requires
+ # windows to work.
+ if windows? && path.to_s =~ /~\d(\/|\\)/
+ require_relative "windows_path"
+ path = Pathname.new(WindowsPath.longname(path.to_s))
+ end
+
# Build up all the parts of the path
original = []
while !path.root?
diff --git a/lib/vagrant/util/presence.rb b/lib/vagrant/util/presence.rb
new file mode 100644
index 000000000..8032bf6c1
--- /dev/null
+++ b/lib/vagrant/util/presence.rb
@@ -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
diff --git a/lib/vagrant/util/safe_env.rb b/lib/vagrant/util/safe_env.rb
new file mode 100644
index 000000000..8d7d8baf1
--- /dev/null
+++ b/lib/vagrant/util/safe_env.rb
@@ -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
diff --git a/lib/vagrant/util/ssh.rb b/lib/vagrant/util/ssh.rb
index 20afcdb04..f5d6b916d 100644
--- a/lib/vagrant/util/ssh.rb
+++ b/lib/vagrant/util/ssh.rb
@@ -138,6 +138,10 @@ module Vagrant
command_options += ["-o", "ProxyCommand=#{ssh_info[:proxy_command]}"]
end
+ if ssh_info[:forward_env]
+ command_options += ["-o", "SendEnv=#{ssh_info[:forward_env].join(" ")}"]
+ end
+
# Configurables -- extra_args should always be last due to the way the
# ssh args parser works. e.g. if the user wants to use the -t option,
# any shell command(s) she'd like to run on the remote server would
@@ -172,6 +176,14 @@ module Vagrant
LOGGER.info("Executing SSH in subprocess: #{ssh} #{command_options.inspect}")
process = ChildProcess.build(ssh, *command_options)
process.io.inherit!
+
+ # Forward configured environment variables.
+ if ssh_info[:forward_env]
+ ssh_info[:forward_env].each do |key|
+ process.environment[key] = ENV[key]
+ end
+ end
+
process.start
process.wait
return process.exit_code
diff --git a/lib/vagrant/util/windows_path.rb b/lib/vagrant/util/windows_path.rb
new file mode 100644
index 000000000..d2ea251d5
--- /dev/null
+++ b/lib/vagrant/util/windows_path.rb
@@ -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
diff --git a/plugins/commands/box/command/add.rb b/plugins/commands/box/command/add.rb
index 114601024..5545ea215 100644
--- a/plugins/commands/box/command/add.rb
+++ b/plugins/commands/box/command/add.rb
@@ -1,9 +1,13 @@
require 'optparse'
+require_relative 'download_mixins'
+
module VagrantPlugins
module CommandBox
module Command
class Add < Vagrant.plugin("2", :command)
+ include DownloadMixins
+
def execute
options = {}
@@ -21,22 +25,7 @@ module VagrantPlugins
options[:force] = f
end
- o.on("--insecure", "Do not validate SSL certificates") do |i|
- options[:insecure] = i
- end
-
- o.on("--cacert FILE", String, "CA certificate for SSL download") do |c|
- options[:ca_cert] = c
- end
-
- o.on("--capath DIR", String, "CA certificate directory for SSL download") do |c|
- options[:ca_path] = c
- end
-
- o.on("--cert FILE", String,
- "A client SSL cert, if needed") do |c|
- options[:client_cert] = c
- end
+ build_download_options(o, options)
o.on("--location-trusted", "Trust 'Location' header from HTTP redirects and use the same credentials for subsequent urls as for the initial one") do |l|
options[:location_trusted] = l
@@ -97,7 +86,7 @@ module VagrantPlugins
box_force: options[:force],
box_download_ca_cert: options[:ca_cert],
box_download_ca_path: options[:ca_path],
- box_download_client_cert: options[:client_cert],
+ box_client_cert: options[:client_cert],
box_download_insecure: options[:insecure],
box_download_location_trusted: options[:location_trusted],
ui: Vagrant::UI::Prefixed.new(@env.ui, "box"),
diff --git a/plugins/commands/box/command/download_mixins.rb b/plugins/commands/box/command/download_mixins.rb
new file mode 100644
index 000000000..eda31b6bf
--- /dev/null
+++ b/plugins/commands/box/command/download_mixins.rb
@@ -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
diff --git a/plugins/commands/box/command/outdated.rb b/plugins/commands/box/command/outdated.rb
index a686fa496..4fd6e5cf3 100644
--- a/plugins/commands/box/command/outdated.rb
+++ b/plugins/commands/box/command/outdated.rb
@@ -1,11 +1,16 @@
require 'optparse'
+require_relative 'download_mixins'
+
module VagrantPlugins
module CommandBox
module Command
class Outdated < Vagrant.plugin("2", :command)
+ include DownloadMixins
+
def execute
options = {}
+ download_options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant box outdated [options]"
@@ -20,6 +25,8 @@ module VagrantPlugins
o.on("--global", "Check all boxes installed") do |g|
options[:global] = g
end
+
+ build_download_options(o, download_options)
end
argv = parse_options(opts)
@@ -27,7 +34,7 @@ module VagrantPlugins
# If we're checking the boxes globally, then do that.
if options[:global]
- outdated_global
+ outdated_global(download_options)
return 0
end
@@ -37,11 +44,11 @@ module VagrantPlugins
box_outdated_refresh: true,
box_outdated_success_ui: true,
machine: machine,
- })
+ }.merge(download_options))
end
end
- def outdated_global
+ def outdated_global(download_options)
boxes = {}
@env.boxes.all.reverse.each do |name, version, provider|
next if boxes[name]
@@ -58,8 +65,8 @@ module VagrantPlugins
md = nil
begin
- md = box.load_metadata
- rescue Vagrant::Errors::DownloaderError => e
+ md = box.load_metadata(download_options)
+ rescue Vagrant::Errors::BoxMetadataDownloadError => e
@env.ui.error(I18n.t(
"vagrant.box_outdated_metadata_error",
name: box.name,
diff --git a/plugins/commands/box/command/update.rb b/plugins/commands/box/command/update.rb
index 5633b0aaf..67d07bcc0 100644
--- a/plugins/commands/box/command/update.rb
+++ b/plugins/commands/box/command/update.rb
@@ -1,11 +1,16 @@
require 'optparse'
+require_relative 'download_mixins'
+
module VagrantPlugins
module CommandBox
module Command
class Update < Vagrant.plugin("2", :command)
+ include DownloadMixins
+
def execute
options = {}
+ download_options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant box update [options]"
@@ -27,21 +32,23 @@ module VagrantPlugins
o.on("--provider PROVIDER", String, "Update box with specific provider") do |p|
options[:provider] = p.to_sym
end
+
+ build_download_options(o, download_options)
end
argv = parse_options(opts)
return if !argv
if options[:box]
- update_specific(options[:box], options[:provider])
+ update_specific(options[:box], options[:provider], download_options)
else
- update_vms(argv, options[:provider])
+ update_vms(argv, options[:provider], download_options)
end
0
end
- def update_specific(name, provider)
+ def update_specific(name, provider, download_options)
boxes = {}
@env.boxes.all.each do |n, v, p|
boxes[n] ||= {}
@@ -74,11 +81,11 @@ module VagrantPlugins
to_update.each do |n, p, v|
box = @env.boxes.find(n, p, v)
- box_update(box, "> #{v}", @env.ui)
+ box_update(box, "> #{v}", @env.ui, download_options)
end
end
- def update_vms(argv, provider)
+ def update_vms(argv, provider, download_options)
with_target_vms(argv, provider: provider) do |machine|
if !machine.config.vm.box
machine.ui.output(I18n.t(
@@ -95,17 +102,25 @@ module VagrantPlugins
box = machine.box
version = machine.config.vm.box_version
- box_update(box, version, machine.ui)
+ # Get download options from machine configuration if not specified
+ # on the command line.
+ download_options[:ca_cert] ||= machine.config.vm.box_download_ca_cert
+ download_options[:ca_path] ||= machine.config.vm.box_download_ca_path
+ download_options[:client_cert] ||= machine.config.vm.box_download_client_cert
+ if download_options[:insecure].nil?
+ download_options[:insecure] = machine.config.vm.box_download_insecure
+ end
+ box_update(box, version, machine.ui, download_options)
end
end
- def box_update(box, version, ui)
+ def box_update(box, version, ui, download_options)
ui.output(I18n.t("vagrant.box_update_checking", name: box.name))
ui.detail("Latest installed version: #{box.version}")
ui.detail("Version constraints: #{version}")
ui.detail("Provider: #{box.provider}")
- update = box.has_update?(version)
+ update = box.has_update?(version, download_options: download_options)
if !update
ui.success(I18n.t(
"vagrant.box_up_to_date_single",
@@ -124,6 +139,10 @@ module VagrantPlugins
box_provider: update[2].name,
box_version: update[1].version,
ui: ui,
+ box_client_cert: download_options[:client_cert],
+ box_download_ca_cert: download_options[:ca_cert],
+ box_download_ca_path: download_options[:ca_path],
+ box_download_insecure: download_options[:insecure]
})
end
end
diff --git a/plugins/commands/login/client.rb b/plugins/commands/login/client.rb
index 63b01f335..fe8811935 100644
--- a/plugins/commands/login/client.rb
+++ b/plugins/commands/login/client.rb
@@ -1,4 +1,5 @@
require "rest_client"
+require "vagrant/util/downloader"
module VagrantPlugins
module LoginCommand
@@ -45,8 +46,23 @@ module VagrantPlugins
with_error_handling do
url = "#{Vagrant.server_url}/api/v1/authenticate"
request = { "user" => { "login" => user, "password" => pass } }
- response = RestClient.post(
- url, JSON.dump(request), content_type: :json)
+
+ proxy = nil
+ proxy ||= ENV["HTTPS_PROXY"] || ENV["https_proxy"]
+ proxy ||= ENV["HTTP_PROXY"] || ENV["http_proxy"]
+
+ response = RestClient::Request.execute(
+ method: :post,
+ url: url,
+ payload: JSON.dump(request),
+ proxy: proxy,
+ headers: {
+ accept: :json,
+ content_type: :json,
+ user_agent: Vagrant::Util::Downloader::USER_AGENT,
+ },
+ )
+
data = JSON.load(response.to_s)
data["token"]
end
diff --git a/plugins/commands/login/middleware/add_authentication.rb b/plugins/commands/login/middleware/add_authentication.rb
index faaa3cfe7..f176e3480 100644
--- a/plugins/commands/login/middleware/add_authentication.rb
+++ b/plugins/commands/login/middleware/add_authentication.rb
@@ -1,3 +1,4 @@
+require "cgi"
require "uri"
require_relative "../client"
@@ -5,6 +6,9 @@ require_relative "../client"
module VagrantPlugins
module LoginCommand
class AddAuthentication
+ VCLOUD = "vagrantcloud.com".freeze
+ ATLAS = "atlas.hashicorp.com".freeze
+
def initialize(app, env)
@app = app
end
@@ -19,19 +23,26 @@ module VagrantPlugins
env[:box_urls].map! do |url|
u = URI.parse(url)
replace = u.host == server_uri.host
+
if !replace
# We need this in here for the transition we made from
# Vagrant Cloud to Atlas. This preserves access tokens
# appending to both without leaking access tokens to
# unsavory URLs.
- replace = u.host == "vagrantcloud.com" &&
- server_uri.host == "atlas.hashicorp.com"
+ if u.host == VCLOUD && server_uri.host == ATLAS
+ replace = true
+ end
end
if replace
- u.query ||= ""
- u.query += "&" if u.query != ""
- u.query += "access_token=#{token}"
+ q = CGI.parse(u.query || "")
+
+ current = q["access_token"]
+ if current && current.empty?
+ q["access_token"] = token
+ end
+
+ u.query = URI.encode_www_form(q)
end
u.to_s
diff --git a/plugins/commands/plugin/command/mixin_install_opts.rb b/plugins/commands/plugin/command/mixin_install_opts.rb
index 0b1b0973a..004fd10b0 100644
--- a/plugins/commands/plugin/command/mixin_install_opts.rb
+++ b/plugins/commands/plugin/command/mixin_install_opts.rb
@@ -3,6 +3,11 @@ module VagrantPlugins
module Command
module MixinInstallOpts
def build_install_opts(o, options)
+ options[:plugin_sources] = [
+ "https://rubygems.org",
+ "http://gems.hashicorp.com",
+ ]
+
o.on("--entry-point NAME", String,
"The name of the entry point file for loading the plugin.") do |entry_point|
options[:entry_point] = entry_point
@@ -17,9 +22,13 @@ module VagrantPlugins
puts
end
+ o.on("--plugin-clean-sources",
+ "Remove all plugin sources defined so far (including defaults)") do |clean|
+ options[:plugin_sources] = [] if clean
+ end
+
o.on("--plugin-source PLUGIN_SOURCE", String,
"Add a RubyGems repository source") do |plugin_source|
- options[:plugin_sources] ||= []
options[:plugin_sources] << plugin_source
end
diff --git a/plugins/commands/port/command.rb b/plugins/commands/port/command.rb
new file mode 100644
index 000000000..af35def4b
--- /dev/null
+++ b/plugins/commands/port/command.rb
@@ -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
diff --git a/plugins/commands/port/locales/en.yml b/plugins/commands/port/locales/en.yml
new file mode 100644
index 000000000..ba1389db7
--- /dev/null
+++ b/plugins/commands/port/locales/en.yml
@@ -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.
diff --git a/plugins/commands/port/plugin.rb b/plugins/commands/port/plugin.rb
new file mode 100644
index 000000000..7437f0436
--- /dev/null
+++ b/plugins/commands/port/plugin.rb
@@ -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
diff --git a/plugins/commands/powershell/command.rb b/plugins/commands/powershell/command.rb
new file mode 100644
index 000000000..836c22a02
--- /dev/null
+++ b/plugins/commands/powershell/command.rb
@@ -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
diff --git a/plugins/commands/powershell/errors.rb b/plugins/commands/powershell/errors.rb
new file mode 100644
index 000000000..4be70551a
--- /dev/null
+++ b/plugins/commands/powershell/errors.rb
@@ -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
diff --git a/plugins/commands/powershell/plugin.rb b/plugins/commands/powershell/plugin.rb
new file mode 100644
index 000000000..325925115
--- /dev/null
+++ b/plugins/commands/powershell/plugin.rb
@@ -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
diff --git a/plugins/commands/powershell/scripts/enable_psremoting.ps1 b/plugins/commands/powershell/scripts/enable_psremoting.ps1
new file mode 100644
index 000000000..4d7ded704
--- /dev/null
+++ b/plugins/commands/powershell/scripts/enable_psremoting.ps1
@@ -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)
diff --git a/plugins/commands/powershell/scripts/reset_trustedhosts.ps1 b/plugins/commands/powershell/scripts/reset_trustedhosts.ps1
new file mode 100644
index 000000000..5865a4e77
--- /dev/null
+++ b/plugins/commands/powershell/scripts/reset_trustedhosts.ps1
@@ -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
\ No newline at end of file
diff --git a/plugins/commands/provision/command.rb b/plugins/commands/provision/command.rb
index a67084f75..0dc5bdf00 100644
--- a/plugins/commands/provision/command.rb
+++ b/plugins/commands/provision/command.rb
@@ -15,7 +15,7 @@ module VagrantPlugins
o.banner = "Usage: vagrant provision [vm-name] [--provision-with x,y,z]"
o.on("--provision-with x,y,z", Array,
- "Enable only certain provisioners, by type.") do |list|
+ "Enable only certain provisioners, by type or by name.") do |list|
options[:provision_types] = list.map { |type| type.to_sym }
end
end
diff --git a/plugins/commands/reload/command.rb b/plugins/commands/reload/command.rb
index 33a6f8e5e..1d43d13b7 100644
--- a/plugins/commands/reload/command.rb
+++ b/plugins/commands/reload/command.rb
@@ -30,7 +30,7 @@ module VagrantPlugins
return if !argv
# Validate the provisioners
- validate_provisioner_flags!(options)
+ validate_provisioner_flags!(options, argv)
@logger.debug("'reload' each target VM...")
machines = []
diff --git a/plugins/commands/ssh_config/command.rb b/plugins/commands/ssh_config/command.rb
index 6013a90b7..320c8ace5 100644
--- a/plugins/commands/ssh_config/command.rb
+++ b/plugins/commands/ssh_config/command.rb
@@ -41,12 +41,15 @@ module VagrantPlugins
forward_agent: ssh_info[:forward_agent],
forward_x11: ssh_info[:forward_x11],
proxy_command: ssh_info[:proxy_command],
- ssh_command: ssh_info[:ssh_command]
+ ssh_command: ssh_info[:ssh_command],
+ forward_env: ssh_info[:forward_env],
}
# Render the template and output directly to STDOUT
template = "commands/ssh_config/config"
- safe_puts(Vagrant::Util::TemplateRenderer.render(template, variables))
+ config = Vagrant::Util::TemplateRenderer.render(template, variables)
+ machine.ui.machine("ssh-config", config)
+ safe_puts(config)
safe_puts
end
diff --git a/plugins/commands/up/command.rb b/plugins/commands/up/command.rb
index b93e859cf..7573a088a 100644
--- a/plugins/commands/up/command.rb
+++ b/plugins/commands/up/command.rb
@@ -55,7 +55,7 @@ module VagrantPlugins
return if !argv
# Validate the provisioners
- validate_provisioner_flags!(options)
+ validate_provisioner_flags!(options, argv)
# Go over each VM and bring it up
@logger.debug("'Up' each target VM...")
diff --git a/plugins/commands/up/start_mixins.rb b/plugins/commands/up/start_mixins.rb
index 548d889ca..bb397346c 100644
--- a/plugins/commands/up/start_mixins.rb
+++ b/plugins/commands/up/start_mixins.rb
@@ -1,3 +1,5 @@
+require "set"
+
module VagrantPlugins
module CommandUp
module StartMixins
@@ -17,7 +19,7 @@ module VagrantPlugins
end
parser.on("--provision-with x,y,z", Array,
- "Enable only certain provisioners, by type.") do |list|
+ "Enable only certain provisioners, by type or by name.") do |list|
options[:provision_types] = list.map { |type| type.to_sym }
options[:provision_enabled] = true
options[:provision_ignore_sentinel] = true
@@ -26,13 +28,26 @@ module VagrantPlugins
# This validates the provisioner flags and raises an exception
# if there are invalid ones.
- def validate_provisioner_flags!(options)
- (options[:provision_types] || []).each do |type|
- klass = Vagrant.plugin("2").manager.provisioners[type]
- if !klass
- raise Vagrant::Errors::ProvisionerFlagInvalid,
- name: type.to_s
- end
+ def validate_provisioner_flags!(options, argv)
+ if options[:provision_types].nil?
+ return
+ end
+
+ provisioner_names = Set.new
+ with_target_vms(argv) do |machine|
+ machine.config.vm.provisioners.map(&:name).each do |name|
+ provisioner_names.add(name)
+ end
+ end
+
+ if (provisioner_names & options[:provision_types]).empty?
+ (options[:provision_types] || []).each do |type|
+ klass = Vagrant.plugin("2").manager.provisioners[type]
+ if !klass
+ raise Vagrant::Errors::ProvisionerFlagInvalid,
+ name: type.to_s
+ end
+ end
end
end
end
diff --git a/plugins/communicators/ssh/communicator.rb b/plugins/communicators/ssh/communicator.rb
index 8297329f9..a9190f569 100644
--- a/plugins/communicators/ssh/communicator.rb
+++ b/plugins/communicators/ssh/communicator.rb
@@ -333,6 +333,7 @@ module VagrantPlugins
auth_methods: auth_methods,
config: false,
forward_agent: ssh_info[:forward_agent],
+ send_env: ssh_info[:forward_env],
keys: ssh_info[:private_key_path],
keys_only: true,
paranoid: false,
@@ -420,7 +421,7 @@ module VagrantPlugins
rescue Errno::EHOSTDOWN
# This is raised if we get an ICMP DestinationUnknown error.
raise Vagrant::Errors::SSHHostDown
- rescue Errno::EHOSTUNREACH
+ rescue Errno::EHOSTUNREACH, Errno::ENETUNREACH
# This is raised if we can't work out how to route traffic.
raise Vagrant::Errors::SSHNoRoute
rescue Net::SSH::Exception => e
@@ -611,6 +612,7 @@ module VagrantPlugins
end
data = pty_stdout[/.*#{PTY_DELIM_START}(.*?)#{PTY_DELIM_END}/m, 1]
+ data ||= ""
@logger.debug("PTY stdout parsed: #{data}")
yield :stdout, data if block_given?
end
diff --git a/plugins/communicators/winrm/communicator.rb b/plugins/communicators/winrm/communicator.rb
index b14b28efa..e22a4f9b0 100644
--- a/plugins/communicators/winrm/communicator.rb
+++ b/plugins/communicators/winrm/communicator.rb
@@ -43,6 +43,7 @@ module VagrantPlugins
# Got it! Let the user know what we're connecting to.
@machine.ui.detail("WinRM address: #{shell.host}:#{shell.port}")
@machine.ui.detail("WinRM username: #{shell.username}")
+ @machine.ui.detail("WinRM execution_time_limit: #{shell.execution_time_limit}")
@machine.ui.detail("WinRM transport: #{shell.config.transport}")
last_message = nil
@@ -136,10 +137,11 @@ module VagrantPlugins
error_key: nil, # use the error_class message key
good_exit: 0,
shell: :powershell,
+ interactive: false,
}.merge(opts || {})
opts[:good_exit] = Array(opts[:good_exit])
- command = wrap_in_scheduled_task(command) if opts[:elevated]
+ command = wrap_in_scheduled_task(command, opts[:interactive]) if opts[:elevated]
output = shell.send(opts[:shell], command, &block)
execution_output(output, opts)
end
@@ -193,9 +195,11 @@ module VagrantPlugins
# in place.
#
# @return The wrapper command to execute
- def wrap_in_scheduled_task(command)
+ def wrap_in_scheduled_task(command, interactive)
path = File.expand_path("../scripts/elevated_shell.ps1", __FILE__)
- script = Vagrant::Util::TemplateRenderer.render(path)
+ script = Vagrant::Util::TemplateRenderer.render(path, options: {
+ interactive: interactive,
+ })
guest_script_path = "c:/tmp/vagrant-elevated-shell.ps1"
file = Tempfile.new(["vagrant-elevated-shell", "ps1"])
begin
@@ -208,14 +212,16 @@ module VagrantPlugins
file.unlink
end
- # convert to double byte unicode string then base64 encode
- # just like PowerShell -EncodedCommand expects
+ # Convert to double byte unicode string then base64 encode
+ # just like PowerShell -EncodedCommand expects.
+ # Suppress the progress stream from leaking to stderr.
wrapped_encoded_command = Base64.strict_encode64(
- "#{command}; exit $LASTEXITCODE".encode('UTF-16LE', 'UTF-8'))
+ "$ProgressPreference='SilentlyContinue'; #{command}; exit $LASTEXITCODE".encode('UTF-16LE', 'UTF-8'))
- "powershell -executionpolicy bypass -file \"#{guest_script_path}\" " +
- "-username \"#{shell.username}\" -password \"#{shell.password}\" " +
- "-encoded_command \"#{wrapped_encoded_command}\""
+ "powershell -executionpolicy bypass -file '#{guest_script_path}' " +
+ "-username '#{shell.username}' -password '#{shell.password}' " +
+ "-encoded_command '#{wrapped_encoded_command}' " +
+ "-execution_time_limit '#{shell.execution_time_limit}'"
end
# Handles the raw WinRM shell result and converts it to a
diff --git a/plugins/communicators/winrm/config.rb b/plugins/communicators/winrm/config.rb
index 79901d503..3387ca142 100644
--- a/plugins/communicators/winrm/config.rb
+++ b/plugins/communicators/winrm/config.rb
@@ -11,6 +11,7 @@ module VagrantPlugins
attr_accessor :timeout
attr_accessor :transport
attr_accessor :ssl_peer_verification
+ attr_accessor :execution_time_limit
def initialize
@username = UNSET_VALUE
@@ -23,6 +24,7 @@ module VagrantPlugins
@timeout = UNSET_VALUE
@transport = UNSET_VALUE
@ssl_peer_verification = UNSET_VALUE
+ @execution_time_limit = UNSET_VALUE
end
def finalize!
@@ -37,6 +39,7 @@ module VagrantPlugins
@retry_delay = 2 if @retry_delay == UNSET_VALUE
@timeout = 1800 if @timeout == UNSET_VALUE
@ssl_peer_verification = true if @ssl_peer_verification == UNSET_VALUE
+ @execution_time_limit = "PT2H" if @execution_time_limit == UNSET_VALUE
end
def validate(machine)
@@ -49,6 +52,7 @@ module VagrantPlugins
errors << "winrm.max_tries cannot be nil." if @max_tries.nil?
errors << "winrm.retry_delay cannot be nil." if @max_tries.nil?
errors << "winrm.timeout cannot be nil." if @timeout.nil?
+ errors << "winrm.execution_time_limit cannot be nil." if @execution_time_limit.nil?
unless @ssl_peer_verification == true || @ssl_peer_verification == false
errors << "winrm.ssl_peer_verification must be a boolean."
end
diff --git a/plugins/communicators/winrm/scripts/elevated_shell.ps1.erb b/plugins/communicators/winrm/scripts/elevated_shell.ps1.erb
index 17767e436..76b260b78 100644
--- a/plugins/communicators/winrm/scripts/elevated_shell.ps1.erb
+++ b/plugins/communicators/winrm/scripts/elevated_shell.ps1.erb
@@ -1,5 +1,17 @@
-param([String]$username, [String]$password, [String]$encoded_command)
+param([String]$username, [String]$password, [String]$encoded_command, [String]$execution_time_limit)
+# Try to get the Schedule.Service object. If it fails, we are probably
+# on an older version of Windows. On old versions, we can just execute
+# directly since priv. escalation isn't a thing.
+$schedule = $null
+Try {
+ $schedule = New-Object -ComObject "Schedule.Service"
+} Catch [System.Management.Automation.PSArgumentException] {
+ powershell.exe -EncodedCommand $encoded_command
+ exit $LASTEXITCODE
+}
+
+$ProgressPreference = "SilentlyContinue"
$task_name = "WinRM_Elevated_Shell"
$out_file = "$env:SystemRoot\Temp\WinRM_Elevated_Shell.log"
@@ -13,7 +25,7 @@ $task_xml = @'
{username}
- Password
+ <%= options[:interactive] ? 'InteractiveTokenOrPassword' : 'Password' %>
HighestAvailable
@@ -33,7 +45,7 @@ $task_xml = @'
false
false
false
- PT2H
+ {execution_time_limit}
4
@@ -49,13 +61,13 @@ $arguments = "/c powershell.exe -EncodedCommand $encoded_command > $out_file
$task_xml = $task_xml.Replace("{arguments}", $arguments)
$task_xml = $task_xml.Replace("{username}", $username)
+$task_xml = $task_xml.Replace("{execution_time_limit}", $execution_time_limit)
-$schedule = New-Object -ComObject "Schedule.Service"
$schedule.Connect()
$task = $schedule.NewTask($null)
$task.XmlText = $task_xml
$folder = $schedule.GetFolder("\")
-$folder.RegisterTaskDefinition($task_name, $task, 6, $username, $password, 1, $null) | Out-Null
+$folder.RegisterTaskDefinition($task_name, $task, 6, $username, $password, <%= options[:interactive] ? 3 : 1 %>, $null) | Out-Null
$registered_task = $folder.GetTask("\$task_name")
$registered_task.Run($null) | Out-Null
@@ -71,7 +83,7 @@ function SlurpOutput($out_file, $cur_line) {
if (Test-Path $out_file) {
get-content $out_file | select -skip $cur_line | ForEach {
$cur_line += 1
- Write-Host "$_"
+ Write-Host "$_"
}
}
return $cur_line
diff --git a/plugins/communicators/winrm/shell.rb b/plugins/communicators/winrm/shell.rb
index 9b0f4e302..f8a50a3a7 100644
--- a/plugins/communicators/winrm/shell.rb
+++ b/plugins/communicators/winrm/shell.rb
@@ -9,7 +9,7 @@ Vagrant::Util::SilenceWarnings.silence! do
require "winrm"
end
-require "winrm-fs/file_manager"
+require "winrm-fs"
module VagrantPlugins
module CommunicatorWinRM
@@ -23,6 +23,7 @@ module VagrantPlugins
HTTPClient::KeepAliveDisconnected,
WinRM::WinRMHTTPTransportError,
WinRM::WinRMAuthorizationError,
+ WinRM::WinRMWSManFault,
Errno::EACCES,
Errno::EADDRINUSE,
Errno::ECONNREFUSED,
@@ -37,6 +38,7 @@ module VagrantPlugins
attr_reader :port
attr_reader :username
attr_reader :password
+ attr_reader :execution_time_limit
attr_reader :config
def initialize(host, port, config)
@@ -47,12 +49,15 @@ module VagrantPlugins
@port = port
@username = config.username
@password = config.password
+ @execution_time_limit = config.execution_time_limit
@config = config
end
def powershell(command, &block)
- # ensure an exit code
+ # Suppress the progress stream from leaking to stderr
+ command = "$ProgressPreference='SilentlyContinue';\r\n" + command
command << "\r\n"
+ # Ensure an exit code
command << "if ($?) { exit 0 } else { if($LASTEXITCODE) { exit $LASTEXITCODE } else { exit 1 } }"
execute_shell(command, :powershell, &block)
end
diff --git a/plugins/guests/arch/cap/configure_networks.rb b/plugins/guests/arch/cap/configure_networks.rb
index fd06c24ea..1b95469fc 100644
--- a/plugins/guests/arch/cap/configure_networks.rb
+++ b/plugins/guests/arch/cap/configure_networks.rb
@@ -27,7 +27,7 @@ module VagrantPlugins
machine.communicate.upload(temp.path, "/tmp/vagrant_network")
machine.communicate.sudo("mv /tmp/vagrant_network /etc/netctl/#{network[:device]}")
- machine.communicate.sudo("ip link set #{network[:device]} down && netctl start #{network[:device]}")
+ machine.communicate.sudo("ip link set #{network[:device]} down && netctl start #{network[:device]} && netctl enable #{network[:device]}")
end
end
end
diff --git a/plugins/guests/darwin/cap/configure_networks.rb b/plugins/guests/darwin/cap/configure_networks.rb
index fefd0a2e2..1be71c82f 100644
--- a/plugins/guests/darwin/cap/configure_networks.rb
+++ b/plugins/guests/darwin/cap/configure_networks.rb
@@ -6,45 +6,109 @@ module VagrantPlugins
module GuestDarwin
module Cap
class ConfigureNetworks
+ @@logger = Log4r::Logger.new("vagrant::guest::darwin::configure_networks")
+
include Vagrant::Util
def self.configure_networks(machine, networks)
- # Slightly different than other plugins, using the template to build commands
- # rather than templating the files.
+ if !machine.provider.capability?(:nic_mac_addresses)
+ raise Vagrant::Errors::CantReadMACAddresses,
+ provider: machine.provider_name.to_s
+ end
- machine.communicate.sudo("networksetup -detectnewhardware")
- machine.communicate.sudo("networksetup -listnetworkserviceorder > /tmp/vagrant.interfaces")
- tmpints = File.join(Dir.tmpdir, File.basename("#{machine.id}.interfaces"))
- machine.communicate.download("/tmp/vagrant.interfaces",tmpints)
+ nic_mac_addresses = machine.provider.capability(:nic_mac_addresses)
+ @@logger.debug("mac addresses: #{nic_mac_addresses.inspect}")
- devlist = []
- ints = ::IO.read(tmpints)
+ mac_service_map = create_mac_service_map(machine)
+
+ networks.each do |network|
+ mac_address = nic_mac_addresses[network[:interface]+1]
+ if mac_address.nil?
+ @@logger.warn("Could not find mac address for network #{network.inspect}")
+ next
+ end
+
+ service_name = mac_service_map[mac_address]
+ if service_name.nil?
+ @@logger.warn("Could not find network service for mac address #{mac_address}")
+ next
+ end
+
+ network_type = network[:type].to_sym
+ if network_type == :static
+ command = "networksetup -setmanual \"#{service_name}\" #{network[:ip]} #{network[:netmask]}"
+ elsif network_type == :dhcp
+ command = "networksetup -setdhcp \"#{service_name}\""
+ else
+ raise "#{network_type} network type is not supported, try static or dhcp"
+ end
+
+ machine.communicate.sudo(command)
+ end
+ end
+
+ # Creates a hash mapping MAC addresses to network service name
+ # Example: { "00C100A1B2C3" => "Thunderbolt Ethernet" }
+ def self.create_mac_service_map(machine)
+ tmp_ints = File.join(Dir.tmpdir, File.basename("#{machine.id}.interfaces"))
+ tmp_hw = File.join(Dir.tmpdir, File.basename("#{machine.id}.hardware"))
+
+ machine.communicate.tap do |comm|
+ comm.sudo("networksetup -detectnewhardware")
+ comm.sudo("networksetup -listnetworkserviceorder > /tmp/vagrant.interfaces")
+ comm.sudo("networksetup -listallhardwareports > /tmp/vagrant.hardware")
+ comm.download("/tmp/vagrant.interfaces", tmp_ints)
+ comm.download("/tmp/vagrant.hardware", tmp_hw)
+ end
+
+ interface_map = {}
+ ints = ::IO.read(tmp_ints)
ints.split(/\n\n/m).each do |i|
- if i.match(/Hardware/) and not i.match(/Ethernet/).nil?
- devmap = {}
+ if i.match(/Hardware/) && i.match(/Ethernet/)
# Ethernet, should be 2 lines,
# (3) Thunderbolt Ethernet
# (Hardware Port: Thunderbolt Ethernet, Device: en1)
# multiline, should match "Thunderbolt Ethernet", "en1"
devicearry = i.match(/\([0-9]+\) (.+)\n.*Device: (.+)\)/m)
- devmap[:interface] = devicearry[2]
- devmap[:service] = devicearry[1]
- devlist << devmap
+ service = devicearry[1]
+ interface = devicearry[2]
+
+ # Should map interface to service { "en1" => "Thunderbolt Ethernet" }
+ interface_map[interface] = service
end
end
- File.delete(tmpints)
+ File.delete(tmp_ints)
- networks.each do |network|
- service_name = devlist[network[:interface]][:service]
- if network[:type].to_sym == :static
- command = "networksetup -setmanual \"#{service_name}\" #{network[:ip]} #{network[:netmask]}"
- elsif network[:type].to_sym == :dhcp
- command = "networksetup -setdhcp \"#{service_name}\""
+ mac_service_map = {}
+ macs = ::IO.read(tmp_hw)
+ macs.split(/\n\n/m).each do |i|
+ if i.match(/Hardware/) && i.match(/Ethernet/)
+ # Ethernet, should be 3 lines,
+ # Hardware Port: Thunderbolt 1
+ # Device: en1
+ # Ethernet Address: a1:b2:c3:d4:e5:f6
+
+ # multiline, should match "en1", "00:c1:00:a1:b2:c3"
+ devicearry = i.match(/Device: (.+)\nEthernet Address: (.+)/m)
+ interface = devicearry[1]
+ naked_mac = devicearry[2].gsub(':','').upcase
+
+ # Skip hardware ports without MAC (bridges, bluetooth, etc.)
+ next if naked_mac == "N/A"
+
+ if !interface_map[interface]
+ @@logger.warn("Could not find network service for interface #{interface}")
+ next
+ end
+
+ # Should map MAC to service, { "00C100A1B2C3" => "Thunderbolt Ethernet" }
+ mac_service_map[naked_mac] = interface_map[interface]
end
-
- machine.communicate.sudo(command)
end
+ File.delete(tmp_hw)
+
+ mac_service_map
end
end
end
diff --git a/plugins/guests/debian/cap/smb.rb b/plugins/guests/debian/cap/smb.rb
new file mode 100644
index 000000000..fcc97af36
--- /dev/null
+++ b/plugins/guests/debian/cap/smb.rb
@@ -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
diff --git a/plugins/guests/debian/guest.rb b/plugins/guests/debian/guest.rb
index be08b3605..a7f3d65de 100644
--- a/plugins/guests/debian/guest.rb
+++ b/plugins/guests/debian/guest.rb
@@ -2,7 +2,7 @@ module VagrantPlugins
module GuestDebian
class Guest < Vagrant.plugin("2", :guest)
def detect?(machine)
- machine.communicate.test("cat /etc/issue | grep 'Debian' | grep -v '8'")
+ machine.communicate.test("cat /etc/issue | grep 'Debian'")
end
end
end
diff --git a/plugins/guests/debian/plugin.rb b/plugins/guests/debian/plugin.rb
index 0ba72ed5d..5d00583e8 100644
--- a/plugins/guests/debian/plugin.rb
+++ b/plugins/guests/debian/plugin.rb
@@ -30,6 +30,11 @@ module VagrantPlugins
require_relative "cap/rsync"
Cap::RSync
end
+
+ guest_capability("debian", "smb_install") do
+ require_relative "cap/smb"
+ Cap::SMB
+ end
end
end
end
diff --git a/plugins/guests/debian8/cap/halt.rb b/plugins/guests/debian8/cap/halt.rb
deleted file mode 100644
index 932281347..000000000
--- a/plugins/guests/debian8/cap/halt.rb
+++ /dev/null
@@ -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
diff --git a/plugins/guests/debian8/plugin.rb b/plugins/guests/debian8/plugin.rb
deleted file mode 100644
index 1f566f5fa..000000000
--- a/plugins/guests/debian8/plugin.rb
+++ /dev/null
@@ -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
diff --git a/plugins/guests/fedora/cap/change_host_name.rb b/plugins/guests/fedora/cap/change_host_name.rb
index 2a93b9de6..f0c95eec6 100644
--- a/plugins/guests/fedora/cap/change_host_name.rb
+++ b/plugins/guests/fedora/cap/change_host_name.rb
@@ -48,7 +48,7 @@ module VagrantPlugins
def update_etc_hosts
ip_address = '([0-9]{1,3}\.){3}[0-9]{1,3}'
search = "^(#{ip_address})\\s+#{Regexp.escape(current_hostname)}(\\s.*)?$"
- replace = "\\1 #{fqdn} #{short_hostname}"
+ replace = "\\1 #{fqdn} #{short_hostname} \\3"
expression = ['s', search, replace, 'g'].join('@')
sudo("sed -ri '#{expression}' /etc/hosts")
@@ -72,4 +72,4 @@ module VagrantPlugins
end
end
end
-end
\ No newline at end of file
+end
diff --git a/plugins/guests/fedora/cap/configure_networks.rb b/plugins/guests/fedora/cap/configure_networks.rb
index 364b744e4..b95d0bdb7 100644
--- a/plugins/guests/fedora/cap/configure_networks.rb
+++ b/plugins/guests/fedora/cap/configure_networks.rb
@@ -109,13 +109,23 @@ module VagrantPlugins
# SSH never dies.
interfaces.each do |interface|
retryable(on: Vagrant::Errors::VagrantError, tries: 3, sleep: 2) do
- machine.communicate.sudo("cat /tmp/vagrant-network-entry_#{interface} >> #{network_scripts_dir}/ifcfg-#{interface}")
- machine.communicate.sudo("! which nmcli >/dev/null 2>&1 || nmcli c reload #{interface}")
- machine.communicate.sudo("/sbin/ifdown #{interface}", error_check: true)
- machine.communicate.sudo("/sbin/ifup #{interface}")
- end
+ machine.communicate.sudo(<<-SCRIPT, error_check: true)
+cat /tmp/vagrant-network-entry_#{interface} >> #{network_scripts_dir}/ifcfg-#{interface}
- machine.communicate.sudo("rm -f /tmp/vagrant-network-entry_#{interface}")
+if command -v nmcli &>/dev/null; then
+ if command -v systemctl &>/dev/null && systemctl -q is-enabled NetworkManager &>/dev/null; then
+ nmcli c reload #{interface}
+ elif command -v service &>/dev/null && service NetworkManager status &>/dev/null; then
+ nmcli c reload #{interface}
+ fi
+fi
+
+/sbin/ifdown #{interface}
+/sbin/ifup #{interface}
+
+rm -f /tmp/vagrant-network-entry_#{interface}
+SCRIPT
+ end
end
end
end
diff --git a/plugins/guests/fedora/guest.rb b/plugins/guests/fedora/guest.rb
index c1b48d4ab..f05861cf8 100644
--- a/plugins/guests/fedora/guest.rb
+++ b/plugins/guests/fedora/guest.rb
@@ -4,7 +4,7 @@ module VagrantPlugins
module GuestFedora
class Guest < Vagrant.plugin("2", :guest)
def detect?(machine)
- machine.communicate.test("grep 'Fedora release 1[6789]\\|Fedora release 2[0-9]' /etc/redhat-release")
+ machine.communicate.test("grep 'Fedora release' /etc/redhat-release")
end
end
end
diff --git a/plugins/guests/linux/cap/mount_nfs.rb b/plugins/guests/linux/cap/mount_nfs.rb
index ecc3ea9ee..26dfb625d 100644
--- a/plugins/guests/linux/cap/mount_nfs.rb
+++ b/plugins/guests/linux/cap/mount_nfs.rb
@@ -33,10 +33,11 @@ module VagrantPlugins
end
# Emit an upstart event if we can
- if machine.communicate.test("test -x /sbin/initctl && test 'upstart' = $(basename $(sudo readlink /proc/1/exe))")
- machine.communicate.sudo(
- "/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{expanded_guest_path}")
- end
+ machine.communicate.sudo <<-SCRIPT
+if command -v /sbin/init &>/dev/null && /sbin/init --version | grep upstart &>/dev/null; then
+ /sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT='#{expanded_guest_path}'
+fi
+SCRIPT
end
end
end
diff --git a/plugins/guests/linux/cap/mount_smb_shared_folder.rb b/plugins/guests/linux/cap/mount_smb_shared_folder.rb
index 136ccfb8a..9db7c0ef3 100644
--- a/plugins/guests/linux/cap/mount_smb_shared_folder.rb
+++ b/plugins/guests/linux/cap/mount_smb_shared_folder.rb
@@ -24,17 +24,14 @@ module VagrantPlugins
mount_gid = "`getent group #{options[:group]} | cut -d: -f3`"
mount_gid_old = "`id -g #{options[:group]}`"
end
-
- smb_password = Shellwords.shellescape(options[:smb_password])
-
+
# If a domain is provided in the username, separate it
username, domain = (options[:smb_username] || '').split('@', 2)
+ smb_password = options[:smb_password]
options[:mount_options] ||= []
options[:mount_options] << "sec=ntlm"
- options[:mount_options] << "username=#{username}"
- options[:mount_options] << "password=#{smb_password}"
- options[:mount_options] << "domain=#{domain}" if domain
+ options[:mount_options] << "credentials=/etc/smb_creds_#{name}"
# First mount command uses getent to get the group
mount_options = "-o uid=#{mount_uid},gid=#{mount_gid}"
@@ -49,6 +46,16 @@ module VagrantPlugins
# Create the guest path if it doesn't exist
machine.communicate.sudo("mkdir -p #{expanded_guest_path}")
+ # Write the credentials file
+ machine.communicate.sudo(<<-SCRIPT)
+cat </etc/smb_creds_#{name}
+username=#{username}
+password=#{smb_password}
+#{domain ? "domain=#{domain}" : ""}
+EOF
+chmod 0600 /etc/smb_creds_#{name}
+SCRIPT
+
# Attempt to mount the folder. We retry here a few times because
# it can fail early on.
attempts = 0
@@ -86,10 +93,11 @@ module VagrantPlugins
end
# Emit an upstart event if we can
- if machine.communicate.test("test -x /sbin/initctl && test 'upstart' = $(basename $(sudo readlink /proc/1/exe))")
- machine.communicate.sudo(
- "/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{expanded_guest_path}")
- end
+ machine.communicate.sudo <<-SCRIPT
+if command -v /sbin/init &>/dev/null && /sbin/init --version | grep upstart &>/dev/null; then
+ /sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT='#{expanded_guest_path}'
+fi
+SCRIPT
end
end
end
diff --git a/plugins/guests/linux/cap/mount_virtualbox_shared_folder.rb b/plugins/guests/linux/cap/mount_virtualbox_shared_folder.rb
index 56c864b6b..57fdc207f 100644
--- a/plugins/guests/linux/cap/mount_virtualbox_shared_folder.rb
+++ b/plugins/guests/linux/cap/mount_virtualbox_shared_folder.rb
@@ -80,10 +80,11 @@ module VagrantPlugins
end
# Emit an upstart event if we can
- if machine.communicate.test("test -x /sbin/initctl && test 'upstart' = $(basename $(sudo readlink /proc/1/exe))")
- machine.communicate.sudo(
- "/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{expanded_guest_path}")
- end
+ machine.communicate.sudo <<-SCRIPT
+if command -v /sbin/init &>/dev/null && /sbin/init --version | grep upstart &>/dev/null; then
+ /sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT='#{expanded_guest_path}'
+fi
+SCRIPT
end
def self.unmount_virtualbox_shared_folder(machine, guestpath, options)
diff --git a/plugins/guests/linux/cap/remove_public_key.rb b/plugins/guests/linux/cap/remove_public_key.rb
index a8d773a40..dfd9daae5 100644
--- a/plugins/guests/linux/cap/remove_public_key.rb
+++ b/plugins/guests/linux/cap/remove_public_key.rb
@@ -10,8 +10,11 @@ module VagrantPlugins
machine.communicate.tap do |comm|
if comm.test("test -f ~/.ssh/authorized_keys")
- comm.execute(
- "sed -i '/^.*#{contents}.*$/d' ~/.ssh/authorized_keys")
+ comm.execute(<