Merge remote-tracking branch 'mitchellh/master'

This commit is contained in:
tfanning 2016-07-26 13:16:56 +01:00
commit 7177739c01
854 changed files with 14855 additions and 30958 deletions

View File

@ -37,5 +37,5 @@ The following guidelines for contribution should be followed if you want to subm
# Additional Resources
* [General GitHub documentation](http://help.github.com/)
* [GitHub pull request documentation](http://help.github.com/send-pull-requests/)
* [General GitHub documentation](https://help.github.com/)
* [GitHub pull request documentation](https://help.github.com/send-pull-requests/)

45
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,45 @@
Please note that the Vagrant issue tracker is reserved for bug reports and
enhancements. For general usage questions, please use the Vagrant mailing list:
https://groups.google.com/forum/#!forum/vagrant-up. Thank you!
### Vagrant version
Run `vagrant -v` to show the version. If you are not running the latest version
of Vagrant, please upgrade before submitting an issue.
### Host operating system
This is the operating system that you run locally.
### Guest operating system
This is the operating system you run in the virtual machine.
### Vagrantfile
```ruby
# Copy-paste your Vagrantfile here
```
Please note, if you are using Homestead or a different Vagrantfile format, we
may be unable to assist with your issue. Try to reproduce the issue using a
vanilla Vagrantfile first.
### Debug output
Provide a link to a GitHub Gist containing the complete debug output:
https://www.vagrantup.com/docs/other/debugging.html. The debug output should
be very long. Do NOT paste the debug output in the issue, just paste the
link to the Gist.
### Expected behavior
What should have happened?
### Actual behavior
What actually happened?
### Steps to reproduce
1.
2.
3.
### References
Are there any other GitHub issues (open or closed) that should be linked here?
For example:
- GH-1234
- ...

16
.gitignore vendored
View File

@ -9,8 +9,8 @@
acceptance_config.yml
boxes/*
/.vagrant
/website/docs/.vagrant
/website/www/.vagrant
/website/.vagrant
/website/build
/vagrant-spec.config.rb
# Bundler/Rubygems
@ -21,6 +21,7 @@ tags
/Gemfile.lock
test/tmp/
vendor/
/exec
# Documentation
_site/*
@ -43,14 +44,3 @@ doc/
.ruby-gemset
.ruby-version
.rvmrc
# Website: docs
website/docs/.sass-cache
website/docs/build
website/docs/Rakefile
# Website: www
website/www/.sass-cache
website/www/build
website/www/Rakefile
exec/

View File

@ -4,13 +4,16 @@ sudo: false
cache: bundler
before_install:
- gem install bundler -v '1.12.5'
addons:
apt:
packages:
- bsdtar
rvm:
- 2.0.0
- 2.2.3
branches:
only:

View File

@ -1,9 +1,228 @@
## Next Version (unreleased)
## Next Version (Unreleased)
FEATURES:
IMPROVEMENTS:
BUG FIXES:
- provisioners/ansible_local: Fix error in `playbook` existence check when
running on a Windows host [GH-6740]
- hosts/arch: Detect NFS server by service name on arch [GH-7630, GH-7629]
- guests/linux: Fix SSH key permissions [GH-7610, GH-7611]
- guests/ubuntu: Fix detection on older guests [GH-7632, GH-7524, GH-7625]
## 1.8.5 (July 18, 2016)
FEATURES:
- core: Provide a way to globally disable box update checks with the
environment variable `VAGRANT_BOX_UPDATE_CHECK_DISABLE`. Setting this
to any non-empty value will instruct Vagrant to not look for box updates
when running `vagrant up`. Setting this environment variable has no
effect on the `vagrant box` commands.
IMPROVEMENTS:
- guests/arch: Support installing synced folder clients [GH-7519]
- guests/darwin: Allow ipv6 static networks [GH-7491]
- providers/virtualbox: Add support for 5.1 [GH-7574]
BUG FIXES:
- core: Bump listen gem and Ruby version to improve rsync performance
[GH-7453, GH-7441]
- core: Check process stdout when detecting if a hyperv admin
[GH-7465, GH-7467]
- core: Ensure removal of temporary directory when box download fails
[GH-7496, GH-7499]
- core: Fix regression for installing plugins from path [GH-7505, GH-7493]
- core: Skip checking conflicts on disabled ports [GH-7587]
- core: Idempotent write-out for state file [GH-7550]
- core/guests: Create common BSD guest for shared logic
- core/guests: Ignore empty output from `/sbin/ip`
[GH-7539, GH-7537, GH-7533, GH-7605]
- synced_folders/nfs: Shellescape rsync paths
[GH-7540, GH-7605]
- synced_folders/nfs: Ensure retries take place [GH-6360, GH-7605]
- synced_folders/rsync: Shellescape rsync paths
[GH-7580, GH-6690, GH-7579, GH-7605]
- synced_folders/rsync: Translate Windows paths
[GH-7012, GH-6702, GH-6568, GH-7046]
- guests/bsd: Consolidate core logic for mounting NFS folders
[GH-7480, GH-7474, GH-7466]
- guests/bsd: Consolidate core logic for public key management [GH-7481]
- guests/bsd: Consolidate core logic for halting [GH-7484]
- guests/centos: Use `ip` instead of `ifconfig` to detect network interfaces
[GH-7460]
- guests/debian: Ensure newline when inserting public key [GH-7456]
- guests/linux: Ensure NFS retries during mounting [GH-7492]
- guests/redhat: Use `/sbin/ip` to list and configure networks for
compatability with older versions of CentOS [GH-7482]
- guests/redhat: Ensure newline when inserting public key [GH-7598, GH-7605]
- guests/ubuntu: Use /etc/os-release to detech [GH-7524]
- guests/ubuntu: Use short hostname [GH-7488, GH-7605]
- providers/hyperv: Fix version check and catch statement [GH-7447, GH-7487]
## 1.8.4 (June 13, 2016)
BUG FIXES:
- core: Fix bundler plugin issue and version constraint [GH-7418, GH-7415]
- providers/virtualbox: Use 8 network interfaces (due to Windows limitation)
[GH-7417, GH-7419]
- provisioners/ansible(both): Honor "galaxy_roles_path" option when running
ansible-playbook [GH-7269, GH-7420]
- provisioners/ansible_local: Add quotes around "ansible-galaxy" arguments
[GH-7420]
IMPROVEMENTS:
- guests/redhat: Add CloudLinux detection [GH-7428, GH-7427]
## 1.8.3 (June 10, 2016)
BREAKING CHANGES:
- The `winrm` communicator now shares the same upload behavior as the `ssh`
communicator. This change should have no impact to most vagrant operations
but may break behavior when uploading directories to an existing
destination target. The `file` provisioner should be the only builtin
provisioner affected by this change. When uploading a directory and the
destination directory exists on the endpoint, the source base directory
will be created below the destination directory on the endpoint and the
source directory contents will be unzipped to that location. Prior to this
release, the contents of the source directory would be unzipped to an
existing destination directory without creating the source base directory.
This new behavior is more consistent with SCP and other well known shell copy commands.
- The Chef provisioner's `channel` default value has changed from "current" to
"stable". The "current" channel includes nightly releases and should be
opt-in only. Note that users wishing to download the Chef Development Kit
will need to opt into the "current" channel until Chef Software promotes
into the "stable" channel.
- The Arch Linux host capability for NFS removed support for rc.d in favor or
systemd which has been present since 2012. Please see GH-7181 for more
information.
FEATURES:
- provider/docker: Allow non-linux users to opt-out of the host VM to run
Docker containers by setting `config.force_host_vm = false` in the
Vagrantfile. This is especially useful for customers who wish to use
the beta builds for Mac and Windows, dlite, or a custom provider.
[GH-7277, GH-7298, 8c11b53]
- provider/docker: New command: `docker-exec` allows attaching to an
already-running container.
[GH-7377, GH-6566, GH-5193, GH-4904, GH-4057, GH-4179, GH-4903]
IMPROVEMENTS:
- core/downloader: increase box resume download limit to 24h
[GH-7352, GH-7272]
- core/package: run validations prior to packaging [GH-7353, GH-7351]
- core/action: make `start` ("vagrant up") run provisioners [GH-4467, GH-4421]
- commands/all: Make it clear that machine IDs can be specified
[GH-7356, GH-7228]
- commands/init: Add support for specifying the box version [GH-7363, GH-5004]
- commands/login: Print a warning with both the environment variable and
local login token are present [GH-7206, GH-7219]
- communicators/winrm: Upgrade to latest WinRM gems [GH-6922]
- provisioners/ansible_local: Allow to install Ansible from pip,
with version selection capability [GH-6654, GH-7167]
- provisioners/ansible_local: Use `provisioning_path` as working directory
for `ansible-galaxy` execution
- provisioners/ansible(both provisioners): Add basic config
validators/converters on `raw_arguments` and `raw_ssh_args` options
[GH-7103]
- provisioners/chef: Add the ability to install on SUSE [GH-6806]
- provisioners/chef: Support legacy solo mode [GH-7327]
- provisioners/docker: Restart container if newer image is available
[GH-7358, GH-6620]
- hosts/arch: Remove sysvinit and assume systemd [GH-7181]
- hosts/linux: Do not use a pager with systemctl commands [GH-7270]
- hosts/darwin: Add `extra_args` support for RDP [GH-5523, GH-6602]
- hosts/windows: Use SafeExec to capture history in Powershell [GH-6749]
- guests/amazon: Add detection [GH-7395, GH-7254]
- guests/freebsd: Add quotes around hostname [GH-6867]
- guests/fedora: Add support for ipv6 static networks [GH-7275, GH-7276]
- guests/tinycore: Add support for shared folders [GH-6977, GH-6968]
- guests/trisquel: Add initial support [GH-6842, GH-6843]
- guests/windows: Add support for automatic login (no password prompting)
[GH-5670]
- core: Add `--no-delete` and provisioning flags to snapshot restore/pop
[GH-6879]
- providers/docker: Allow TCP and UDP ports on the same number [GH-7365,
GH-5527]
- providers/hyperv: Add support for differencing disk [GH-7090]
- providers/hyperv: Add support for snapshots [GH-7110]
- providers/hyperv: Reinstate compatibility with PS 4 [GH-7108]
- providers/virtualbox: Add linked clone support for Virtualbox 1.4 [GH-7050]
- synced_folders/nfs: Read static and dynamic IPs [GH-7290, GH-7289]
BUG FIXES:
- core: Bump nokogiri version to fix windows bug [GH-6766, GH-6848]
- core: Revert a change made to the output of the identify file [GH-6962,
GH-6929, GH-6589]
- core: Fix login command behind a proxy [GH-6898, GH-6899]
- core: Fix support for regular expressions on multi-machine `up`
[GH-6908, GH-6909]
- core: Allow boxes to use pre-release versions [GH-6892, GH-6893]
- core: Rescue `Errno:ENOTCONN` waiting for port to be open [GH-7182, GH-7184]
- core: Properly authenticate metadata box URLs [GH-6776, GH-7158]
- core: Do not run provisioners if already run on resume [GH-7059, GH-6787]
- core: Implement better tracking of tempfiles and tmpdirs to identify file
leaks [GH-7355]
- core: Allow SSH forwarding on Windows [GH-7287, GH-7202]
- core: Allow customizing `keys_only` SSH option [GH-7360, GH-4275]
- core: Allow customizing `paranoid` SSH option [GH-7360, GH-4275]
- command/box_update: Do not update the same box twice [GH-6042, GH-7379]
- command/init: Remove unnecessary `sudo` from generated Vagrantfile
[GH-7369, GH-7295]
- docs & core: Be consistent about the "2" in the Vagrantfile version
[GH-6961, GH-6963]
- guests/all: Refactor guest capabilities to run in a single command -
**please see GH-7393 for the complete list of changes!**
- guests/arch: Restart network after configuration [GH-7120, GH-7119]
- guests/debian: Do not return an error if ifdown fails [GH-7159,
GH-7155, GH-6871]
- guests/freebsd: Use `pkg` to install rsync [GH-6760]
- guests/freebsd: Use `netif` to configure networks [GH-5852, GH-7093]
- guests/coreos: Detect all interface names [GH-6608, GH-6610]
- providers/hyperv: Only specify Hyper-V if the parameter is support
[GH-7101, GH-7098]
- providers/virtualbox: Set maximum network adapters to 36 [GH-7293, GH-7286]
- providers/virtualbox: Do not fail when master VM from linked clone is
missing [GH-7126, GH-6742]
- providers/virtualbox: Use scoped overrides in preparring NFS
[GH-7387, GH-7386]
- provisioners/ansible: Fix a race condition in the concurrent generations of
the ansible inventory file, while running `vagrant up --parallel`
[GH-6526, GH-7190]
- provisioners/ansible_local: Don't quote the Ansible arguments defined in the
`raw_arguments` option [GH-7103]
- provisioners/ansible_local: Format json `extra_vars` with double quotes
[GH-6726, GH-7103]
- provisioners/ansible_local: Fix errors in absolute paths to playbook or
galaxy resources when running on a Windows host [GH-6740, GH-6757]
- provisioners/ansible_local: Change the way to verify `ansible-galaxy`
presence, to avoid a non-zero status code with Ansible 2.0 [GH-6793]
- provisioners/ansible(both provisioners): The Ansible configuration files
detection is only executed by the `provision` action [GH-6763, GH-6984]
- provisioners/chef: Do not use double sudo when installing
[GGH-6805, GH-6804]
- provisioners/chef: Change the default channel to "stable" (previously it
was "current") [GH-7001, GH-6979]
- provisioners/chef: Default node_name to hostname if present
[GH-7063, GH-7153]
- provisioners/docker: Fix -no-trunc command option [GH-7085]
- provisioners/docker: Allow provisioning when container name is specified
[GH-7074, GH-7086]
- provisioners/puppet: Use `where.exe` to locate puppet binary
[GH-6912, GH-6876]
- provisioners/salt: Move masterless config to apply to all platforms
[GH-7207, Gh-6924, GH-6915]
- pushes/ftp: Create parent directories when uploading [GH-7154, GH-6316]
- synced_folders/smb: Do not interpolate configuration file [GH-6906]
## 1.8.1 (December 21, 2015)
@ -15,6 +234,8 @@ BUG FIXES:
UNC paths [GH-6598]
- core: Fix a crash in parsing the config in some cases with network
configurations [GH-6730]
- core: Clean up temporarily files created by bundler
[GH-7354, GH-6301, GH-3469, GH-6231]
- 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]
@ -597,7 +818,7 @@ BUG FIXES:
- commands/package: base package won't crash with exception [GH-4017]
- commands/rsync-auto: Destroyed machines won't raise exceptions. [GH-4031]
- commands/ssh: Extra args are passed through to Docker container. [GH-4378]
- communicators/ssh: Nicer error if remote unexpectedly disconects. [GH-4038]
- communicators/ssh: Nicer error if remote unexpectedly disconnects. [GH-4038]
- communicators/ssh: Clean error when max sessions is hit. [GH-4044]
- communicators/ssh: Fix many issues around PTY-enabled output parsing.
[GH-4408]
@ -1306,7 +1527,7 @@ IMPROVEMENTS:
certs from a custom CA. [GH-2337]
- commands/box/add: Can now specify a client cert when downloading a
box. [GH-1889]
- commands/init: Add `--output` option for specifing output path, or
- commands/init: Add `--output` option for specifying output path, or
"-" for stdin. [GH-1364]
- commands/provision: Add `--no-parallel` option to disable provider
parallelization if the provider supports it. [GH-2404]
@ -2845,7 +3066,7 @@ compatibility.
in a saved state. [GH-123]
- Added `config.chef.recipe_url` which allows you to specify a URL to
a gzipped tar file for chef solo to download cookbooks. See the
[chef-solo docs](http://wiki.opscode.com/display/chef/Chef+Solo#ChefSolo-RunningfromaURL) for more information.
[chef-solo docs](https://docs.chef.io/chef_solo.html) for more information.
[GH-121]
- Added `vagrant box repackage` which repackages boxes which have
been added. This is useful in case you want to redistribute a base

View File

@ -3,7 +3,7 @@
* 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)
* Mailing list: [Google Groups](https://groups.google.com/group/vagrant-up)
Vagrant is a tool for building and distributing development environments.
@ -21,12 +21,12 @@ between Windows, Mac OS X, and Linux.
For the quick-start, we'll bring up a development machine on
[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] (https://www.openstack.org/), [VMware] (http://www.vmware.com/), [Docker] (https://docs.docker.com/), etc.
system such as [OpenStack] (https://www.openstack.org/), [VMware] (https://www.vmware.com/), [Docker] (https://docs.docker.com/), etc.
First, make sure your development machine has
[VirtualBox](https://www.virtualbox.org/)
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](https://www.vagrantup.com/downloads.html).
To build your first virtual environment:
@ -40,7 +40,7 @@ the box doesn't already exist on your system.
## Getting Started Guide
To learn how to build a fully functional development environment, follow the
[getting started guide](http://docs.vagrantup.com/v2/getting-started/index.html).
[getting started guide](https://www.vagrantup.com/docs/getting-started/index.html).
## Installing the Gem from Git

51
RELEASE.md Normal file
View File

@ -0,0 +1,51 @@
# Releasing Vagrant
This documents how to release Vagrant. Various steps in this document will
require privileged access to private systems, so this document is only
targetted at Vagrant core members who have the ability to cut a release.
1. Update `version.txt` to the version you want to release.
1. Update `CHANGELOG.md` to have a header with the release version and date.
1. Commit those changes and also tag the release with the version:
```
$ git tag vX.Y.Z
$ git push --tags
```
1. Trigger an installer creation run within the HashiCorp Bamboo installation.
This will take around 45 minutes.
1. Download all the resulting artifacts into the `pkg/dist` folder relative to
the Vagrant repository on your local machine.
1. Run `./scripts/sign.sh` with the version that is being created. This must be
run from the Vagrant repo root. This will GPG sign and checksum the files.
1. Run the following command to upload the binaries to the releases site:
```
$ hc-releases upload pkg/dist
```
1. Publish the new index files to the releases site:
```
$ hc-releases publish
```
1. Update `website/config.rb` to point to the latest version, commit, and push.
1. Tell HashiBot to deploy in ``#deploys`
```
hashibot deploy vagrant
```
1. Update `version.txt` to append `.dev` and add a new blank entry in the
CHANGELOG, commit, and push.
1. Update [Checkpoint](https://checkpoint.hashicorp.com/control) with the new
version.

View File

@ -139,7 +139,7 @@ begin
end
# Also allow users to force colors.
if argv.include?("--color")
if argv.include?("--color") || ENV["VAGRANT_FORCE_COLOR"]
argv.delete("--color")
opts[:ui_class] = Vagrant::UI::Colored
end

View File

@ -53,7 +53,7 @@ __vagrantinvestigate() {
_vagrant() {
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
commands="box connect destroy docker-logs docker-run global-status halt help init list-commands login package plugin provision push rdp reload resume rsync rsync-auto share ssh ssh-config status suspend up version"
commands="box connect destroy docker-exec docker-logs docker-run global-status halt help init list-commands login package plugin provision push rdp reload resume rsync rsync-auto share snapshot ssh ssh-config status suspend up version"
if [ $COMP_CWORD == 1 ]
then
@ -104,6 +104,11 @@ _vagrant() {
COMPREPLY=($(compgen -W "${commands}" -- ${cur}))
return 0
;;
"snapshot")
snapshot_commands="delete list pop push restore save"
COMPREPLY=($(compgen -W "${snapshot_commands}" -- ${cur}))
return 0
;;
*)
;;
esac

View File

@ -1,5 +1,5 @@
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 --no-pager 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 /*/exports

View File

@ -1,7 +1,7 @@
# Insecure Keypair
These keys are the "insecure" public/private keypair we offer to
[base box creators](http://docs.vagrantup.com/v2/boxes/base.html) for use in their base boxes so that
[base box creators](https://www.vagrantup.com/docs/boxes/base.html) for use in their base boxes so that
vagrant installations can automatically SSH into the boxes.
If you're working with a team or company or with a custom box and

View File

@ -18,6 +18,10 @@ module Vagrant
# to NOT be metadata.
METADATA_SIZE_LIMIT = 20971520
# This is the amount of time to "resume" downloads if a partial box
# file already exists.
RESUME_DELAY = 24 * 60 * 60
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::action::builtin::box_add")
@ -30,7 +34,7 @@ module Vagrant
u = u.gsub("\\", "/")
if Util::Platform.windows? && u =~ /^[a-z]:/i
# On Windows, we need to be careful about drive letters
u = "file://#{URI.escape(u)}"
u = "file:///#{URI.escape(u)}"
end
if u =~ /^[a-z0-9]+:.*$/i && !u.start_with?("file://")
@ -253,7 +257,7 @@ module Vagrant
end
provider_url = metadata_provider.url
if url != authenticated_url
if provider_url != authenticated_url
# Authenticate the provider URL since we're using auth
hook_env = env[:hook].call(:authenticate_box_url, box_urls: [provider_url])
authed_urls = hook_env[:box_urls]
@ -261,7 +265,7 @@ module Vagrant
raise "Bad box authentication hook, did not generate proper results."
end
provider_url = authed_urls[0]
end
end
box_add(
[[provider_url, metadata_provider.url]],
@ -393,7 +397,7 @@ module Vagrant
if env[:box_clean]
@logger.info("Cleaning existing temp box file.")
delete = true
elsif temp_path.mtime.to_i < (Time.now.to_i - 6 * 60 * 60)
elsif temp_path.mtime.to_i < (Time.now.to_i - RESUME_DELAY)
@logger.info("Existing temp file is too old. Removing.")
delete = true
end
@ -468,6 +472,8 @@ module Vagrant
if uri.scheme == "file"
url = uri.path
url ||= uri.opaque
#7570 Strip leading slash left in front of drive letter by uri.path
Util::Platform.windows? && url.gsub!(/^\/([a-zA-Z]:)/, '\1')
begin
File.open(url, "r") do |f|

View File

@ -93,6 +93,11 @@ module Vagrant
guest_port = options[:guest]
host_port = options[:host]
if options[:disabled]
@logger.debug("Skipping disabled port #{host_port}.")
next
end
if options[:protocol] && options[:protocol] != "tcp"
@logger.debug("Skipping #{host_port} because UDP protocol.")
next

View File

@ -3,6 +3,7 @@ require "pathname"
require 'vagrant/util/safe_chdir'
require 'vagrant/util/subprocess'
require 'vagrant/util/presence'
module Vagrant
module Action
@ -20,25 +21,63 @@ module Vagrant
class Package
include Util
# Perform sanity validations that the provided output filepath is sane.
# In particular, this function validates:
#
# - The output path is a regular file (not a directory or symlink)
# - No file currently exists at the given path
# - A directory of package files was actually provided (internal)
#
# @param [String] output path to the output file
# @param [String] directory path to a directory containing the files
def self.validate!(output, directory)
filename = File.basename(output.to_s)
output = fullpath(output)
if File.directory?(output)
raise Vagrant::Errors::PackageOutputDirectory
end
if File.exist?(output)
raise Vagrant::Errors::PackageOutputExists, filename: filename
end
if !Vagrant::Util::Presence.present?(directory) || !File.directory?(directory)
raise Vagrant::Errors::PackageRequiresDirectory
end
end
# Calculate the full path of the given path, relative to the current
# working directory (where the command was run).
#
# @param [String] output the relative path
def self.fullpath(output)
File.expand_path(output, Dir.pwd)
end
# The path to the final output file.
# @return [String]
attr_reader :fullpath
def initialize(app, env)
@app = app
env["package.files"] ||= {}
env["package.output"] ||= "package.box"
@fullpath = self.class.fullpath(env["package.output"])
end
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"] ||
!File.directory?(env["package.directory"])
self.class.validate!(env["package.output"], env["package.directory"])
raise Errors::PackageOutputDirectory if File.directory?(fullpath)
@app.call(env)
@env[:ui].info I18n.t("vagrant.actions.general.package.compressing", tar_path: tar_path)
@env[:ui].info I18n.t("vagrant.actions.general.package.compressing", fullpath: fullpath)
copy_include_files
setup_private_key
compress
@ -54,7 +93,7 @@ module Vagrant
end
# Cleanup any packaged files if the packaging failed at some point.
File.delete(tar_path) if File.exist?(tar_path)
File.delete(fullpath) if File.exist?(fullpath)
end
# This method copies the include files (passed in via command line)
@ -88,7 +127,7 @@ module Vagrant
def compress
# Get the output path. We have to do this up here so that the
# pwd returns the proper thing.
output_path = tar_path.to_s
output_path = fullpath.to_s
# Switch into that directory and package everything up
Util::SafeChdir.safe_chdir(@env["package.directory"]) do
@ -147,11 +186,6 @@ module Vagrant
f.puts %Q[end]
end
end
# Path to the final box output file
def tar_path
File.expand_path(@env["package.output"], FileUtils.pwd)
end
end
end
end

View File

@ -118,7 +118,7 @@ module Vagrant
# @param [Hash] download_options Options to pass to the downloader.
# @return [BoxMetadata]
def load_metadata(**download_options)
tf = Tempfile.new("vagrant")
tf = Tempfile.new("vagrant-load-metadata")
tf.close
url = @metadata_url

View File

@ -1,4 +1,5 @@
require "digest/sha1"
require "fileutils"
require "monitor"
require "tmpdir"
@ -276,17 +277,16 @@ module Vagrant
next if versiondir.basename.to_s.start_with?(".")
version = versiondir.basename.to_s
Gem::Version.new(version)
end.compact
# Traverse through versions with the latest version first
versions.sort.reverse.each do |v|
if !requirements.all? { |r| r.satisfied_by?(v) }
if !requirements.all? { |r| r.satisfied_by?(Gem::Version.new(v)) }
# Unsatisfied version requirements
next
end
versiondir = box_directory.join(v.to_s)
versiondir = box_directory.join(v)
providers.each do |provider|
provider_dir = versiondir.join(provider.to_s)
next if !provider_dir.directory?
@ -303,7 +303,7 @@ module Vagrant
end
return Box.new(
name, provider, v.to_s, provider_dir,
name, provider, v, provider_dir,
metadata_url: metadata_url,
)
end
@ -448,7 +448,7 @@ module Vagrant
yield dir
ensure
dir.rmtree if dir.exist?
FileUtils.rm_rf(dir.to_s)
end
# Checks if a box with a given name exists.

View File

@ -2,6 +2,7 @@ require "monitor"
require "pathname"
require "set"
require "tempfile"
require "fileutils"
require "bundler"
@ -55,13 +56,13 @@ module Vagrant
# Setup the "local" Bundler configuration. We need to set BUNDLE_PATH
# because the existence of this actually suppresses `sudo`.
@appconfigpath = Dir.mktmpdir
@appconfigpath = Dir.mktmpdir("vagrant-bundle-app-config")
File.open(File.join(@appconfigpath, "config"), "w+") do |f|
f.write("BUNDLE_PATH: \"#{bundle_path}\"")
end
# Setup the Bundler configuration
@configfile = File.open(Tempfile.new("vagrant").path + "1", "w+")
@configfile = tempfile("vagrant-configfile")
@configfile.close
# Build up the Gemfile for our Bundler context. We make sure to
@ -84,9 +85,13 @@ module Vagrant
# Removes any temporary files created by init
def deinit
File.unlink(ENV["BUNDLE_APP_CONFIG"]) rescue nil
File.unlink(ENV["BUNDLE_CONFIG"]) rescue nil
File.unlink(ENV["GEMFILE"]) rescue nil
# If we weren't enabled, then we don't do anything.
return if !@enabled
FileUtils.rm_rf(ENV["BUNDLE_APP_CONFIG"]) rescue nil
FileUtils.rm_f(ENV["BUNDLE_CONFIG"]) rescue nil
FileUtils.rm_f(ENV["BUNDLE_GEMFILE"]) rescue nil
FileUtils.rm_f(ENV["BUNDLE_GEMFILE"]+".lock") rescue nil
end
# Installs the list of plugins.
@ -181,7 +186,7 @@ module Vagrant
def build_gemfile(plugins)
sources = plugins.values.map { |p| p["sources"] }.flatten.compact.uniq
f = File.open(Tempfile.new("vagrant").path + "2", "w+")
f = tempfile("vagrant-gemfile")
f.tap do |gemfile|
sources.each do |source|
next if source == ""
@ -203,7 +208,6 @@ module Vagrant
gemfile.puts(%Q[gem "#{name}", #{version.inspect}, #{opts.inspect}])
end
gemfile.puts("end")
gemfile.close
end
end
@ -250,11 +254,14 @@ module Vagrant
def with_isolated_gem
raise Errors::BundlerDisabled if !@enabled
tmp_gemfile = tempfile("vagrant-gemfile")
tmp_gemfile.close
# Remove bundler settings so that Bundler isn't loaded when building
# native extensions because it causes all sorts of problems.
old_rubyopt = ENV["RUBYOPT"]
old_gemfile = ENV["BUNDLE_GEMFILE"]
ENV["BUNDLE_GEMFILE"] = Tempfile.new("vagrant-gemfile").path
ENV["BUNDLE_GEMFILE"] = tmp_gemfile.path
ENV["RUBYOPT"] = (ENV["RUBYOPT"] || "").gsub(/-rbundler\/setup\s*/, "")
# Set the GEM_HOME so gems are installed only to our local gem dir
@ -265,7 +272,10 @@ module Vagrant
# Reset the all specs override that Bundler does
old_all = Gem::Specification._all
Gem::Specification.all = nil
# WARNING: Seriously don't touch this without reading the comment attached
# to the monkey-patch at the bottom of this file.
Gem::Specification.vagrant_reset!
# /etc/gemrc and so on.
old_config = nil
@ -284,6 +294,8 @@ module Vagrant
return yield
end
ensure
tmp_gemfile.unlink rescue nil
ENV["BUNDLE_GEMFILE"] = old_gemfile
ENV["GEM_HOME"] = @gem_home
ENV["RUBYOPT"] = old_rubyopt
@ -293,6 +305,17 @@ module Vagrant
Gem::Specification.all = old_all
end
# This method returns a proper "tempfile" on disk. Ruby's Tempfile class
# would work really great for this, except GC can come along and remove
# the file before we are done with it. This is because we "close" the file,
# but we might be shelling out to a subprocess.
#
# @return [File]
def tempfile(name)
path = Dir::Tmpname.create(name) {}
return File.open(path, "w+")
end
# This is pretty hacky but it is a custom implementation of
# Gem::ConfigFile so that we don't load any gemrc files.
class NilGemConfig < Gem::ConfigFile
@ -311,6 +334,36 @@ module Vagrant
end
end
# This monkey patches Gem::Specification from RubyGems to add a new method,
# `vagrant_reset!`. For some background, Vagrant needs to set the value
# of these variables to nil to force new specs to be loaded. Previously,
# this was accomplished by setting Gem::Specification.specs = nil. However,
# newer versions of Rubygems try to map across that nil using a group_by
# clause, breaking things.
#
# This generally never affected Vagrant users who were using the official
# Vagrant installers because we lock to an older version of Rubygems that
# does not have this issue. The users of the official debian packages,
# however, experienced this issue because they float on Rubygems.
#
# In GH-7073, a number of Debian users reported this issue, but it was not
# reproducible in the official installer for reasons described above. Commit
# ba77d4b switched to using Gem::Specification.reset, but this actually
# broke the ability to install gems locally (GH-7493) because it resets
# the complete local cache, which is already built.
#
# The only solution that works with both new and old versions of Rubygems
# is to provide our own function for JUST resetting all the stubs. Both
# @@all and @@stubs must be set to a falsey value, so some of the
# originally-suggested solutions of using an empty array do not work. Only
# setting these values to nil (without clearing the cache), allows Vagrant
# to install and manage plugins.
class Gem::Specification < Gem::BasicSpecification
def self.vagrant_reset!
@@all = @@stubs = nil
end
end
if ::Bundler::UI.const_defined? :Silent
class BundlerUI < ::Bundler::UI::Silent
attr_reader :output

View File

@ -324,10 +324,6 @@ module Vagrant
error_key(:darwin_mount_failed)
end
class DarwinNFSMountFailed < VagrantError
error_key(:darwin_nfs_mount_failed)
end
class DestroyRequiresForce < VagrantError
error_key(:destroy_requires_force)
end
@ -404,10 +400,6 @@ module Vagrant
error_key(:linux_mount_failed)
end
class LinuxNFSMountFailed < VagrantError
error_key(:linux_nfs_mount_failed)
end
class LinuxRDPClientNotFound < VagrantError
error_key(:linux_rdp_client_not_found)
end
@ -456,6 +448,10 @@ module Vagrant
error_key(:not_found, "vagrant.actions.vm.host_only_network")
end
class NetworkTypeNotSupported < VagrantError
error_key(:network_type_not_supported)
end
class NFSBadExports < VagrantError
error_key(:nfs_bad_exports)
end
@ -464,6 +460,10 @@ module Vagrant
error_key(:nfs_cant_read_exports)
end
class NFSMountFailed < VagrantError
error_key(:nfs_mount_failed)
end
class NFSNoGuestIP < VagrantError
error_key(:nfs_no_guest_ip)
end
@ -780,6 +780,14 @@ module Vagrant
error_key(:virtualbox_no_name)
end
class VirtualBoxMountFailed < VagrantError
error_key(:virtualbox_mount_failed)
end
class VirtualBoxMountNotSupportedBSD < VagrantError
error_key(:virtualbox_mount_not_supported_bsd)
end
class VirtualBoxNameExists < VagrantError
error_key(:virtualbox_name_exists)
end

View File

@ -109,6 +109,7 @@ module Vagrant
@provider_options = provider_options
@ui = Vagrant::UI::Prefixed.new(@env.ui, @name)
@ui_mutex = Mutex.new
@state_mutex = Mutex.new
# Read the ID, which is usually in local storage
@id = nil
@ -434,6 +435,8 @@ module Vagrant
info[:host] ||= @config.ssh.default.host
info[:port] ||= @config.ssh.default.port
info[:private_key_path] ||= @config.ssh.default.private_key_path
info[:keys_only] ||= @config.ssh.default.keys_only
info[:paranoid] ||= @config.ssh.default.paranoid
info[:username] ||= @config.ssh.default.username
# We set overrides if they are set. These take precedence over
@ -505,11 +508,17 @@ module Vagrant
# master index.
uuid = index_uuid
if uuid
entry = @env.machine_index.get(uuid)
if entry
entry.state = result.short_description
@env.machine_index.set(entry)
@env.machine_index.release(entry)
# active_machines provides access to query this info on each machine
# from a different thread, ensure multiple machines do not access
# the locked entry simultaneously as this triggers a locked machine
# exception.
@state_mutex.synchronize do
entry = @env.machine_index.get(uuid)
if entry
entry.state = result.short_description
@env.machine_index.set(entry)
@env.machine_index.release(entry)
end
end
end

View File

@ -1,4 +1,6 @@
require "json"
require "fileutils"
require "tempfile"
module Vagrant
module Plugin
@ -91,8 +93,13 @@ module Vagrant
# This saves the state back into the state file.
def save!
@path.open("w+") do |f|
Tempfile.open(@path.basename.to_s, @path.dirname.to_s) do |f|
f.binmode
f.write(JSON.dump(@data))
f.fsync
f.chmod(0644)
f.close
FileUtils.mv(f.path, @path)
end
end

View File

@ -212,7 +212,7 @@ module Vagrant
end
def clear_line
# See: http://en.wikipedia.org/wiki/ANSI_escape_code
# See: https://en.wikipedia.org/wiki/ANSI_escape_code
reset = "\r\033[K"
info(reset, new_line: false)

View File

@ -30,7 +30,7 @@ module Vagrant
return true
end
rescue Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, \
Errno::ENETUNREACH, Errno::EACCES
Errno::ENETUNREACH, Errno::EACCES, Errno::ENOTCONN
# Any of the above exceptions signal that the port is closed.
return false
end

View File

@ -1,6 +1,6 @@
require 'rbconfig'
require 'shellwords'
require 'tmpdir'
require "rbconfig"
require "shellwords"
require "tmpdir"
require "vagrant/util/subprocess"
@ -10,17 +10,21 @@ 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")
return @_cygwin if defined?(@_cygwin)
@_cygwin = -> {
# Installer detects Cygwin
return true if ENV["VAGRANT_DETECTED_OS"] &&
ENV["VAGRANT_DETECTED_OS"].downcase.include?("cygwin")
# Ruby running in Cygwin
return true if 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")
# 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")
}.call
return @_cygwin
end
[:darwin, :bsd, :freebsd, :linux, :solaris].each do |type|
@ -30,11 +34,9 @@ module Vagrant
end
def windows?
%W[mingw mswin].each do |text|
return true if platform.include?(text)
end
false
return @_windows if defined?(@_windows)
@_windows = %w[mingw mswin].any? { |t| platform.include?(t) }
return @_windows
end
# Checks if the user running Vagrant on Windows has administrative
@ -42,23 +44,28 @@ module Vagrant
#
# @return [Boolean]
def windows_admin?
return @_windows_admin if defined?(@_windows_admin)
# We lazily-load this because it is only available on Windows
require 'win32/registry'
require "win32/registry"
# Verify that we have administrative privileges. The odd method of
# detecting this is based on this StackOverflow question:
#
# http://stackoverflow.com/questions/560366/
# https://stackoverflow.com/questions/560366/
# detect-if-running-with-administrator-privileges-under-windows-xp
begin
Win32::Registry::HKEY_USERS.open("S-1-5-19") {}
rescue Win32::Registry::Error
return false
end
@_windows_admin = -> {
begin
Win32::Registry::HKEY_USERS.open("S-1-5-19") {}
# 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?
# The above doesn't seem to be 100% bullet proof. See GH-5616.
return (`reg query HKU\\S-1-5-19 2>&1` =~ /ERROR/).nil?
rescue Win32::Registry::Error
return false
end
}.call
return @_windows_admin
end
# Checks if the user running Vagrant on Windows is a member of the
@ -70,14 +77,17 @@ module Vagrant
#
# @return [Boolean]
def windows_hyperv_admin?
return @_windows_hyperv_admin if defined?(@_windows_hyperv_admin)
@_windows_hyperv_admin = -> {
begin
username = ENV["USERNAME"]
process = Subprocess.execute("net", "localgroup", "Hyper-V Administrators")
output = process.stdout.chomp
return output.include?(username)
return process.stdout.include?(username)
rescue Errors::CommandUnavailableWindows
return false
end
}.call
return @_windows_hyperv_admin
end
# This takes any path and converts it from a Windows path to a
@ -125,16 +135,18 @@ module Vagrant
# directory runs a different filesystem than the root directory.
# However, this works in many cases.
def fs_case_sensitive?
Dir.mktmpdir("vagrant") do |tmp_dir|
tmp_file = File.join(tmp_dir, "FILE")
return @_fs_case_sensitive if defined?(@_fs_case_sensitive)
@_fs_case_sensitive = Dir.mktmpdir("vagrant-fs-case-sensitive") do |dir|
tmp_file = File.join(dir, "FILE")
File.open(tmp_file, "w") do |f|
f.write("foo")
end
# The filesystem is case sensitive if the lowercased version
# of the filename is NOT reported as existing.
!File.file?(File.join(tmp_dir, "file"))
!File.file?(File.join(dir, "file"))
end
return @_fs_case_sensitive
end
# This expands the path and ensures proper casing of each part
@ -197,18 +209,31 @@ module Vagrant
# Returns a boolean noting whether the terminal supports color.
# output.
def terminal_supports_colors?
if windows?
return true if ENV.key?("ANSICON")
return true if cygwin?
return true if ENV["TERM"] == "cygwin"
return false
end
return @_terminal_supports_colors if defined?(@_terminal_supports_colors)
@_terminal_supports_colors = -> {
if windows?
return true if ENV.key?("ANSICON")
return true if cygwin?
return true if ENV["TERM"] == "cygwin"
return false
end
true
return true
}.call
return @_terminal_supports_colors
end
def platform
RbConfig::CONFIG["host_os"].downcase
return @_platform if defined?(@_platform)
@_platform = RbConfig::CONFIG["host_os"].downcase
return @_platform
end
# @private
# Reset the cached values for platform. This is not considered a public
# API and should only be used for testing.
def reset!
instance_variables.each(&method(:remove_instance_variable))
end
end
end

View File

@ -14,7 +14,7 @@ module VagrantPlugins
o.separator "Options:"
o.separator ""
o.on("-f", "--force", "Destroy without confirmation.") do |f|
o.on("-f", "--force", "Remove without confirmation.") do |f|
options[:force] = f
end

View File

@ -86,6 +86,8 @@ module VagrantPlugins
end
def update_vms(argv, provider, download_options)
machines = {}
with_target_vms(argv, provider: provider) do |machine|
if !machine.config.vm.box
machine.ui.output(I18n.t(
@ -100,6 +102,14 @@ module VagrantPlugins
next
end
name = machine.box.name
provider = machine.box.provider
version = machine.config.vm.box_version || machine.box.version
machines["#{name}_#{provider}_#{version}"] = machine
end
machines.each do |_, machine|
box = machine.box
version = machine.config.vm.box_version
# Get download options from machine configuration if not specified

View File

@ -10,7 +10,7 @@ module VagrantPlugins
options[:force] = false
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant destroy [options] [name]"
o.banner = "Usage: vagrant destroy [options] [name|id]"
o.separator ""
o.separator "Options:"
o.separator ""

View File

@ -12,7 +12,7 @@ module VagrantPlugins
options[:force] = false
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant halt [options] [name]"
o.banner = "Usage: vagrant halt [options] [name|id]"
o.separator ""
o.separator "Options:"
o.separator ""

View File

@ -22,6 +22,10 @@ module VagrantPlugins
o.separator "Options:"
o.separator ""
o.on("--box-version VERSION", "Version of the box to add") do |f|
options[:box_version] = f
end
o.on("-f", "--force", "Overwrite existing Vagrantfile") do |f|
options[:force] = f
end
@ -54,8 +58,10 @@ module VagrantPlugins
template_path = ::Vagrant.source_root.join(template)
contents = Vagrant::Util::TemplateRenderer.render(template_path,
box_name: argv[0] || "base",
box_url: argv[1])
box_name: argv[0] || "base",
box_url: argv[1],
box_version: options[:box_version],
)
if save_path
# Write out the contents

View File

@ -1,9 +1,12 @@
require "rest_client"
require "vagrant/util/downloader"
require "vagrant/util/presence"
module VagrantPlugins
module LoginCommand
class Client
include Vagrant::Util::Presence
# Initializes a login client with the given Vagrant::Environment.
#
# @param [Vagrant::Environment] env
@ -50,6 +53,7 @@ module VagrantPlugins
proxy = nil
proxy ||= ENV["HTTPS_PROXY"] || ENV["https_proxy"]
proxy ||= ENV["HTTP_PROXY"] || ENV["http_proxy"]
RestClient.proxy = proxy
response = RestClient::Request.execute(
method: :post,
@ -87,7 +91,21 @@ module VagrantPlugins
#
# @return [String]
def token
if ENV["ATLAS_TOKEN"] && !ENV["ATLAS_TOKEN"].empty?
if present?(ENV["ATLAS_TOKEN"]) && token_path.exist?
@env.ui.warn <<-EOH.strip
Vagrant detected both the ATLAS_TOKEN environment variable and a Vagrant login
token are present on this system. The ATLAS_TOKEN environment variable takes
precedence over the locally stored token. To remove this error, either unset
the ATLAS_TOKEN environment variable or remove the login token stored on disk:
~/.vagrant.d/data/vagrant_login_token
In general, the ATLAS_TOKEN is more preferred because it is respected by all
HashiCorp products.
EOH
end
if present?(ENV["ATLAS_TOKEN"])
@logger.debug("Using authentication token from environment variable")
return ENV["ATLAS_TOKEN"]
end

View File

@ -11,7 +11,7 @@ module VagrantPlugins
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant package [options] [name]"
o.banner = "Usage: vagrant package [options] [name|id]"
o.separator ""
o.separator "Options:"
o.separator ""

View File

@ -5,7 +5,7 @@ module VagrantPlugins
def build_install_opts(o, options)
options[:plugin_sources] = [
"https://rubygems.org",
"http://gems.hashicorp.com",
"https://gems.hashicorp.com",
]
o.on("--entry-point NAME", String,

View File

@ -43,7 +43,7 @@ module VagrantPlugins
# Clear the sources so that installation uses custom sources
old_sources = Gem.sources
Gem.sources = Gem.default_sources
Gem.sources << "http://gems.hashicorp.com"
Gem.sources << "https://gems.hashicorp.com"
# Use a silent UI so that we have no output
Gem::DefaultUserInteraction.use_ui(Gem::SilentUI.new) do

View File

@ -15,7 +15,7 @@ module VagrantPlugins
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant port [options] [name]"
o.banner = "Usage: vagrant port [options] [name|id]"
o.separator ""
o.separator "Options:"
o.separator ""

View File

@ -11,7 +11,7 @@ module VagrantPlugins
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant rdp [options] [name] [-- extra args]"
o.banner = "Usage: vagrant rdp [options] [name|id] [-- extra args]"
end
# Parse out the extra args to send to the RDP client, which

View File

@ -1,24 +1,38 @@
require 'optparse'
require Vagrant.source_root.join("plugins/commands/up/start_mixins")
module VagrantPlugins
module CommandResume
class Command < Vagrant.plugin("2", :command)
# We assume that the `up` plugin exists and that we'll have access
# to this.
include VagrantPlugins::CommandUp::StartMixins
def self.synopsis
"resume a suspended vagrant machine"
end
def execute
options = {}
options[:provision_ignore_sentinel] = false
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant resume [vm-name]"
o.separator ""
build_start_options(o, options)
end
# Parse the options
argv = parse_options(opts)
return if !argv
# Validate the provisioners
validate_provisioner_flags!(options, argv)
@logger.debug("'resume' each target VM...")
with_target_vms(argv) do |machine|
machine.action(:resume)
machine.action(:resume, options)
end
# Success, exit status 0

View File

@ -1,6 +1,10 @@
require 'json'
require 'optparse'
require 'vagrant'
require Vagrant.source_root.join("plugins/commands/up/start_mixins")
require_relative "push_shared"
module VagrantPlugins
@ -8,19 +12,30 @@ module VagrantPlugins
module Command
class Pop < Vagrant.plugin("2", :command)
include PushShared
include VagrantPlugins::CommandUp::StartMixins
def execute
options = {}
options[:snapshot_delete] = true
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant snapshot pop [options] [vm-name]"
o.separator ""
build_start_options(o, options)
o.separator "Restore state that was pushed with `vagrant snapshot push`."
o.on("--no-delete", "Don't delete the snapshot after the restore") do
options[:snapshot_delete] = false
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
return shared_exec(argv, method(:pop))
# Validate the provisioners
validate_provisioner_flags!(options, argv)
return shared_exec(argv, method(:pop), options)
end
end
end

View File

@ -4,7 +4,7 @@ module VagrantPlugins
module CommandSnapshot
module Command
module PushShared
def shared_exec(argv, m)
def shared_exec(argv, m, opts={})
with_target_vms(argv) do |vm|
if !vm.id
vm.ui.info("Not created. Cannot push snapshot state.")
@ -12,7 +12,7 @@ module VagrantPlugins
end
vm.env.lock("machine-snapshot-stack") do
m.call(vm)
m.call(vm, opts)
end
end
@ -20,14 +20,14 @@ module VagrantPlugins
0
end
def push(machine)
def push(machine, opts={})
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)
def pop(machine, opts={})
# By reverse sorting, we should be able to find the first
# pushed snapshot.
name = nil
@ -45,11 +45,9 @@ module VagrantPlugins
return
end
# Restore the snapshot and tell the provider to delete it as well.
machine.action(
:snapshot_restore,
snapshot_name: name,
snapshot_delete: true)
# Restore the snapshot and tell the provider to delete it, if required
opts[:snapshot_name] = name
machine.action(:snapshot_restore, opts)
end
end
end

View File

@ -1,15 +1,23 @@
require 'optparse'
require 'vagrant'
require Vagrant.source_root.join("plugins/commands/up/start_mixins")
module VagrantPlugins
module CommandSnapshot
module Command
class Restore < Vagrant.plugin("2", :command)
include VagrantPlugins::CommandUp::StartMixins
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant snapshot restore [options] [vm-name] <name>"
o.separator ""
build_start_options(o, options)
o.separator "Restore a snapshot taken previously with snapshot save."
end
@ -21,9 +29,14 @@ module VagrantPlugins
help: opts.help.chomp
end
# Validate the provisioners
validate_provisioner_flags!(options, argv)
name = argv.pop
options[:snapshot_name] = name
with_target_vms(argv) do |vm|
vm.action(:snapshot_restore, snapshot_name: name)
vm.action(:snapshot_restore, options)
end
# Success, exit status 0

View File

@ -11,7 +11,7 @@ module VagrantPlugins
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant ssh [options] [name] [-- extra ssh args]"
o.banner = "Usage: vagrant ssh [options] [name|id] [-- extra ssh args]"
o.separator ""
o.separator "Options:"
o.separator ""
@ -55,6 +55,7 @@ module VagrantPlugins
exit_status = env[:ssh_run_exit_status] || 0
return exit_status
else
Vagrant::Bundler.instance.deinit
@logger.debug("Invoking `ssh` action on machine")
vm.action(:ssh, ssh_opts: ssh_opts)

View File

@ -15,7 +15,7 @@ module VagrantPlugins
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant ssh-config [options] [name]"
o.banner = "Usage: vagrant ssh-config [options] [name|id]"
o.separator ""
o.separator "Options:"
o.separator ""

View File

@ -9,7 +9,7 @@ module VagrantPlugins
def execute
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant status [name]"
o.banner = "Usage: vagrant status [name|id]"
end
# Parse the options

View File

@ -9,7 +9,7 @@ module VagrantPlugins
def execute
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant suspend [name]"
o.banner = "Usage: vagrant suspend [name|id]"
end
# Parse the options

View File

@ -22,7 +22,7 @@ module VagrantPlugins
options[:provision_ignore_sentinel] = false
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant up [options] [name]"
o.banner = "Usage: vagrant up [options] [name|id]"
o.separator ""
o.separator "Options:"
o.separator ""
@ -128,9 +128,9 @@ module VagrantPlugins
# 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|
with_target_vms(names) do |machine|
# Check if we have this machine in the index
entry = @env.machine_index.get(name.to_s)
entry = @env.machine_index.get(machine.name.to_s)
# Get the provider for this machine. This logic isn't completely
# straightforward. If we have a forced provider, we always use
@ -146,7 +146,7 @@ module VagrantPlugins
p = provider
p = entry.provider.to_sym if !p && entry
p = @env.default_provider(
machine: name.to_sym, check_usable: false) if !p
machine: machine.name.to_sym, check_usable: false) if !p
# Add it to the set
providers.add(p)

View File

@ -335,8 +335,8 @@ module VagrantPlugins
forward_agent: ssh_info[:forward_agent],
send_env: ssh_info[:forward_env],
keys: ssh_info[:private_key_path],
keys_only: true,
paranoid: false,
keys_only: ssh_info[:keys_only],
paranoid: ssh_info[:paranoid],
password: ssh_info[:password],
port: ssh_info[:port],
timeout: 15,

View File

@ -1,6 +1,6 @@
require "timeout"
require "log4r"
require "tempfile"
require "timeout"
require_relative "helper"
require_relative "shell"
@ -10,6 +10,8 @@ module VagrantPlugins
module CommunicatorWinRM
# Provides communication channel for Vagrant commands via WinRM.
class Communicator < Vagrant.plugin("2", :communicator)
include Vagrant::Util
def self.match?(machine)
# This is useless, and will likely be removed in the future (this
# whole method).
@ -142,6 +144,7 @@ module VagrantPlugins
opts[:good_exit] = Array(opts[:good_exit])
command = wrap_in_scheduled_task(command, opts[:interactive]) if opts[:elevated]
@logger.debug("#{opts[:shell]} executing:\n#{command}")
output = shell.send(opts[:shell], command, &block)
execution_output(output, opts)
end
@ -201,15 +204,12 @@ module VagrantPlugins
interactive: interactive,
})
guest_script_path = "c:/tmp/vagrant-elevated-shell.ps1"
file = Tempfile.new(["vagrant-elevated-shell", "ps1"])
begin
file.write(script)
file.fsync
file.close
upload(file.path, guest_script_path)
ensure
file.close
file.unlink
Tempfile.open(["vagrant-elevated-shell", "ps1"]) do |f|
f.binmode
f.write(script)
f.fsync
f.close
upload(f.path, guest_script_path)
end
# Convert to double byte unicode string then base64 encode

View File

@ -12,6 +12,7 @@ module VagrantPlugins
attr_accessor :transport
attr_accessor :ssl_peer_verification
attr_accessor :execution_time_limit
attr_accessor :basic_auth_only
def initialize
@username = UNSET_VALUE
@ -25,12 +26,13 @@ module VagrantPlugins
@transport = UNSET_VALUE
@ssl_peer_verification = UNSET_VALUE
@execution_time_limit = UNSET_VALUE
@basic_auth_only = UNSET_VALUE
end
def finalize!
@username = "vagrant" if @username == UNSET_VALUE
@password = "vagrant" if @password == UNSET_VALUE
@transport = :plaintext if @transport == UNSET_VALUE
@transport = :negotiate if @transport == UNSET_VALUE
@host = nil if @host == UNSET_VALUE
is_ssl = @transport == :ssl
@port = (is_ssl ? 5986 : 5985) if @port == UNSET_VALUE
@ -40,6 +42,7 @@ module VagrantPlugins
@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
@basic_auth_only = false if @basic_auth_only == UNSET_VALUE
end
def validate(machine)
@ -56,6 +59,9 @@ module VagrantPlugins
unless @ssl_peer_verification == true || @ssl_peer_verification == false
errors << "winrm.ssl_peer_verification must be a boolean."
end
unless @basic_auth_only == true || @basic_auth_only == false
errors << "winrm.basic_auth_only must be a boolean."
end
{ "WinRM" => errors }
end

View File

@ -54,20 +54,21 @@ module VagrantPlugins
end
def powershell(command, &block)
# 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)
command += "\r\nif ($?) { exit 0 } else { if($LASTEXITCODE) { exit $LASTEXITCODE } else { exit 1 } }"
execute_with_rescue(executor.method("run_powershell_script"), command, &block)
end
def cmd(command, &block)
execute_shell(command, :cmd, &block)
execute_with_rescue(executor.method("run_cmd"), command, &block)
end
def wql(query, &block)
execute_shell(query, :wql, &block)
retryable(tries: @config.max_tries, on: @@exceptions_to_retry_on, sleep: @config.retry_delay) do
handle_output(session.method("run_wql"), query, &block)
end
rescue => e
raise_winrm_exception(e, "run_wql", query)
end
def upload(from, to)
@ -82,42 +83,35 @@ module VagrantPlugins
protected
def execute_shell(command, shell=:powershell, &block)
raise Errors::WinRMInvalidShell, shell: shell unless [:powershell, :cmd, :wql].include?(shell)
begin
execute_shell_with_retry(command, shell, &block)
rescue => e
raise_winrm_exception(e, shell, command)
end
def execute_with_rescue(method, command, &block)
handle_output(method, command, &block)
rescue => e
raise_winrm_exception(e, method.name, command)
end
def execute_shell_with_retry(command, shell, &block)
retryable(tries: @config.max_tries, on: @@exceptions_to_retry_on, sleep: @config.retry_delay) do
@logger.debug("#{shell} executing:\n#{command}")
output = session.send(shell, command) do |out, err|
block.call(:stdout, out) if block_given? && out
block.call(:stderr, err) if block_given? && err
end
def handle_output(execute_method, command, &block)
output = execute_method.call(command) do |out, err|
block.call(:stdout, out) if block_given? && out
block.call(:stderr, err) if block_given? && err
end
@logger.debug("Output: #{output.inspect}")
@logger.debug("Output: #{output.inspect}")
# Verify that we didn't get a parser error, and if so we should
# set the exit code to 1. Parse errors return exit code 0 so we
# need to do this.
if output[:exitcode] == 0
(output[:data] || []).each do |data|
next if !data[:stderr]
if data[:stderr].include?("ParserError")
@logger.warn("Detected ParserError, setting exit code to 1")
output[:exitcode] = 1
break
end
# Verify that we didn't get a parser error, and if so we should
# set the exit code to 1. Parse errors return exit code 0 so we
# need to do this.
if output[:exitcode] == 0
(output[:data] || []).each do |data|
next if !data[:stderr]
if data[:stderr].include?("ParserError")
@logger.warn("Detected ParserError, setting exit code to 1")
output[:exitcode] = 1
break
end
end
return output
end
return output
end
def raise_winrm_exception(exception, shell = nil, command = nil)
@ -170,7 +164,7 @@ module VagrantPlugins
client = ::WinRM::WinRMWebService.new(endpoint, @config.transport.to_sym, endpoint_options)
client.set_timeout(@config.timeout)
client.toggle_nori_type_casting(:off) #we don't want coersion of types
client.logger = @logger
client
end
@ -178,11 +172,15 @@ module VagrantPlugins
@session ||= new_session
end
def executor
@executor ||= session.create_executor
end
def endpoint
case @config.transport.to_sym
when :ssl
"https://#{@host}:#{@port}/wsman"
when :plaintext
when :plaintext, :negotiate
"http://#{@host}:#{@port}/wsman"
else
raise Errors::WinRMInvalidTransport, transport: @config.transport
@ -194,8 +192,10 @@ module VagrantPlugins
pass: @password,
host: @host,
port: @port,
basic_auth_only: true,
no_ssl_peer_verification: !@config.ssl_peer_verification }
basic_auth_only: @config.basic_auth_only,
no_ssl_peer_verification: !@config.ssl_peer_verification,
retry_delay: @config.retry_delay,
retry_limit: @config.max_tries }
end
end #WinShell class
end

View File

@ -0,0 +1,14 @@
module VagrantPlugins
module GuestAmazon
module Cap
class Flavor
def self.flavor(machine)
# Amazon AMI is a frankenstien RHEL, mainly based on 6
# Maybe in the future if they incoporate RHEL 7 elements
# this should be extended to read /etc/os-release or similar
return :rhel
end
end
end
end
end

View File

@ -0,0 +1,9 @@
module VagrantPlugins
module GuestAmazon
class Guest < Vagrant.plugin("2", :guest)
def detect?(machine)
machine.communicate.test("grep 'Amazon Linux AMI' /etc/os-release")
end
end
end
end

View File

@ -0,0 +1,20 @@
require "vagrant"
module VagrantPlugins
module GuestAmazon
class Plugin < Vagrant.plugin("2")
name "Amazon Linux guest"
description "Amazon linux guest support."
guest(:amazon, :redhat) do
require_relative "guest"
Guest
end
guest_capability(:amazon, :flavor) do
require_relative "cap/flavor"
Cap::Flavor
end
end
end
end

View File

@ -3,12 +3,24 @@ module VagrantPlugins
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("hostnamectl set-hostname #{name}")
comm.sudo("sed -i 's@^\\(127[.]0[.]0[.]1[[:space:]]\\+\\)@\\1#{name} @' /etc/hosts")
end
comm = machine.communicate
if !comm.test("hostname -f | grep '^#{name}$'", sudo: false)
basename = name.split(".", 2)[0]
comm.sudo <<-EOH.gsub(/^ {14}/, "")
set -e
# Set hostname
hostnamectl set-hostname '#{basename}'
# Remove comments and blank lines from /etc/hosts
sed -i'' -e 's/#.*$//' -e '/^$/d' /etc/hosts
# Prepend ourselves to /etc/hosts
grep -w '#{name}' /etc/hosts || {
sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts
}
EOH
end
end
end

View File

@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
require "ipaddr"
require "socket"
require "tempfile"
require "vagrant/util/template_renderer"
require_relative "../../../../lib/vagrant/util/template_renderer"
module VagrantPlugins
module GuestArch
@ -10,25 +11,46 @@ module VagrantPlugins
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
comm = machine.communicate
networks.each do |network|
commands = ["set -e"]
interfaces = machine.guest.capability(:network_interfaces)
networks.each.with_index do |network, i|
network[:device] = interfaces[network[:interface]]
entry = TemplateRenderer.render("guests/arch/network_#{network[:type]}", options: network)
# Arch expects netmasks to be in the "24" or "64", but users may
# specify IPV4 netmasks like "255.255.255.0". This magic converts
# the netmask to the proper value.
if network[:netmask] && network[:netmask].to_s.include?(".")
network[:netmask] = (32-Math.log2((IPAddr.new(network[:netmask], Socket::AF_INET).to_i^0xffffffff)+1)).to_i
end
temp = Tempfile.new("vagrant")
temp.binmode
temp.write(entry)
temp.close
entry = TemplateRenderer.render("guests/arch/network_#{network[:type]}",
options: network,
)
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]} && netctl enable #{network[:device]}")
remote_path = "/tmp/vagrant-network-#{network[:device]}-#{Time.now.to_i}-#{i}"
Tempfile.open("vagrant-arch-configure-networks") do |f|
f.binmode
f.write(entry)
f.fsync
f.close
comm.upload(f.path, remote_path)
end
commands << <<-EOH.gsub(/^ {14}/, '')
# Configure #{network[:device]}
mv '#{remote_path}' '/etc/netctl/#{network[:device]}'
ip link set '#{network[:device]}' down
netctl restart '#{network[:device]}'
netctl enable '#{network[:device]}'
EOH
end
# Run all the network modification commands in one communicator call.
comm.sudo(commands.join("\n"))
end
end
end

View File

@ -0,0 +1,35 @@
module VagrantPlugins
module GuestArch
module Cap
class NFS
def self.nfs_client_installed(machine)
machine.communicate.test("pacman -Q nfs-utils")
end
def self.nfs_pre(machine)
comm = machine.communicate
# There is a bug in NFS where the rpcbind functionality is not started
# and it's not a dependency of nfs-utils. Read more here:
#
# https://bbs.archlinux.org/viewtopic.php?id=193410
#
comm.sudo <<-EOH.gsub(/^ {12}/, "")
set -e
systemctl enable rpcbind
systemctl start rpcbind
EOH
end
def self.nfs_client_install(machine)
comm = machine.communicate
comm.sudo <<-EOH.gsub(/^ {12}/, "")
set -e
pacman --noconfirm -Syy
pacman --noconfirm -S nfs-utils ntp
EOH
end
end
end
end
end

View File

@ -0,0 +1,16 @@
module VagrantPlugins
module GuestArch
module Cap
class RSync
def self.rsync_install(machine)
comm = machine.communicate
comm.sudo <<-EOH.gsub(/^ {12}/, '')
set -e
pacman -Sy --noconfirm
pacman -S --noconfirm rsync
EOH
end
end
end
end
end

View File

@ -0,0 +1,17 @@
module VagrantPlugins
module GuestArch
module Cap
class SMB
def self.smb_install(machine)
comm = machine.communicate
if !comm.test("test -f /usr/bin/mount.cifs")
comm.sudo <<-EOH.gsub(/^ {14}/, '')
pacman -Sy --noconfirm
pacman -S --noconfirm cifs-utils
EOH
end
end
end
end
end
end

View File

@ -6,20 +6,45 @@ module VagrantPlugins
name "Arch guest"
description "Arch guest support."
guest("arch", "linux") do
require File.expand_path("../guest", __FILE__)
guest(:arch, :linux) do
require_relative "guest"
Guest
end
guest_capability("arch", "change_host_name") do
guest_capability(:arch, :change_host_name) do
require_relative "cap/change_host_name"
Cap::ChangeHostName
end
guest_capability("arch", "configure_networks") do
guest_capability(:arch, :configure_networks) do
require_relative "cap/configure_networks"
Cap::ConfigureNetworks
end
guest_capability(:arch, :nfs_client_install) do
require_relative "cap/nfs"
Cap::NFS
end
guest_capability(:arch, :nfs_client_installed) do
require_relative "cap/nfs"
Cap::NFS
end
guest_capability(:arch, :nfs_pre) do
require_relative "cap/nfs"
Cap::NFS
end
guest_capability(:arch, :rsync_install) do
require_relative "cap/rsync"
Cap::RSync
end
guest_capability(:arch, :smb_install) do
require_relative "cap/smb"
Cap::SMB
end
end
end
end

View File

@ -3,7 +3,25 @@ module VagrantPlugins
module Cap
class ChangeHostName
def self.change_host_name(machine, name)
machine.communicate.sudo("hostnamectl set-hostname #{name}")
comm = machine.communicate
if !comm.test("hostname -f | grep '^#{name}$'", sudo: false)
basename = name.split(".", 2)[0]
comm.sudo <<-EOH.gsub(/^ {14}/, "")
set -e
# Set hostname
hostnamectl set-hostname '#{basename}'
# Remove comments and blank lines from /etc/hosts
sed -i'' -e 's/#.*$//' -e '/^$/d' /etc/hosts
# Prepend ourselves to /etc/hosts
grep -w '#{name}' /etc/hosts || {
sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts
}
EOH
end
end
end
end

View File

@ -1,4 +1,4 @@
require 'vagrant'
require "vagrant"
module VagrantPlugins
module GuestAtomic
@ -6,17 +6,17 @@ module VagrantPlugins
name "Atomic Host guest"
description "Atomic Host guest support."
guest("atomic", "fedora") do
require File.expand_path("../guest", __FILE__)
guest(:atomic, :fedora) do
require_relative "guest"
Guest
end
guest_capability("atomic", "change_host_name") do
guest_capability(:atomic, :change_host_name) do
require_relative "cap/change_host_name"
Cap::ChangeHostName
end
guest_capability("atomic", "docker_daemon_running") do
guest_capability(:atomic, :docker_daemon_running) do
require_relative "cap/docker"
Cap::Docker
end

View File

@ -1,10 +1,10 @@
module VagrantPlugins
module GuestOpenBSD
module GuestBSD
module Cap
class Halt
def self.halt(machine)
begin
machine.communicate.sudo("shutdown -p -h now")
machine.communicate.sudo("/sbin/shutdown -p now", shell: "sh")
rescue IOError
# Do nothing, because it probably means the machine shut down
# and SSH connection was lost.

View File

@ -0,0 +1,49 @@
require "shellwords"
require "vagrant/util/retryable"
module VagrantPlugins
module GuestBSD
module Cap
class NFS
extend Vagrant::Util::Retryable
# Mount the given NFS folder.
def self.mount_nfs_folder(machine, ip, folders)
comm = machine.communicate
folders.each do |name, opts|
# Mount each folder separately so we can retry.
commands = ["set -e"]
# Shellescape the paths in case they do not have special characters.
guest_path = Shellwords.escape(opts[:guestpath])
host_path = Shellwords.escape(opts[:hostpath])
# Build the list of mount options.
mount_opts = []
mount_opts << "nfsv#{opts[:nfs_version]}" if opts[:nfs_version]
mount_opts << "mntudp" if opts[:nfs_udp]
if opts[:mount_options]
mount_opts = mount_opts + opts[:mount_options].dup
end
mount_opts = mount_opts.join(",")
# Make the directory on the guest.
commands << "mkdir -p #{guest_path}"
# Perform the mount operation.
commands << "/sbin/mount -t nfs -o '#{mount_opts}' #{ip}:#{host_path} #{guest_path}"
# Run the command, raising a specific error.
retryable(on: Vagrant::Errors::NFSMountFailed, tries: 3, sleep: 5) do
machine.communicate.sudo(commands.join("\n"),
error_class: Vagrant::Errors::NFSMountFailed,
shell: "sh",
)
end
end
end
end
end
end
end

View File

@ -0,0 +1,66 @@
require "tempfile"
require "vagrant/util/shell_quote"
module VagrantPlugins
module GuestBSD
module Cap
class PublicKey
def self.insert_public_key(machine, contents)
comm = machine.communicate
contents = contents.strip << "\n"
remote_path = "/tmp/vagrant-insert-pubkey-#{Time.now.to_i}"
Tempfile.open("vagrant-bsd-insert-public-key") do |f|
f.binmode
f.write(contents)
f.fsync
f.close
comm.upload(f.path, remote_path)
end
# Use execute (not sudo) because we want to execute this as the SSH
# user (which is "vagrant" by default).
comm.execute <<-EOH.gsub(/^ {12}/, "")
set -e
mkdir -p ~/.ssh
chmod 0700 ~/.ssh
cat '#{remote_path}' >> ~/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys
rm -f '#{remote_path}'
EOH
end
def self.remove_public_key(machine, contents)
comm = machine.communicate
contents = contents.strip << "\n"
remote_path = "/tmp/vagrant-remove-pubkey-#{Time.now.to_i}"
Tempfile.open("vagrant-bsd-remove-public-key") do |f|
f.binmode
f.write(contents)
f.fsync
f.close
comm.upload(f.path, remote_path)
end
# Use execute (not sudo) because we want to execute this as the SSH
# user (which is "vagrant" by default).
comm.execute <<-EOH.sub(/^ {12}/, "")
set -e
if test -f ~/.ssh/authorized_keys; then
grep -v -x -f '#{remote_path}' ~/.ssh/authorized_keys > ~/.ssh/authorized_keys.tmp
mv ~/.ssh/authorized_keys.tmp ~/.ssh/authorized_keys
chmod 0600 ~/.ssh/authorized_keys
fi
rm -f '#{remote_path}'
EOH
end
end
end
end
end

View File

@ -0,0 +1,16 @@
module VagrantPlugins
module GuestBSD
module Cap
class VirtualBox
# BSD-based guests do not currently support VirtualBox synced folders.
# Instead of raising an error about a missing capability, this defines
# the capability and then provides a more detailed error message,
# linking to sources on the Internet where the problem is
# better-described.
def self.mount_virtualbox_shared_folder(machine, name, guestpath, options)
raise Vagrant::Errors::VirtualBoxMountNotSupportedBSD
end
end
end
end
end

View File

@ -0,0 +1,9 @@
module VagrantPlugins
module GuestBSD
class Guest < Vagrant.plugin("2", :guest)
def detect?(machine)
machine.communicate.test("uname -s | grep -i 'Darwin|BSD'")
end
end
end
end

View File

@ -0,0 +1,40 @@
require "vagrant"
module VagrantPlugins
module GuestBSD
class Plugin < Vagrant.plugin("2")
name "BSD-based guest"
description "BSD-based guest support."
guest(:bsd) do
require_relative "guest"
Guest
end
guest_capability(:bsd, :halt) do
require_relative "cap/halt"
Cap::Halt
end
guest_capability(:bsd, :insert_public_key) do
require_relative "cap/public_key"
Cap::PublicKey
end
guest_capability(:bsd, :mount_nfs_folder) do
require_relative "cap/nfs"
Cap::NFS
end
guest_capability(:bsd, :mount_virtualbox_shared_folder) do
require_relative "cap/virtualbox"
Cap::VirtualBox
end
guest_capability(:bsd, :remove_public_key) do
require_relative "cap/public_key"
Cap::PublicKey
end
end
end
end

View File

@ -3,10 +3,16 @@ module VagrantPlugins
module Cap
class ChangeHostName
def self.change_host_name(machine, name)
machine.communicate.tap do |comm|
if !comm.test("sudo hostname --fqdn | grep '#{name}'")
comm.sudo("hostname #{name.split('.')[0]}")
end
comm = machine.communicate
if !comm.test("hostname -f | grep '^#{name}$'", sudo: false)
basename = name.split(".", 2)[0]
comm.sudo("hostname '#{basename}'")
# Note that when working with CoreOS, we explicitly do not add the
# entry to /etc/hosts because this file does not exist on CoreOS.
# We could create it, but the recommended approach on CoreOS is to
# use Fleet to manage /etc/hosts files.
end
end
end

View File

@ -1,6 +1,6 @@
require "tempfile"
require "vagrant/util/template_renderer"
require_relative "../../../../lib/vagrant/util/template_renderer"
module VagrantPlugins
module GuestCoreOS
@ -10,64 +10,76 @@ module VagrantPlugins
def self.configure_networks(machine, networks)
machine.communicate.tap do |comm|
# Disable default etcd
comm.sudo("systemctl stop etcd")
# Read network interface names
interfaces = []
comm.sudo("ifconfig | grep 'enp0\\|ens' | cut -f1 -d:") do |_, result|
comm.sudo("ifconfig | grep '(e[n,t][h,s,p][[:digit:]]([a-z][[:digit:]])?' | cut -f1 -d:") do |_, result|
interfaces = result.split("\n")
end
# Configure interfaces
# FIXME: fix matching of interfaces with IP adresses
networks.each do |network|
comm.sudo("ifconfig #{interfaces[network[:interface].to_i]} #{network[:ip]} netmask #{network[:netmask]}")
end
primary_machine_config = machine.env.active_machines.first
primary_machine = machine.env.machine(*primary_machine_config, true)
get_ip = lambda do |machine|
ip = nil
machine.config.vm.networks.each do |type, opts|
if type == :private_network && opts[:ip]
ip = opts[:ip]
break
end
end
ip
end
primary_machine_ip = get_ip.(primary_machine)
current_ip = get_ip.(machine)
primary_machine_ip = get_ip(primary_machine)
current_ip = get_ip(machine)
if current_ip == primary_machine_ip
entry = TemplateRenderer.render("guests/coreos/etcd.service", options: {
my_ip: current_ip
})
my_ip: current_ip,
})
else
connection_string = "#{primary_machine_ip}:7001"
entry = TemplateRenderer.render("guests/coreos/etcd.service", options: {
connection_string: connection_string,
my_ip: current_ip
my_ip: current_ip,
})
end
Tempfile.open("vagrant") do |temp|
temp.binmode
temp.write(entry)
temp.close
comm.upload(temp.path, "/tmp/etcd-cluster.service")
Tempfile.open("vagrant-coreos-configure-networks") do |f|
f.binmode
f.write(entry)
f.fsync
f.close
comm.upload(f.path, "/tmp/etcd-cluster.service")
end
comm.sudo("mv /tmp/etcd-cluster.service /media/state/units/")
comm.sudo("systemctl restart local-enable.service")
# Build a list of commands
commands = []
# Restart default etcd
comm.sudo("systemctl start etcd")
# Stop default systemd
commands << "systemctl stop etcd"
# Configure interfaces
# FIXME: fix matching of interfaces with IP adresses
networks.each do |network|
iface = interfaces[network[:interface].to_i]
commands << "ifconfig #{iface} #{network[:ip]} netmask #{network[:netmask]}".squeeze(" ")
end
commands << <<-EOH.gsub(/^ {14}/, '')
mv /tmp/etcd-cluster.service /media/state/units/
systemctl restart local-enable.service
# Restart default etcd
systemctl start etcd
EOH
# Run all network configuration commands in one communicator session.
comm.sudo(commands.join("\n"))
end
end
private
def self.get_ip(machine)
ip = nil
machine.config.vm.networks.each do |type, opts|
if type == :private_network && opts[:ip]
ip = opts[:ip]
break
end
end
ip
end
end
end
end

View File

@ -1,3 +1,5 @@
require "vagrant"
module VagrantPlugins
module GuestCoreOS
class Guest < Vagrant.plugin("2", :guest)

View File

@ -6,22 +6,22 @@ module VagrantPlugins
name "CoreOS guest"
description "CoreOS guest support."
guest("coreos", "linux") do
require File.expand_path("../guest", __FILE__)
guest(:coreos, :linux) do
require_relative "guest"
Guest
end
guest_capability("coreos", "change_host_name") do
guest_capability(:coreos, :change_host_name) do
require_relative "cap/change_host_name"
Cap::ChangeHostName
end
guest_capability("coreos", "configure_networks") do
guest_capability(:coreos, :configure_networks) do
require_relative "cap/configure_networks"
Cap::ConfigureNetworks
end
guest_capability("coreos", "docker_daemon_running") do
guest_capability(:coreos, :docker_daemon_running) do
require_relative "cap/docker"
Cap::Docker
end

View File

@ -3,13 +3,34 @@ module VagrantPlugins
module Cap
class ChangeHostName
def self.change_host_name(machine, name)
if !machine.communicate.test("hostname -f | grep '^#{name}$' || hostname -s | grep '^#{name}$'")
machine.communicate.sudo("scutil --set ComputerName #{name}")
machine.communicate.sudo("scutil --set HostName #{name}")
# LocalHostName shouldn't contain dots.
# It is used by Bonjour and visible through file sharing services.
machine.communicate.sudo("scutil --set LocalHostName #{name.gsub(/\.+/, '')}")
machine.communicate.sudo("hostname #{name}")
comm = machine.communicate
if !comm.test("hostname -f | grep '^#{name}$'", sudo: false)
basename = name.split(".", 2)[0]
comm.sudo <<-EOH.gsub(/^ {14}/, '')
set -e
# Set hostname
scutil --set ComputerName '#{name}'
scutil --set HostName '#{name}'
# LocalHostName should not contain dots - it is used by Bonjour and
# visible through file sharing services.
scutil --set LocalHostName '#{basename}'
hostname '#{name}'
# Remove comments and blank lines from /etc/hosts
sed -i'' -e 's/#.*$//' /etc/hosts
sed -i'' -e '/^$/d' /etc/hosts
# Prepend ourselves to /etc/hosts - sed on bsd is sad
grep -w '#{name}' /etc/hosts || {
echo -e '127.0.0.1\\t#{name}\\t#{basename}' | cat - /etc/hosts > /tmp/tmp-hosts
mv /tmp/tmp-hosts /etc/hosts
}
EOH
end
end
end

View File

@ -3,16 +3,16 @@ module VagrantPlugins
module Cap
module ChooseAddressableIPAddr
def self.choose_addressable_ip_addr(machine, possible)
machine.communicate.tap do |comm|
possible.each do |ip|
command = "ping -c1 -t1 #{ip}"
if comm.test(command)
return ip
end
comm = machine.communicate
possible.each do |ip|
if comm.test("ping -c1 -t1 #{ip}")
return ip
end
end
nil
# If we got this far, there are no addressable IPs
return nil
end
end
end

View File

@ -35,12 +35,19 @@ module VagrantPlugins
end
network_type = network[:type].to_sym
if network_type == :static
command = "networksetup -setmanual \"#{service_name}\" #{network[:ip]} #{network[:netmask]}"
elsif network_type == :dhcp
case network_type.to_sym
when :static
command = "networksetup -setmanual \"#{service_name}\" #{network[:ip]} #{network[:netmask]} #{network[:router]}"
when :static6
command = "networksetup -setv6manual \"#{service_name}\" #{network[:ip]} #{network[:netmask]} #{network[:router]}"
when :dhcp
command = "networksetup -setdhcp \"#{service_name}\""
when :dhcp6
# This is not actually possible yet in Vagrant, but when we do
# enable IPv6 across the board, Darwin will already have support.
command = "networksetup -setv6automatic \"#{service_name}\""
else
raise "#{network_type} network type is not supported, try static or dhcp"
raise Vagrant::Errors::NetworkTypeNotSupported, type: network_type
end
machine.communicate.sudo(command)
@ -65,7 +72,7 @@ module VagrantPlugins
ints = ::IO.read(tmp_ints)
ints.split(/\n\n/m).each do |i|
if i.match(/Hardware/) && i.match(/Ethernet/)
# Ethernet, should be 2 lines,
# Ethernet, should be 2 lines,
# (3) Thunderbolt Ethernet
# (Hardware Port: Thunderbolt Ethernet, Device: en1)

View File

@ -4,7 +4,9 @@ module VagrantPlugins
class Halt
def self.halt(machine)
begin
machine.communicate.sudo("shutdown -h now")
# Darwin does not support the `-p` option like the rest of the
# BSD-based guests, so it needs its own cap.
machine.communicate.sudo("/sbin/shutdown -h now")
rescue IOError
# Do nothing because SSH connection closed and it probably
# means the VM just shut down really fast.

View File

@ -1,21 +0,0 @@
require "vagrant/util/shell_quote"
module VagrantPlugins
module GuestDarwin
module Cap
class InsertPublicKey
def self.insert_public_key(machine, contents)
contents = contents.chomp
contents = Vagrant::Util::ShellQuote.escape(contents, "'")
machine.communicate.tap do |comm|
comm.execute("mkdir -p ~/.ssh")
comm.execute("chmod 0700 ~/.ssh")
comm.execute("printf '#{contents}\\n' >> ~/.ssh/authorized_keys")
comm.execute("chmod 0600 ~/.ssh/authorized_keys")
end
end
end
end
end
end

View File

@ -1,37 +0,0 @@
require "vagrant/util/retryable"
module VagrantPlugins
module GuestDarwin
module Cap
class MountNFSFolder
extend Vagrant::Util::Retryable
def self.mount_nfs_folder(machine, ip, folders)
folders.each do |name, opts|
# Expand the guest path so we can handle things like "~/vagrant"
expanded_guest_path = machine.guest.capability(
:shell_expand_guest_path, opts[:guestpath])
# Create the folder
machine.communicate.sudo("mkdir -p #{expanded_guest_path}")
# Figure out any options
mount_opts = ["vers=#{opts[:nfs_version]}"]
mount_opts << "udp" if opts[:nfs_udp]
if opts[:mount_options]
mount_opts = opts[:mount_options].dup
end
mount_command = "mount -t nfs " +
"-o '#{mount_opts.join(",")}' " +
"'#{ip}:#{opts[:hostpath]}' '#{expanded_guest_path}'"
retryable(on: Vagrant::Errors::DarwinNFSMountFailed, tries: 10, sleep: 5) do
machine.communicate.sudo(
mount_command,
error_class: Vagrant::Errors::DarwinNFSMountFailed)
end
end
end
end
end
end
end

View File

@ -1,21 +0,0 @@
require "vagrant/util/shell_quote"
module VagrantPlugins
module GuestDarwin
module Cap
class RemovePublicKey
def self.remove_public_key(machine, contents)
contents = contents.chomp
contents = Vagrant::Util::ShellQuote.escape(contents, "'")
machine.communicate.tap do |comm|
if comm.test("test -f ~/.ssh/authorized_keys")
comm.execute(
"sed -i '' '/^.*#{contents}.*$/d' ~/.ssh/authorized_keys")
end
end
end
end
end
end
end

View File

@ -1,3 +1,5 @@
require "shellwords"
module VagrantPlugins
module GuestDarwin
module Cap
@ -11,9 +13,8 @@ module VagrantPlugins
end
def self.rsync_pre(machine, opts)
machine.communicate.tap do |comm|
comm.sudo("mkdir -p '#{opts[:guestpath]}'")
end
guest_path = Shellwords.escape(opts[:guestpath])
machine.communicate.sudo("mkdir -p #{guest_path}")
end
def self.rsync_post(machine, opts)
@ -21,8 +22,10 @@ module VagrantPlugins
return
end
guest_path = Shellwords.escape(opts[:guestpath])
machine.communicate.sudo(
"find '#{opts[:guestpath]}' '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " +
"find #{guest_path} '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " +
"xargs -0 chown #{opts[:owner]}:#{opts[:group]}")
end
end

View File

@ -1,4 +1,4 @@
require 'vagrant/util/template_renderer'
require "vagrant"
module VagrantPlugins
module GuestDarwin

View File

@ -6,82 +6,67 @@ module VagrantPlugins
name "Darwin guest"
description "Darwin guest support."
guest("darwin") do
require File.expand_path("../guest", __FILE__)
guest(:darwin, :bsd) do
require_relative "guest"
Guest
end
guest_capability("darwin", "change_host_name") do
guest_capability(:darwin, :change_host_name) do
require_relative "cap/change_host_name"
Cap::ChangeHostName
end
guest_capability("darwin", "choose_addressable_ip_addr") do
guest_capability(:darwin, :choose_addressable_ip_addr) do
require_relative "cap/choose_addressable_ip_addr"
Cap::ChooseAddressableIPAddr
end
guest_capability("darwin", "configure_networks") do
guest_capability(:darwin, :configure_networks) do
require_relative "cap/configure_networks"
Cap::ConfigureNetworks
end
guest_capability("darwin", "halt") do
guest_capability(:darwin, :halt) do
require_relative "cap/halt"
Cap::Halt
end
guest_capability("darwin", "insert_public_key") do
require_relative "cap/insert_public_key"
Cap::InsertPublicKey
end
guest_capability("darwin", "mount_nfs_folder") do
require_relative "cap/mount_nfs_folder"
Cap::MountNFSFolder
end
guest_capability("darwin", "mount_smb_shared_folder") do
guest_capability(:darwin, :mount_smb_shared_folder) do
require_relative "cap/mount_smb_shared_folder"
Cap::MountSMBSharedFolder
end
guest_capability("darwin", "mount_vmware_shared_folder") do
guest_capability(:darwin, :mount_vmware_shared_folder) do
require_relative "cap/mount_vmware_shared_folder"
Cap::MountVmwareSharedFolder
end
guest_capability("darwin", "remove_public_key") do
require_relative "cap/remove_public_key"
Cap::RemovePublicKey
end
guest_capability("darwin", "rsync_installed") do
guest_capability(:darwin, :rsync_installed) do
require_relative "cap/rsync"
Cap::RSync
end
guest_capability("darwin", "rsync_command") do
guest_capability(:darwin, :rsync_command) do
require_relative "cap/rsync"
Cap::RSync
end
guest_capability("darwin", "rsync_post") do
guest_capability(:darwin, :rsync_post) do
require_relative "cap/rsync"
Cap::RSync
end
guest_capability("darwin", "rsync_pre") do
guest_capability(:darwin, :rsync_pre) do
require_relative "cap/rsync"
Cap::RSync
end
guest_capability("darwin", "shell_expand_guest_path") do
guest_capability(:darwin, :shell_expand_guest_path) do
require_relative "cap/shell_expand_guest_path"
Cap::ShellExpandGuestPath
end
guest_capability("darwin", "verify_vmware_hgfs") do
guest_capability(:darwin, :verify_vmware_hgfs) do
require_relative "cap/verify_vmware_hgfs"
Cap::VerifyVmwareHgfs
end

View File

@ -3,91 +3,43 @@ module VagrantPlugins
module Cap
class ChangeHostName
def self.change_host_name(machine, name)
new(machine, name).change!
end
comm = machine.communicate
attr_reader :machine, :new_hostname
if !comm.test("hostname -f | grep '^#{name}$'", sudo: false)
basename = name.split(".", 2)[0]
comm.sudo <<-EOH.gsub(/^ {14}/, '')
# Ensure exit on command error
set -e
def initialize(machine, new_hostname)
@machine = machine
@new_hostname = new_hostname
end
# Set the hostname
echo '#{basename}' > /etc/hostname
hostname -F /etc/hostname
def change!
return unless should_change?
if command -v hostnamectl; then
hostnamectl set-hostname '#{basename}'
fi
update_etc_hostname
update_etc_hosts
refresh_hostname_service
update_mailname
renew_dhcp
end
# Remove comments and blank lines from /etc/hosts
sed -i'' -e 's/#.*$//' -e '/^$/d' /etc/hosts
def should_change?
new_hostname != current_hostname
end
# Prepend ourselves to /etc/hosts
grep -w '#{name}' /etc/hosts || {
sed -i'' '1i 127.0.0.1\\t#{name}\\t#{basename}' /etc/hosts
}
def current_hostname
@current_hostname ||= get_current_hostname
end
# Update mailname
echo '#{name}' > /etc/mailname
def get_current_hostname
hostname = ""
sudo "hostname -f" do |type, data|
hostname = data.chomp if type == :stdout && hostname.empty?
# Restart hostname services
if test -f /etc/init.d/hostname; then
/etc/init.d/hostname start || true
fi
if test -f /etc/init.d/hostname.sh; then
/etc/init.d/hostname.sh start || true
fi
EOH
end
hostname
end
def update_etc_hostname
sudo("echo '#{short_hostname}' > /etc/hostname")
end
# /etc/hosts should resemble:
# 127.0.0.1 localhost
# 127.0.1.1 host.fqdn.com host.fqdn host
def update_etc_hosts
if test("grep '#{current_hostname}' /etc/hosts")
# Current hostname entry is in /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}"
expression = ['s', search, replace, 'g'].join('@')
sudo("sed -ri '#{expression}' /etc/hosts")
else
# Current hostname entry isn't in /etc/hosts, just append it
sudo("echo '127.0.1.1 #{fqdn} #{short_hostname}' >>/etc/hosts")
end
end
def refresh_hostname_service
sudo("hostname -F /etc/hostname")
end
def update_mailname
sudo("hostname --fqdn > /etc/mailname")
end
def renew_dhcp
sudo("ifdown -a; ifup -a; ifup eth0")
end
def fqdn
new_hostname
end
def short_hostname
new_hostname.split('.').first
end
def sudo(cmd, &block)
machine.communicate.sudo(cmd, &block)
end
def test(cmd)
machine.communicate.test(cmd)
end
end
end

View File

@ -1,7 +1,6 @@
require 'set'
require 'tempfile'
require "tempfile"
require "vagrant/util/template_renderer"
require_relative "../../../../lib/vagrant/util/template_renderer"
module VagrantPlugins
module GuestDebian
@ -10,50 +9,62 @@ module VagrantPlugins
include Vagrant::Util
def self.configure_networks(machine, networks)
machine.communicate.tap do |comm|
# First, remove any previous network modifications
# from the interface file.
comm.sudo("sed -e '/^#VAGRANT-BEGIN/,$ d' /etc/network/interfaces > /tmp/vagrant-network-interfaces.pre")
comm.sudo("sed -ne '/^#VAGRANT-END/,$ p' /etc/network/interfaces | tac | sed -e '/^#VAGRANT-END/,$ d' | tac > /tmp/vagrant-network-interfaces.post")
comm = machine.communicate
# Accumulate the configurations to add to the interfaces file as
# well as what interfaces we're actually configuring since we use that
# later.
interfaces = Set.new
entries = []
networks.each do |network|
interfaces.add(network[:interface])
entry = TemplateRenderer.render("guests/debian/network_#{network[:type]}",
options: network)
commands = ["set -e"]
entries = []
interfaces = machine.guest.capability(:network_interfaces)
entries << entry
end
networks.each do |network|
network[:device] = interfaces[network[:interface]]
# Perform the careful dance necessary to reconfigure
# the network interfaces
temp = Tempfile.new("vagrant")
temp.binmode
temp.write(entries.join("\n"))
temp.close
comm.upload(temp.path, "/tmp/vagrant-network-entry")
# Bring down all the interfaces we're reconfiguring. By bringing down
# each specifically, we avoid reconfiguring eth0 (the NAT interface) so
# SSH never dies.
interfaces.each do |interface|
comm.sudo("/sbin/ifdown eth#{interface} 2> /dev/null")
comm.sudo("/sbin/ip addr flush dev eth#{interface} 2> /dev/null")
end
comm.sudo('cat /tmp/vagrant-network-interfaces.pre /tmp/vagrant-network-entry /tmp/vagrant-network-interfaces.post > /etc/network/interfaces')
comm.sudo('rm -f /tmp/vagrant-network-interfaces.pre /tmp/vagrant-network-entry /tmp/vagrant-network-interfaces.post')
# Bring back up each network interface, reconfigured
interfaces.each do |interface|
comm.sudo("/sbin/ifup eth#{interface}")
end
entry = TemplateRenderer.render("guests/debian/network_#{network[:type]}",
options: network,
)
entries << entry
end
Tempfile.open("vagrant-debian-configure-networks") do |f|
f.binmode
f.write(entries.join("\n"))
f.fsync
f.close
comm.upload(f.path, "/tmp/vagrant-network-entry")
end
networks.each do |network|
# Ubuntu 16.04+ returns an error when downing an interface that
# does not exist. The `|| true` preserves the behavior that older
# Ubuntu versions exhibit and Vagrant expects (GH-7155)
commands << "/sbin/ifdown '#{network[:device]}' || true"
commands << "/sbin/ip addr flush dev '#{network[:device]}'"
end
# Reconfigure /etc/network/interfaces.
commands << <<-EOH.gsub(/^ {12}/, "")
# Remove any previous network modifications from the interfaces file
sed -e '/^#VAGRANT-BEGIN/,$ d' /etc/network/interfaces > /tmp/vagrant-network-interfaces.pre
sed -ne '/^#VAGRANT-END/,$ p' /etc/network/interfaces | tac | sed -e '/^#VAGRANT-END/,$ d' | tac > /tmp/vagrant-network-interfaces.post
cat \\
/tmp/vagrant-network-interfaces.pre \\
/tmp/vagrant-network-entry \\
/tmp/vagrant-network-interfaces.post \\
> /etc/network/interfaces
rm -f /tmp/vagrant-network-interfaces.pre
rm -f /tmp/vagrant-network-entry
rm -f /tmp/vagrant-network-interfaces.post
EOH
# Bring back up each network interface, reconfigured.
networks.each do |network|
commands << "/sbin/ifup '#{network[:device]}'"
end
# Run all the commands in one session to prevent partial configuration
# due to a severed network.
comm.sudo(commands.join("\n"))
end
end
end

View File

@ -0,0 +1,16 @@
module VagrantPlugins
module GuestDebian
module Cap
class NFS
def self.nfs_client_install(machine)
comm = machine.communicate
comm.sudo <<-EOH.gsub(/^ {12}/, '')
set -e
apt-get -yqq update
apt-get -yqq install nfs-common portmap
EOH
end
end
end
end
end

View File

@ -1,14 +0,0 @@
module VagrantPlugins
module GuestDebian
module Cap
class NFSClient
def self.nfs_client_install(machine)
machine.communicate.tap do |comm|
comm.sudo("apt-get -y update")
comm.sudo("apt-get -y install nfs-common portmap")
end
end
end
end
end
end

View File

@ -3,10 +3,12 @@ module VagrantPlugins
module Cap
class RSync
def self.rsync_install(machine)
machine.communicate.tap do |comm|
comm.sudo("apt-get -y update")
comm.sudo("apt-get -y install rsync")
end
comm = machine.communicate
comm.sudo <<-EOH.gsub(/^ {14}/, '')
set -e
apt-get -yqq update
apt-get -yqq install rsync
EOH
end
end
end

View File

@ -3,13 +3,12 @@ module VagrantPlugins
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
comm = machine.communicate
if !comm.test("test -f /sbin/mount.cifs")
comm.sudo <<-EOH.gsub(/^ {14}/, '')
apt-get -yqq update
apt-get -yqq install cifs-utils
EOH
end
end
end

View File

@ -1,3 +1,5 @@
require "vagrant"
module VagrantPlugins
module GuestDebian
class Guest < Vagrant.plugin("2", :guest)

View File

@ -6,32 +6,32 @@ module VagrantPlugins
name "Debian guest"
description "Debian guest support."
guest("debian", "linux") do
require File.expand_path("../guest", __FILE__)
guest(:debian, :linux) do
require_relative "guest"
Guest
end
guest_capability("debian", "configure_networks") do
guest_capability(:debian, :configure_networks) do
require_relative "cap/configure_networks"
Cap::ConfigureNetworks
end
guest_capability("debian", "change_host_name") do
guest_capability(:debian, :change_host_name) do
require_relative "cap/change_host_name"
Cap::ChangeHostName
end
guest_capability("debian", "nfs_client_install") do
require_relative "cap/nfs_client"
Cap::NFSClient
guest_capability(:debian, :nfs_client_install) do
require_relative "cap/nfs"
Cap::NFS
end
guest_capability("debian", "rsync_install") do
guest_capability(:debian, :rsync_install) do
require_relative "cap/rsync"
Cap::RSync
end
guest_capability("debian", "smb_install") do
guest_capability(:debian, :smb_install) do
require_relative "cap/smb"
Cap::SMB
end

View File

@ -13,9 +13,9 @@ module VagrantPlugins
comm.execute("localcli storage nfs remove -v #{volume}")
end
mount_command = "localcli storage nfs add -H #{ip} -s '#{opts[:hostpath]}' -v '#{volume}'"
retryable(on: Vagrant::Errors::LinuxNFSMountFailed, tries: 5, sleep: 2) do
retryable(on: Vagrant::Errors::NFSMountFailed, tries: 5, sleep: 2) do
comm.execute(mount_command,
error_class: Vagrant::Errors::LinuxNFSMountFailed)
error_class: Vagrant::Errors::NFSMountFailed)
end
# symlink vmfs volume to :guestpath

View File

@ -6,27 +6,27 @@ module VagrantPlugins
name "ESXi guest."
description "ESXi guest support."
guest("esxi") do
require File.expand_path("../guest", __FILE__)
guest(:esxi) do
require_relative "guest"
Guest
end
guest_capability("esxi", "change_host_name") do
guest_capability(:esxi, :change_host_name) do
require_relative "cap/change_host_name"
Cap::ChangeHostName
end
guest_capability("esxi", "configure_networks") do
guest_capability(:esxi, :configure_networks) do
require_relative "cap/configure_networks"
Cap::ConfigureNetworks
end
guest_capability("esxi", "mount_nfs_folder") do
guest_capability(:esxi, :mount_nfs_folder) do
require_relative "cap/mount_nfs_folder"
Cap::MountNFSFolder
end
guest_capability("esxi", "halt") do
guest_capability(:esxi, :halt) do
require_relative "cap/halt"
Cap::Halt
end

View File

@ -1,75 +0,0 @@
module VagrantPlugins
module GuestFedora
module Cap
class ChangeHostName
def self.change_host_name(machine, name)
new(machine, name).change!
end
attr_reader :machine, :new_hostname
def initialize(machine, new_hostname)
@machine = machine
@new_hostname = new_hostname
end
def change!
return unless should_change?
update_etc_hostname
update_etc_hosts
refresh_hostname_service
end
def should_change?
new_hostname != current_hostname
end
def current_hostname
@current_hostname ||= get_current_hostname
end
def get_current_hostname
hostname = ""
sudo "hostname -f" do |type, data|
hostname = data.chomp if type == :stdout && hostname.empty?
end
hostname
end
def update_etc_hostname
sudo("echo '#{short_hostname}' > /etc/hostname")
end
# /etc/hosts should resemble:
# 127.0.0.1 localhost
# 127.0.1.1 host.fqdn.com host.fqdn host
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} \\3"
expression = ['s', search, replace, 'g'].join('@')
sudo("sed -ri '#{expression}' /etc/hosts")
end
def refresh_hostname_service
sudo("hostname -F /etc/hostname")
end
def fqdn
new_hostname
end
def short_hostname
new_hostname.split('.').first
end
def sudo(cmd, &block)
machine.communicate.sudo(cmd, &block)
end
end
end
end
end

View File

@ -1,134 +0,0 @@
require "set"
require "tempfile"
require "vagrant/util/retryable"
require "vagrant/util/template_renderer"
module VagrantPlugins
module GuestFedora
module Cap
class ConfigureNetworks
extend Vagrant::Util::Retryable
include Vagrant::Util
def self.configure_networks(machine, networks)
network_scripts_dir = machine.guest.capability("network_scripts_dir")
virtual = false
interface_names = Array.new
interface_names_by_slot = Array.new
machine.communicate.sudo("/usr/sbin/biosdevname &>/dev/null; echo $?") do |_, result|
# The above command returns:
# - '4' if /usr/sbin/biosdevname detects it is running in a virtual machine
# - '127' if /usr/sbin/biosdevname doesn't exist
virtual = true if ['4', '127'].include? result.chomp
end
if virtual
machine.communicate.sudo("ls /sys/class/net | egrep -v lo\\|docker") do |_, result|
interface_names = result.split("\n")
end
interface_names_by_slot = networks.map do |network|
"#{interface_names[network[:interface]]}"
end
else
machine.communicate.sudo("/usr/sbin/biosdevname -d | grep Kernel | cut -f2 -d: | sed -e 's/ //;'") do |_, result|
interface_names = result.split("\n")
end
interface_name_pairs = Array.new
interface_names.each do |interface_name|
machine.communicate.sudo("/usr/sbin/biosdevname --policy=all_ethN -i #{interface_name}") do |_, result|
interface_name_pairs.push([interface_name, result.gsub("\n", "")])
end
end
setting_interface_names = networks.map do |network|
"eth#{network[:interface]}"
end
interface_names_by_slot = interface_names.dup
interface_name_pairs.each do |interface_name, previous_interface_name|
if setting_interface_names.index(previous_interface_name) == nil
interface_names_by_slot.delete(interface_name)
end
end
end
# Read interface MAC addresses for later matching
mac_addresses = Array.new(interface_names.length)
interface_names.each_with_index do |ifname, index|
machine.communicate.sudo("cat /sys/class/net/#{ifname}/address") do |_, result|
mac_addresses[index] = result.strip
end
end
# Accumulate the configurations to add to the interfaces file as well
# as what interfaces we're actually configuring since we use that later.
interfaces = Set.new
networks.each do |network|
interface = nil
if network[:mac_address]
found_idx = mac_addresses.find_index(network[:mac_address])
# Ignore network if requested MAC address could not be found
next if found_idx.nil?
interface = interface_names[found_idx]
else
ifname_by_slot = interface_names_by_slot[network[:interface]-1]
# Don't overwrite if interface was already matched via MAC address
next if interfaces.include?(ifname_by_slot)
interface = ifname_by_slot
end
interfaces.add(interface)
network[:device] = interface
# Remove any previous vagrant configuration in this network
# interface's configuration files.
machine.communicate.sudo("touch #{network_scripts_dir}/ifcfg-#{interface}")
machine.communicate.sudo("sed -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' #{network_scripts_dir}/ifcfg-#{interface} > /tmp/vagrant-ifcfg-#{interface}")
machine.communicate.sudo("cat /tmp/vagrant-ifcfg-#{interface} > #{network_scripts_dir}/ifcfg-#{interface}")
machine.communicate.sudo("rm -f /tmp/vagrant-ifcfg-#{interface}")
# Render and upload the network entry file to a deterministic
# temporary location.
entry = TemplateRenderer.render("guests/fedora/network_#{network[:type]}",
options: network)
temp = Tempfile.new("vagrant")
temp.binmode
temp.write(entry)
temp.close
machine.communicate.upload(temp.path, "/tmp/vagrant-network-entry_#{interface}")
end
# Bring down all the interfaces we're reconfiguring. By bringing down
# each specifically, we avoid reconfiguring p7p (the NAT interface) so
# SSH never dies.
interfaces.each do |interface|
retryable(on: Vagrant::Errors::VagrantError, tries: 3, sleep: 2) do
machine.communicate.sudo(<<-SCRIPT, error_check: true)
cat /tmp/vagrant-network-entry_#{interface} >> #{network_scripts_dir}/ifcfg-#{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

@ -14,7 +14,7 @@ module VagrantPlugins
if version.nil?
return :fedora
else
return "fedora_#{version}".to_sym
return :"fedora_#{version}"
end
end
end

View File

@ -1,15 +0,0 @@
module VagrantPlugins
module GuestFedora
module Cap
class NetworkScriptsDir
# The path to the directory with the network configuration scripts.
# This is pulled out into its own directory since there are other
# operating systems (SUSE) which behave similarly but with a different
# path to the network scripts.
def self.network_scripts_dir(machine)
"/etc/sysconfig/network-scripts"
end
end
end
end
end

View File

@ -6,27 +6,12 @@ module VagrantPlugins
name "Fedora guest"
description "Fedora guest support."
guest("fedora", "redhat") do
require File.expand_path("../guest", __FILE__)
guest(:fedora, :redhat) do
require_relative "guest"
Guest
end
guest_capability("fedora", "change_host_name") do
require_relative "cap/change_host_name"
Cap::ChangeHostName
end
guest_capability("fedora", "configure_networks") do
require_relative "cap/configure_networks"
Cap::ConfigureNetworks
end
guest_capability("fedora", "network_scripts_dir") do
require_relative "cap/network_scripts_dir"
Cap::NetworkScriptsDir
end
guest_capability("fedora", "flavor") do
guest_capability(:fedora, :flavor) do
require_relative "cap/flavor"
Cap::Flavor
end

View File

@ -3,9 +3,26 @@ module VagrantPlugins
module Cap
class ChangeHostName
def self.change_host_name(machine, name)
if !machine.communicate.test("hostname -f | grep '^#{name}$' || hostname -s | grep '^#{name}$'", {shell: "sh"})
machine.communicate.sudo("sed -i '' 's/^hostname=.*$/hostname=#{name}/' /etc/rc.conf", {shell: "sh"})
machine.communicate.sudo("hostname #{name}", {shell: "sh"})
comm = machine.communicate
if !comm.test("hostname -f | grep '^#{name}$'", sudo: false, shell: "sh")
basename = name.split(".", 2)[0]
command = <<-EOH.gsub(/^ {14}/, '')
# Set the hostname
hostname '#{name}'
sed -i '' 's/^hostname=.*$/hostname=\"#{name}\"/' /etc/rc.conf
# Remove comments and blank lines from /etc/hosts
sed -i'' -e 's/#.*$//' /etc/hosts
sed -i'' -e '/^$/d' /etc/hosts
# Prepend ourselves to /etc/hosts
grep -w '#{name}' /etc/hosts || {
echo -e '127.0.0.1\\t#{name}\\t#{basename}' | cat - /etc/hosts > /tmp/tmp-hosts
mv /tmp/tmp-hosts /etc/hosts
}
EOH
comm.sudo(command, shell: "sh")
end
end
end

View File

@ -1,6 +1,6 @@
require "tempfile"
require "vagrant/util/template_renderer"
require_relative "../../../../lib/vagrant/util/template_renderer"
module VagrantPlugins
module GuestFreeBSD
@ -9,42 +9,57 @@ module VagrantPlugins
include Vagrant::Util
def self.configure_networks(machine, networks)
options = { shell: "sh" }
comm = machine.communicate
commands = []
interfaces = []
# Remove any previous network additions to the configuration file.
machine.communicate.sudo("sed -i '' -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' /etc/rc.conf", {shell: "sh"})
commands << "sed -i'' -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' /etc/rc.conf"
networks.each do |network|
# Determine the interface prefix...
command = "ifconfig -a | grep -o ^[0-9a-z]*"
result = ""
ifname = ""
machine.communicate.execute(command) do |type, data|
result << data if type == :stdout
if result.split(/\n/).any?{|i| i.match(/vtnet*/)}
ifname = "vtnet#{network[:interface]}"
else
ifname = "em#{network[:interface]}"
end
end
entry = TemplateRenderer.render("guests/freebsd/network_#{network[:type]}",
options: network, ifname: ifname)
# Write the entry to a temporary location
temp = Tempfile.new("vagrant")
temp.binmode
temp.write(entry)
temp.close
machine.communicate.upload(temp.path, "/tmp/vagrant-network-entry")
machine.communicate.sudo("su -m root -c 'cat /tmp/vagrant-network-entry >> /etc/rc.conf'", {shell: "sh"})
machine.communicate.sudo("rm -f /tmp/vagrant-network-entry", {shell: "sh"})
if network[:type].to_sym == :static
machine.communicate.sudo("ifconfig #{ifname} inet #{network[:ip]} netmask #{network[:netmask]}", {shell: "sh"})
elsif network[:type].to_sym == :dhcp
machine.communicate.sudo("dhclient #{ifname}", {shell: "sh"})
end
comm.sudo("ifconfig -a | grep -o ^[0-9a-z]* | grep -v '^lo'", options) do |_, stdout|
interfaces = stdout.split("\n")
end
networks.each.with_index do |network, i|
network[:device] = interfaces[network[:interface]]
entry = TemplateRenderer.render("guests/freebsd/network_#{network[:type]}",
options: network,
)
remote_path = "/tmp/vagrant-network-#{network[:device]}-#{Time.now.to_i}-#{i}"
Tempfile.open("vagrant-freebsd-configure-networks") do |f|
f.binmode
f.write(entry)
f.fsync
f.close
comm.upload(f.path, remote_path)
end
commands << <<-EOH.gsub(/^ {14}/, '')
cat '#{remote_path}' >> /etc/rc.conf
rm -f '#{remote_path}'
EOH
# If the network is DHCP, then we have to start the dhclient, unless
# it is already running. See GH-5852 for more information
if network[:type].to_sym == :dhcp
file = "/var/run/dhclient.#{network[:device]}.pid"
commands << <<-EOH.gsub(/^ {16}/, '')
if ! test -f '#{file}' || ! kill -0 $(cat '#{file}'); then
dhclient '#{network[:device]}'
fi
EOH
end
# For some reason, this returns status 1... every time
commands << "/etc/rc.d/netif restart '#{network[:device]}' || true"
end
comm.sudo(commands.join("\n"), options)
end
end
end

View File

@ -1,16 +0,0 @@
module VagrantPlugins
module GuestFreeBSD
module Cap
class Halt
def self.halt(machine)
begin
machine.communicate.sudo("shutdown -p now", {shell: "sh"})
rescue IOError
# Do nothing because SSH connection closed and it probably
# means the VM just shut down really fast.
end
end
end
end
end
end

View File

@ -1,21 +0,0 @@
require "vagrant/util/shell_quote"
module VagrantPlugins
module GuestFreeBSD
module Cap
class InsertPublicKey
def self.insert_public_key(machine, contents)
contents = Vagrant::Util::ShellQuote.escape(contents, "'")
contents = contents.gsub("\n", "\\n")
machine.communicate.tap do |comm|
comm.execute("mkdir -p ~/.ssh", shell: "sh")
comm.execute("chmod 0700 ~/.ssh", shell: "sh")
comm.execute("printf '#{contents}' >> ~/.ssh/authorized_keys", shell: "sh")
comm.execute("chmod 0600 ~/.ssh/authorized_keys", shell: "sh")
end
end
end
end
end
end

View File

@ -1,21 +0,0 @@
module VagrantPlugins
module GuestFreeBSD
module Cap
class MountNFSFolder
def self.mount_nfs_folder(machine, ip, folders)
folders.each do |name, opts|
if opts[:nfs_version]
nfs_version_mount_option="-o nfsv#{opts[:nfs_version]}"
end
machine.communicate.sudo("mkdir -p #{opts[:guestpath]}", {shell: "sh"})
machine.communicate.sudo(
"mount -t nfs #{nfs_version_mount_option} " +
"'#{ip}:#{opts[:hostpath]}' '#{opts[:guestpath]}'", {shell: "sh"})
end
end
end
end
end
end

View File

@ -1,21 +0,0 @@
require "vagrant/util/shell_quote"
module VagrantPlugins
module GuestFreeBSD
module Cap
class RemovePublicKey
def self.remove_public_key(machine, contents)
contents = contents.chomp
contents = Vagrant::Util::ShellQuote.escape(contents, "'")
machine.communicate.tap do |comm|
if comm.test("test -f ~/.ssh/authorized_keys")
comm.execute(
"sed -i .bak '/^.*#{contents}.*$/d' ~/.ssh/authorized_keys")
end
end
end
end
end
end
end

View File

@ -1,19 +1,11 @@
require "shellwords"
module VagrantPlugins
module GuestFreeBSD
module Cap
class RSync
def self.rsync_install(machine)
version = nil
machine.communicate.execute("uname -r") do |type, result|
version = result.split('.')[0].to_i if type == :stdout
end
pkg_cmd = "pkg_add -r"
if version && version >= 10
pkg_cmd = "pkg install -y"
end
machine.communicate.sudo("#{pkg_cmd} rsync")
machine.communicate.sudo("pkg install -y rsync")
end
def self.rsync_installed(machine)
@ -25,9 +17,8 @@ module VagrantPlugins
end
def self.rsync_pre(machine, opts)
machine.communicate.tap do |comm|
comm.sudo("mkdir -p '#{opts[:guestpath]}'")
end
guest_path = Shellwords.escape(opts[:guestpath])
machine.communicate.sudo("mkdir -p #{guest_path}")
end
def self.rsync_post(machine, opts)
@ -35,8 +26,10 @@ module VagrantPlugins
return
end
guest_path = Shellwords.escape(opts[:guestpath])
machine.communicate.sudo(
"find '#{opts[:guestpath]}' '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " +
"find #{guest_path} '(' ! -user #{opts[:owner]} -or ! -group #{opts[:group]} ')' -print0 | " +
"xargs -0 -r chown #{opts[:owner]}:#{opts[:group]}")
end
end

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