Merge branch 'master' into environments_wip
Conflicts: Vagrantfile vagrant.gemspec
This commit is contained in:
commit
cf847e0410
|
@ -16,6 +16,7 @@ pkg/*
|
|||
tags
|
||||
/Gemfile.lock
|
||||
test/tmp/
|
||||
vendor/
|
||||
|
||||
# Documentation
|
||||
_site/*
|
||||
|
@ -31,6 +32,7 @@ doc/
|
|||
# IDE junk
|
||||
.idea/*
|
||||
*.iml
|
||||
.project
|
||||
|
||||
# Ruby Managers
|
||||
.rbenv
|
||||
|
@ -47,3 +49,4 @@ website/docs/Rakefile
|
|||
website/www/.sass-cache
|
||||
website/www/build
|
||||
website/www/Rakefile
|
||||
exec/
|
||||
|
|
|
@ -2,6 +2,9 @@ language: ruby
|
|||
before_install:
|
||||
- sudo apt-get update -qq
|
||||
- sudo apt-get install -qq -y bsdtar
|
||||
- rvm @global do gem uninstall bundler --all --executables
|
||||
- gem uninstall bundler --all --executables
|
||||
- gem install bundler --version '< 1.7.0'
|
||||
rvm:
|
||||
- 2.0.0
|
||||
env:
|
||||
|
|
229
CHANGELOG.md
229
CHANGELOG.md
|
@ -1,3 +1,230 @@
|
|||
## 1.7.3 (unreleased)
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
- guests/darwin: Support inserting generated key. [GH-5204]
|
||||
- guests/fedora: Support Fedora 21. [GH-5277]
|
||||
- guests/redhat: Support Scientific Linux 7 [GH-5303]
|
||||
- guests/solaris,solaris11: Support inserting generated key. [GH-5182]
|
||||
[GH-5290]
|
||||
- providers/virtualbox: regexp supported for bridge configuration. [GH-5320]
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
- core: push configurations are validated with global configs [GH-5130]
|
||||
- core: remove executable permissions on internal file [GH-5220]
|
||||
- core: check name and version in `has_plugin?` [GH-5218]
|
||||
- core/cli: fix box checksum validation [GH-4665, GH-5221]
|
||||
- communicators/winrm: improve error handling significantly and improve
|
||||
the error messages shown to be more human-friendly. [GH-4943]
|
||||
- hosts/nfs: allow colons (`:`) in NFS IDs [GH-5222]
|
||||
- guests/debian: Halt works properly on Debian 8. [GH-5369]
|
||||
- guests/funtoo: fix incorrect path in configure networks [GH-4812]
|
||||
- guests/windows: Create rsync folder prior to rsync-ing. [GH-5282]
|
||||
- guests/windows: Changing hostname requires reboot again since
|
||||
the non-reboot code path was crashing Windows server. [GH-5261]
|
||||
- hosts/windows: More accurately get host IP address in VPNs. [GH-5349]
|
||||
- plugins/login: allow users to login with a token [GH-5145]
|
||||
- providers/docker: Build image from `/var/lib/docker` for more disk
|
||||
space on some systems. [GH-5302]
|
||||
- providers/hyperv: allow users to configure memory, cpu count, and vmname [GH-5183]
|
||||
- providers/hyperv: import respects secure boot. [GH-5209]
|
||||
- providers/hyperv: only set EFI secure boot for gen 2 machines [GH-5538]
|
||||
- providers/virtualbox: read netmask from dhcpservers [GH-5233]
|
||||
- providers/virtualbox: Fix exception when VirtualBox version is empty. [GH-5308]
|
||||
- provisioners/ansible: fix SSH settings to support more than 5 ssh keys [GH-5017]
|
||||
- provisioners/ansible: increase ansible connection timeout to 30 seconds [GH-5018]
|
||||
- provisioners/docker: Only add docker user to group if exists. [GH-5315]
|
||||
- provisioners/chef: Use `command -v` to check for binary instead of
|
||||
`which` since that doesn't exist on some systems. [GH-5170]
|
||||
- provisioners/chef-zero: support more chef-zero/local mode attributes [GH-5339]
|
||||
- provisioners/docker: use docker.com instead of docker.io [GH-5216]
|
||||
- pushes/atlas: send additional box metadata [GH-5283]
|
||||
- pushes/ftp: improve check for remote directory existence [GH-5549]
|
||||
- synced\_folders/rsync: add `IdentitiesOnly=yes` to the rsync command. [GH-5175]
|
||||
- virtualbox/config: fix misleading error message for private_network [GH-5536, GH-5418]
|
||||
|
||||
## 1.7.2 (January 6, 2015)
|
||||
|
||||
BREAKING CHANGES:
|
||||
|
||||
- If you depended on the paths that Chef/Puppet provisioners use to
|
||||
store cookbooks (ex. "/tmp/vagrant-chef-1"), these will no longer be
|
||||
correct. Without this change, Chef/Puppet didn't work at all with
|
||||
`vagrant provision`. We expect this to affect only a minor number of
|
||||
people, since it's not something that was ever documented or recommended
|
||||
by Vagrant, or even meant to be supported.
|
||||
|
||||
FEATURES:
|
||||
|
||||
- provisioners/salt: add support for grains [GH-4895]
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
- commands/reload,up: `--provision-with` implies `--provision` [GH-5085]
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
- core: private boxes still referencing vagrantcloud.com will have
|
||||
their vagrant login access token properly appended
|
||||
- core: push plugin configuration is properly validated
|
||||
- core: restore box packaging functionality
|
||||
- commands/package: fix crash
|
||||
- commands/push: push lookups are by user-defined name, not push
|
||||
strategy name [GH-4975]
|
||||
- commands/push: validate the configuration
|
||||
- communicators/winrm: detect parse errors in PowerShell and error
|
||||
- guests/arch: fix network configuration due to poor line breaks. [GH-4964]
|
||||
- guests/solaris: Merge configurations properly so configs can be set
|
||||
in default Vagrantfiles. [GH-5092]
|
||||
- installer: SSL cert bundle contains 1024-bit keys, fixing SSL verification
|
||||
for a lot of sites.
|
||||
- installer: vagrant executable properly `cygpaths` the SSL bundle path
|
||||
for Cygwin
|
||||
- installer: Nokogiri (XML lib used by Vagrant and dependencies) linker
|
||||
dependencies fixed, fixing load issues on some platforms
|
||||
- providers/docker: Symlinks in shared folders work. [GH-5093]
|
||||
- providers/hyperv: VM start errors turn into proper Vagrant errors. [GH-5101]
|
||||
- provisioners/chef: fix missing shared folder error [GH-4988]
|
||||
- provisioners/chef: remove Chef version check from solo.rb generation and
|
||||
make `roles_path` populate correctly
|
||||
- provisioners/chef: fix bad invocation of `with_clean_env` [GH-5021]
|
||||
- pushes/atlas: support more verbose logging
|
||||
- pushes/ftp: expand file paths relative to the Vagrantfile
|
||||
- pushes/ftp: improved debugging output
|
||||
- pushes/ftp: create parent directories if they do not exist on the remote
|
||||
server
|
||||
|
||||
## 1.7.1 (December 11, 2014)
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
- provisioners/ansible: Use Docker proxy if needed. [GH-4906]
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
- providers/docker: Add support of SSH agent forwarding. [GH-4905]
|
||||
|
||||
## 1.7.0 (December 9, 2014)
|
||||
|
||||
BREAKING CHANGES:
|
||||
|
||||
- provisioners/ansible: `raw_arguments` has now highest priority
|
||||
- provisioners/ansible: only the `ssh` connection transport is supported
|
||||
(`paramiko` can be enabled with `raw_arguments` at your own risks)
|
||||
|
||||
FEATURES:
|
||||
|
||||
- **Vagrant Push**: Vagrant can now deploy! `vagrant push` is a single
|
||||
command to deploy your application. Deploy to Heroku, FTP, or
|
||||
HashiCorp's commercial product Atlas. New push strategies can be
|
||||
added with plugins.
|
||||
- **Named provisioners**: Provisioners can now be named. This name is used
|
||||
for output as well as `--provision-with` for better control.
|
||||
- Default provider logic improved: Providers in `config.vm.provider` blocks
|
||||
in your Vagrantfile now have higher priority than plugins. Earlier
|
||||
providers are chosen before later ones. [GH-3812]
|
||||
- If the default insecure keypair is used, Vagrant will automatically replace
|
||||
it with a randomly generated keypair on first `vagrant up`. [GH-2608]
|
||||
- Vagrant Login is now part of Vagrant core
|
||||
- Chef Zero provisioner: Use Chef 11's "local" mode to run recipes against an
|
||||
in-memory Chef Server
|
||||
- Chef Apply provisioner: Specify inline Chef recipes and recipe snippets
|
||||
using the Chef Apply provisioner
|
||||
|
||||
IMPROVEMENTS:
|
||||
|
||||
- core: `has_plugin?` function now takes a second argument which is a
|
||||
version constraint requirement. [GH-4650]
|
||||
- core: ".vagrantplugins" file in the same file as your Vagrantfile
|
||||
will be loaded for defining inline plugins. [GH-3775]
|
||||
- commands/plugin: Plugin list machine-readable output contains the plugin
|
||||
name as the target for versions and other info. [GH-4506]
|
||||
- env/with_cleanenv: New helper for plugin developers to use when shelling out
|
||||
to another Ruby environment
|
||||
- guests/arch: Support predictable network interface naming. [GH-4468]
|
||||
- guests/suse: Support NFS client install, rsync setup. [GH-4492]
|
||||
- guests/tinycore: Support changing host names. [GH-4469]
|
||||
- guests/tinycore: Support DHCP-based networks. [GH-4710]
|
||||
- guests/windows: Hostname can be set without reboot. [GH-4687]
|
||||
- providers/docker: Build output is now shown. [GH-3739]
|
||||
- providers/docker: Can now start containers from private repositories
|
||||
more easily. Vagrant will login for you if you specify auth. [GH-4042]
|
||||
- providers/docker: `stop_timeout` can be used to modify the `docker stop`
|
||||
timeout. [GH-4504]
|
||||
- provisioners/chef: Automatically install Chef when using a Chef provisioner.
|
||||
- provisioners/ansible: Always show Ansible command executed when Vagrant log
|
||||
level is debug (even if ansible.verbose is false)
|
||||
- synced\_folders/nfs: Won't use `sudo` to write to /etc/exports if there
|
||||
are write privileges. [GH-2643]
|
||||
- synced\_folders/smb: Credentials from one SMB will be copied to the rest. [GH-4675]
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
- core: Fix cases where sometimes SSH connection would hang.
|
||||
- core: On a graceful halt, force halt if capability "insert public key"
|
||||
is missing. [GH-4684]
|
||||
- core: Don't share `/vagrant` if any "." folder is shared. [GH-4675]
|
||||
- core: Fix SSH private key permissions more aggressively. [GH-4670]
|
||||
- core: Custom Vagrant Cloud server URL now respected in more cases.
|
||||
- core: On downloads, don't continue downloads if the remote server
|
||||
doesn't support byte ranges. [GH-4479]
|
||||
- core: Box downloads recognize more complex content types that include
|
||||
"application/json" [GH-4525]
|
||||
- core: If all sub-machines are `autostart: false`, don't start any. [GH-4552]
|
||||
- core: Update global-status state in more cases. [GH-4513]
|
||||
- core: Only delete machine state if the machine is not created in initialize
|
||||
- commands/box: `--cert` flag works properly. [GH-4691]
|
||||
- command/docker-logs: Won't crash if container is removed. [GH-3990]
|
||||
- command/docker-run: Synced folders will be attached properly. [GH-3873]
|
||||
- command/rsync: Sync to Docker containers properly. [GH-4066]
|
||||
- guests/darwin: Hostname sets bonjour name and local host name. [GH-4535]
|
||||
- guests/freebsd: NFS mounting can specify the version. [GH-4518]
|
||||
- guests/linux: More descriptive error message if SMB mount fails. [GH-4641]
|
||||
- guests/rhel: Hostname setting on 7.x series works properly. [GH-4527]
|
||||
- guests/rhel: Installing NFS client works properly on 7.x [GH-4499]
|
||||
- guests/solaris11: Static IP address preserved after restart. [GH-4621]
|
||||
- guests/ubuntu: Detect with `lsb_release` instead of `/etc/issue`. [GH-4565]
|
||||
- hosts/windows: RDP client shouldn't map all drives by default. [GH-4534]
|
||||
- providers/docker: Create args works. [GH-4526]
|
||||
- providers/docker: Nicer error if package is called. [GH-4595]
|
||||
- providers/docker: Host IP restriction is forwarded through. [GH-4505]
|
||||
- providers/docker: Protocol is now honored in direct `ports settings.
|
||||
- providers/docker: Images built using `build_dir` will more robustly
|
||||
capture the final image. [GH-4598]
|
||||
- providers/docker: NFS synced folders now work. [GH-4344]
|
||||
- providers/docker: Read the created container ID more robustly.
|
||||
- providers/docker: `vagrant share` uses correct IP of proxy VM if it
|
||||
exists. [GH-4342]
|
||||
- providers/docker: `vagrant_vagrantfile` expands home directory. [GH-4000]
|
||||
- providers/docker: Fix issue where multiple identical proxy VMs would
|
||||
be created. [GH-3963]
|
||||
- providers/docker: Multiple links with the same name work. [GH-4571]
|
||||
- providers/virtualbox: Show a human-friendly error if VirtualBox didn't
|
||||
clean up an existing VM. [GH-4681]
|
||||
- providers/virtualbox: Detect case when VirtualBox reports 0.0.0.0 as
|
||||
IP address and don't allow it. [GH-4671]
|
||||
- providers/virtualbox: Show more descriptive error if VirtualBox is
|
||||
reporting an empty version. [GH-4657]
|
||||
- provisioners/ansible: Force `ssh` (OpenSSH) connection by default [GH-3396]
|
||||
- provisioners/ansible: Don't use or modify `~/.ssh/known_hosts` file by default,
|
||||
similarly to native vagrant commands [GH-3900]
|
||||
- provisioners/ansible: Use intermediate Docker host when needed. [GH-4071]
|
||||
- provisioners/docker: Get GPG key over SSL. [GH-4597]
|
||||
- provisioners/docker: Search for docker binary in multiple places. [GH-4580]
|
||||
- provisioners/salt: Highstate works properly with a master. [GH-4471]
|
||||
- provisioners/shell: Retry getting SSH info a few times. [GH-3924]
|
||||
- provisioners/shell: PowerShell scripts can have args. [GH-4548]
|
||||
- synced\_folders/nfs: Don't modify NFS exports file if no exports. [GH-4619]
|
||||
- synced\_folders/nfs: Prune exports for file path IDs. [GH-3815]
|
||||
|
||||
PLUGIN AUTHOR CHANGES:
|
||||
|
||||
- `Machine#action` can be called with the option `lock: false` to not
|
||||
acquire a machine lock.
|
||||
- `Machine#reload` will now properly trigger the `machine_id_changed`
|
||||
callback on providers.
|
||||
|
||||
## 1.6.5 (September 4, 2014)
|
||||
|
||||
BUG FIXES:
|
||||
|
@ -8,6 +235,8 @@ BUG FIXES:
|
|||
- guests/redhat: Fix typo causing crash in configuring networks. [GH-4438]
|
||||
- guests/redhat: Fix typo causing hostnames to not set. [GH-4443]
|
||||
- providers/virtualbox: NFS works when using DHCP private network. [GH-4433]
|
||||
- provisioners/salt: Fix error when removing non-existent bootstrap script
|
||||
on Windows. [GH-4614]
|
||||
|
||||
## 1.6.4 (September 2, 2014)
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ The following guidelines for contribution should be followed if you want to subm
|
|||
## How to prepare
|
||||
|
||||
* You need a [GitHub account](https://github.com/signup/free)
|
||||
* Submit an [issue ticket](https://github.com/mitchellh/vagrant/issues) for your issue if the is no one yet.
|
||||
* Submit an [issue ticket](https://github.com/mitchellh/vagrant/issues) for your issue if there is not one yet.
|
||||
* Describe the issue and include steps to reproduce when it's a bug.
|
||||
* Ensure to mention the earliest version that you know is affected.
|
||||
* If you plan on submitting a bug report, please submit debug-level logs along
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -1,4 +1,4 @@
|
|||
source "http://rubygems.org"
|
||||
source "https://rubygems.org"
|
||||
|
||||
gemspec
|
||||
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2014 Mitchell Hashimoto
|
||||
Copyright (c) 2010-2015 Mitchell Hashimoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -55,7 +55,7 @@ Ruby 2.0 is needed.
|
|||
|
||||
### Dependencies and Unit Tests
|
||||
|
||||
To hack on vagrant, you'll need [bundler](http://github.com/carlhuda/bundler) which can
|
||||
To hack on Vagrant, you'll need [bundler](http://github.com/carlhuda/bundler) which can
|
||||
be installed with a simple `gem install bundler`. Afterwards, do the following:
|
||||
|
||||
bundle install
|
||||
|
@ -68,7 +68,7 @@ like so:
|
|||
|
||||
bundle exec vagrant help
|
||||
|
||||
**NOTE:** By default running Vagrant in via `bundle` will disable plugins.
|
||||
**NOTE:** By default running Vagrant via `bundle` will disable plugins.
|
||||
This is necessary because Vagrant creates its own private Bundler context
|
||||
(it does not respect your Gemfile), because it uses Bundler to manage plugin
|
||||
dependencies.
|
||||
|
|
|
@ -1,35 +1,62 @@
|
|||
# This Vagrantfile can be used to develop Vagrant. Note that VirtualBox
|
||||
# doesn't run in VirtualBox so you can't actually _run_ Vagrant within
|
||||
# the VM created by this Vagrantfile, but you can use it to develop the
|
||||
# Ruby, run unit tests, etc.
|
||||
|
||||
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
|
||||
VAGRANTFILE_API_VERSION = "2"
|
||||
|
||||
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
config.vm.box = "centos7"
|
||||
|
||||
config.vm.define :centos7 do |centos7|
|
||||
config.vm.network "private_network", ip: "192.168.11.3"
|
||||
config.vm.hostname = "centos7"
|
||||
# config.vm.network "public_network"
|
||||
# config.ssh.forward_agent = true
|
||||
config.vm.synced_folder "../puppet", "/puppet"
|
||||
|
||||
config.vm.provider "virtualbox" do |vb|
|
||||
vb.gui = false
|
||||
# Use VBoxManage to customize the VM. For example to change memory:
|
||||
vb.customize ["modifyvm", :id, "--memory", "2048"]
|
||||
end
|
||||
|
||||
config.vm.provision "puppet" do |puppet|
|
||||
puppet.environment_path = "../puppet/environments"
|
||||
puppet.environment = "testenv"
|
||||
# puppet.manifests_path = "../puppet/manifests"
|
||||
# puppet.manifest_file = "site.pp"
|
||||
puppet.module_path = [ "../puppet/modules/public", "../puppet/modules/private" ]
|
||||
# puppet.options = "--debug --verbose"
|
||||
end
|
||||
|
||||
# Deprecated method:
|
||||
#puppet apply --debug --verbose --modulepath '/puppet/modules/private:/puppet/modules/public:/etc/puppet/modules'
|
||||
#--manifestdir /tmp/vagrant-puppet-1/manifests --detailed-exitcodes /tmp/vagrant-puppet-1/manifests/site.pp
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "hashicorp/precise64"
|
||||
|
||||
["vmware_fusion", "vmware_workstation", "virtualbox"].each do |provider|
|
||||
config.vm.provider provider do |v, override|
|
||||
v.memory = "1024"
|
||||
end
|
||||
end
|
||||
|
||||
config.vm.provision "shell", inline: $shell
|
||||
|
||||
config.push.define "www", strategy: "local-exec" do |push|
|
||||
push.script = "scripts/website_push_www.sh"
|
||||
end
|
||||
|
||||
config.push.define "docs", strategy: "local-exec" do |push|
|
||||
push.script = "scripts/website_push_docs.sh"
|
||||
end
|
||||
end
|
||||
|
||||
$shell = <<-CONTENTS
|
||||
MARKER_FILE="/usr/local/etc/vagrant_provision_marker"
|
||||
|
||||
# Only provision once
|
||||
if [ -f "${MARKER_FILE}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Update apt
|
||||
apt-get update
|
||||
|
||||
# Install basic dependencies
|
||||
apt-get install -y build-essential bsdtar curl
|
||||
|
||||
# Install RVM
|
||||
su -l -c 'curl -L https://get.rvm.io | bash -s stable' vagrant
|
||||
|
||||
# Add the vagrant user to the RVM group
|
||||
#usermod -a -G rvm vagrant
|
||||
|
||||
# Install some Rubies
|
||||
su -l -c 'rvm install 2.1.1' vagrant
|
||||
su -l -c 'rvm --default use 2.1.1' vagrant
|
||||
|
||||
# Output the Ruby version (for sanity)
|
||||
su -l -c 'ruby --version' vagrant
|
||||
|
||||
# Install Git
|
||||
apt-get install -y git
|
||||
|
||||
# Automatically move into the shared folder, but only add the command
|
||||
# if it's not already there.
|
||||
grep -q 'cd /vagrant' /home/vagrant/.bash_profile || echo 'cd /vagrant' >> /home/vagrant/.bash_profile
|
||||
|
||||
# Touch the marker file so we don't do this again
|
||||
touch ${MARKER_FILE}
|
||||
CONTENTS
|
||||
|
|
|
@ -15,7 +15,7 @@ end
|
|||
|
||||
# Fast path the version of Vagrant
|
||||
if argv.include?("-v") || argv.include?("--version")
|
||||
require "vagrant/version"
|
||||
require_relative "../lib/vagrant/version"
|
||||
puts "Vagrant #{Vagrant::VERSION}"
|
||||
exit 0
|
||||
end
|
||||
|
|
|
@ -65,7 +65,7 @@ _vagrant() {
|
|||
then
|
||||
case "$prev" in
|
||||
"init")
|
||||
local box_list=$(find $HOME/.vagrant.d/boxes -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
||||
local box_list=$(find "${VAGRANT_HOME:-${HOME}/.vagrant.d}/boxes" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
||||
COMPREPLY=($(compgen -W "${box_list}" -- ${cur}))
|
||||
return 0
|
||||
;;
|
||||
|
@ -111,7 +111,7 @@ _vagrant() {
|
|||
then
|
||||
case "$prev" in
|
||||
"remove"|"repackage")
|
||||
local box_list=$(find $HOME/.vagrant.d/boxes -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
||||
local box_list=$(find "${VAGRANT_HOME:-${HOME}/.vagrant.d}/boxes" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
||||
COMPREPLY=($(compgen -W "${box_list}" -- ${cur}))
|
||||
return 0
|
||||
;;
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
Cmnd_Alias VAGRANT_EXPORTS_ADD = /usr/bin/tee -a /etc/exports
|
||||
Cmnd_Alias VAGRANT_NFSD_CHECK = /usr/bin/systemctl status nfs-server.service
|
||||
Cmnd_Alias VAGRANT_NFSD_START = /usr/bin/systemctl start nfs-server.service
|
||||
Cmnd_Alias VAGRANT_NFSD_APPLY = /usr/sbin/exportfs -ar
|
||||
Cmnd_Alias VAGRANT_EXPORTS_REMOVE = /bin/sed -r -e * d -ibak /etc/exports
|
||||
%vagrant ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD_CHECK, VAGRANT_NFSD_START, VAGRANT_NFSD_APPLY, VAGRANT_EXPORTS_REMOVE
|
|
@ -123,6 +123,7 @@ module Vagrant
|
|||
c.register([:"2", :host]) { Plugin::V2::Host }
|
||||
c.register([:"2", :provider]) { Plugin::V2::Provider }
|
||||
c.register([:"2", :provisioner]) { Plugin::V2::Provisioner }
|
||||
c.register([:"2", :push]) { Plugin::V2::Push }
|
||||
c.register([:"2", :synced_folder]) { Plugin::V2::SyncedFolder }
|
||||
end
|
||||
|
||||
|
@ -141,13 +142,22 @@ module Vagrant
|
|||
# This checks if a plugin with the given name is installed. This can
|
||||
# be used from the Vagrantfile to easily branch based on plugin
|
||||
# availability.
|
||||
def self.has_plugin?(name)
|
||||
def self.has_plugin?(name, version=nil)
|
||||
if !version
|
||||
# We check the plugin names first because those are cheaper to check
|
||||
return true if plugin("2").manager.registered.any? { |p| p.name == name }
|
||||
end
|
||||
|
||||
# Make the requirement object
|
||||
version = Gem::Requirement.new([version]) if version
|
||||
|
||||
# Now check the plugin gem names
|
||||
require "vagrant/plugin/manager"
|
||||
Plugin::Manager.instance.installed_specs.any? { |s| s.name == name }
|
||||
Plugin::Manager.instance.installed_specs.any? do |s|
|
||||
match = s.name == name
|
||||
next match if !version
|
||||
next match && version.satisfied_by?(s.version)
|
||||
end
|
||||
end
|
||||
|
||||
# Returns a superclass to use when creating a plugin for Vagrant.
|
||||
|
|
|
@ -147,7 +147,7 @@ module Vagrant
|
|||
# element is an authenticated URL.
|
||||
# @param [Hash] env
|
||||
# @param [Bool] expanded True if the metadata URL was expanded with
|
||||
# a Vagrant Cloud server URL.
|
||||
# a Atlas server URL.
|
||||
def add_from_metadata(url, env, expanded)
|
||||
original_url = env[:box_url]
|
||||
provider = env[:box_provider]
|
||||
|
@ -369,7 +369,7 @@ module Vagrant
|
|||
#
|
||||
# @return [Hash]
|
||||
def downloader(url, env, **opts)
|
||||
opts[:ui] = true if !opts.has_key?(:ui)
|
||||
opts[:ui] = true if !opts.key?(:ui)
|
||||
|
||||
temp_path = env[:tmp_path].join("box" + Digest::SHA1.hexdigest(url))
|
||||
@logger.info("Downloading box: #{url} => #{temp_path}")
|
||||
|
@ -401,7 +401,7 @@ module Vagrant
|
|||
downloader_options[:ca_path] = env[:box_download_ca_path]
|
||||
downloader_options[:continue] = true
|
||||
downloader_options[:insecure] = env[:box_download_insecure]
|
||||
downloader_options[:client_cert] = env[:box_client_cert]
|
||||
downloader_options[:client_cert] = env[:box_download_client_cert]
|
||||
downloader_options[:headers] = ["Accept: application/json"] if opts[:json]
|
||||
downloader_options[:ui] = env[:ui] if opts[:ui]
|
||||
|
||||
|
@ -409,7 +409,7 @@ module Vagrant
|
|||
end
|
||||
|
||||
def download(url, env, **opts)
|
||||
opts[:ui] = true if !opts.has_key?(:ui)
|
||||
opts[:ui] = true if !opts.key?(:ui)
|
||||
|
||||
d = downloader(url, env, **opts)
|
||||
|
||||
|
@ -483,7 +483,7 @@ module Vagrant
|
|||
output = d.head
|
||||
match = output.scan(/^Content-Type: (.+?)$/i).last
|
||||
return false if !match
|
||||
match.last.chomp == "application/json"
|
||||
!!(match.last.chomp =~ /application\/json/)
|
||||
end
|
||||
|
||||
def validate_checksum(checksum_type, checksum, path)
|
||||
|
|
|
@ -11,7 +11,7 @@ module Vagrant
|
|||
end
|
||||
|
||||
def call(env)
|
||||
if !env.has_key?(:config_validate) || env[:config_validate]
|
||||
if !env.key?(:config_validate) || env[:config_validate]
|
||||
errors = env[:machine].config.validate(env[:machine])
|
||||
|
||||
if errors && !errors.empty?
|
||||
|
|
|
@ -24,7 +24,7 @@ module Vagrant
|
|||
|
||||
def call(env)
|
||||
graceful = true
|
||||
graceful = !env[:force_halt] if env.has_key?(:force_halt)
|
||||
graceful = !env[:force_halt] if env.key?(:force_halt)
|
||||
|
||||
# By default, we didn't succeed.
|
||||
env[:result] = false
|
||||
|
@ -59,6 +59,9 @@ module Vagrant
|
|||
rescue Timeout::Error
|
||||
# Don't worry about it, we catch the case later.
|
||||
end
|
||||
rescue Errors::GuestCapabilityNotFound
|
||||
# This happens if insert_public_key is called on a guest that
|
||||
# doesn't support it. This will block a destroy so we let it go.
|
||||
rescue Errors::MachineGuestNotReady
|
||||
env[:ui].detail(I18n.t("vagrant.actions.vm.halt.guest_not_ready"))
|
||||
end
|
||||
|
|
|
@ -64,6 +64,8 @@ module Vagrant
|
|||
box_download_ca_path = machine.config.vm.box_download_ca_path
|
||||
box_download_client_cert = machine.config.vm.box_download_client_cert
|
||||
box_download_insecure = machine.config.vm.box_download_insecure
|
||||
box_download_checksum_type = machine.config.vm.box_download_checksum_type
|
||||
box_download_checksum = machine.config.vm.box_download_checksum
|
||||
box_formats = machine.provider_options[:box_format] ||
|
||||
machine.provider_name
|
||||
|
||||
|
@ -79,12 +81,15 @@ module Vagrant
|
|||
env[:action_runner].run(Vagrant::Action.action_box_add, env.merge({
|
||||
box_name: machine.config.vm.box,
|
||||
box_url: machine.config.vm.box_url || machine.config.vm.box,
|
||||
box_server_url: machine.config.vm.box_server_url,
|
||||
box_provider: box_formats,
|
||||
box_version: machine.config.vm.box_version,
|
||||
box_client_cert: box_download_client_cert,
|
||||
box_download_client_cert: box_download_client_cert,
|
||||
box_download_ca_cert: box_download_ca_cert,
|
||||
box_download_ca_path: box_download_ca_path,
|
||||
box_download_insecure: box_download_insecure,
|
||||
box_checksum_type: box_download_checksum_type,
|
||||
box_checksum: box_download_checksum,
|
||||
}))
|
||||
rescue Errors::BoxAlreadyExists
|
||||
# Just ignore this, since it means the next part will succeed!
|
||||
|
|
|
@ -15,7 +15,7 @@ module Vagrant
|
|||
# Get all the configured provisioners
|
||||
@_provisioner_instances = env[:machine].config.vm.provisioners.map do |provisioner|
|
||||
# Instantiate the provisioner
|
||||
klass = Vagrant.plugin("2").manager.provisioners[provisioner.name]
|
||||
klass = Vagrant.plugin("2").manager.provisioners[provisioner.type]
|
||||
|
||||
# This can happen in the case the configuration isn't validated.
|
||||
next nil if !klass
|
||||
|
@ -23,10 +23,11 @@ module Vagrant
|
|||
result = klass.new(env[:machine], provisioner.config)
|
||||
|
||||
# Store in the type map so that --provision-with works properly
|
||||
@_provisioner_types[result] = provisioner.name
|
||||
@_provisioner_types[result] = provisioner.type
|
||||
|
||||
# Build up the options
|
||||
options = {
|
||||
name: provisioner.name,
|
||||
run: provisioner.run,
|
||||
}
|
||||
|
||||
|
|
|
@ -93,11 +93,12 @@ module Vagrant
|
|||
|
||||
config = opts[:config]
|
||||
config ||= machine.config.vm
|
||||
config_folders = config.synced_folders
|
||||
folders = {}
|
||||
|
||||
# Determine all the synced folders as well as the implementation
|
||||
# they're going to use.
|
||||
config.synced_folders.each do |id, data|
|
||||
config_folders.each do |id, data|
|
||||
# Ignore disabled synced folders
|
||||
next if data[:disabled]
|
||||
|
||||
|
@ -112,12 +113,14 @@ module Vagrant
|
|||
raise "Internal error. Report this as a bug. Invalid: #{data[:type]}"
|
||||
end
|
||||
|
||||
if !opts[:disable_usable_check]
|
||||
if !impl_class[0].new.usable?(machine, true)
|
||||
# Verify that explicitly defined shared folder types are
|
||||
# actually usable.
|
||||
raise Errors::SyncedFolderUnusable, type: data[:type].to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Keep track of this shared folder by the implementation.
|
||||
folders[impl] ||= {}
|
||||
|
@ -126,7 +129,7 @@ module Vagrant
|
|||
|
||||
# If we have folders with the "default" key, then determine the
|
||||
# most appropriate implementation for this.
|
||||
if folders.has_key?("") && !folders[""].empty?
|
||||
if folders.key?("") && !folders[""].empty?
|
||||
default_impl = default_synced_folder_type(machine, plugins)
|
||||
if !default_impl
|
||||
types = plugins.to_hash.keys.map { |t| t.to_s }.sort.join(", ")
|
||||
|
|
|
@ -24,13 +24,13 @@ module Vagrant
|
|||
|
||||
# Tracks whether we were configured to provision
|
||||
config_enabled = true
|
||||
config_enabled = env[:provision_enabled] if env.has_key?(:provision_enabled)
|
||||
config_enabled = env[:provision_enabled] if env.key?(:provision_enabled)
|
||||
|
||||
# Check if we already provisioned, and if so, disable the rest
|
||||
provision_enabled = true
|
||||
|
||||
ignore_sentinel = true
|
||||
if env.has_key?(:provision_ignore_sentinel)
|
||||
if env.key?(:provision_ignore_sentinel)
|
||||
ignore_sentinel = env[:provision_ignore_sentinel]
|
||||
end
|
||||
if ignore_sentinel
|
||||
|
@ -69,7 +69,7 @@ module Vagrant
|
|||
end
|
||||
|
||||
# Store the value so that other actions can use it
|
||||
env[:provision_enabled] = provision_enabled if !env.has_key?(:provision_enabled)
|
||||
env[:provision_enabled] = provision_enabled if !env.key?(:provision_enabled)
|
||||
|
||||
# Ask the provisioners to modify the configuration if needed
|
||||
provisioner_instances(env).each do |p, _|
|
||||
|
@ -103,14 +103,20 @@ module Vagrant
|
|||
provisioner_instances(env).each do |p, options|
|
||||
type_name = type_map[p]
|
||||
next if env[:provision_types] && \
|
||||
!env[:provision_types].include?(type_name)
|
||||
!env[:provision_types].include?(type_name) && \
|
||||
!env[:provision_types].include?(options[:name])
|
||||
|
||||
# Don't run if sentinel is around and we're not always running
|
||||
next if !provision_enabled && options[:run] != :always
|
||||
|
||||
name = type_name
|
||||
if options[:name]
|
||||
name = "#{options[:name]} (#{type_name})"
|
||||
end
|
||||
|
||||
env[:ui].info(I18n.t(
|
||||
"vagrant.actions.vm.provision.beginning",
|
||||
provisioner: type_name))
|
||||
provisioner: name))
|
||||
|
||||
env[:hook].call(:provisioner_run, env.merge(
|
||||
callable: method(:run_provisioner),
|
||||
|
|
|
@ -31,11 +31,6 @@ module Vagrant
|
|||
|
||||
info[:private_key_path] ||= []
|
||||
|
||||
# Check SSH key permissions
|
||||
info[:private_key_path].each do |path|
|
||||
SSH.check_key_permissions(Pathname.new(path))
|
||||
end
|
||||
|
||||
if info[:private_key_path].empty? && info[:password]
|
||||
env[:ui].warn(I18n.t("vagrant.ssh_exec_password"))
|
||||
end
|
||||
|
|
|
@ -29,11 +29,6 @@ module Vagrant
|
|||
|
||||
info[:private_key_path] ||= []
|
||||
|
||||
# Check SSH key permissions
|
||||
info[:private_key_path].each do |path|
|
||||
SSH.check_key_permissions(Pathname.new(path))
|
||||
end
|
||||
|
||||
if info[:private_key_path].empty?
|
||||
raise Errors::SSHRunRequiresKeys
|
||||
end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
require 'fileutils'
|
||||
require "pathname"
|
||||
|
||||
require 'vagrant/util/safe_chdir'
|
||||
require 'vagrant/util/subprocess'
|
||||
|
@ -36,6 +37,9 @@ module Vagrant
|
|||
|
||||
@app.call(env)
|
||||
|
||||
@env[:ui].info I18n.t("vagrant.actions.general.package.compressing", tar_path: tar_path)
|
||||
copy_include_files
|
||||
setup_private_key
|
||||
compress
|
||||
end
|
||||
|
||||
|
@ -81,11 +85,6 @@ module Vagrant
|
|||
|
||||
# Compress the exported file into a package
|
||||
def compress
|
||||
@env[:ui].info I18n.t("vagrant.actions.general.package.compressing", tar_path: tar_path)
|
||||
|
||||
# Copy over the included files
|
||||
copy_include_files
|
||||
|
||||
# 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
|
||||
|
@ -100,6 +99,39 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
# This will copy the generated private key into the box and use
|
||||
# it for SSH by default. We have to do this because we now generate
|
||||
# random keypairs on boot, so packaged boxes would stop working
|
||||
# without this.
|
||||
def setup_private_key
|
||||
# If we don't have machine, we do nothing (weird)
|
||||
return if !@env[:machine]
|
||||
|
||||
# If we don't have a data dir, we also do nothing (base package)
|
||||
return if !@env[:machine].data_dir
|
||||
|
||||
# If we don't have a generated private key, we do nothing
|
||||
path = @env[:machine].data_dir.join("private_key")
|
||||
return if !path.file?
|
||||
|
||||
# Copy it into our box directory
|
||||
dir = Pathname.new(@env["package.directory"])
|
||||
new_path = dir.join("vagrant_private_key")
|
||||
FileUtils.cp(path, new_path)
|
||||
|
||||
# Append it to the Vagrantfile (or create a Vagrantfile)
|
||||
vf_path = dir.join("Vagrantfile")
|
||||
mode = "w+"
|
||||
mode = "a" if vf_path.file?
|
||||
vf_path.open(mode) do |f|
|
||||
f.binmode
|
||||
f.puts
|
||||
f.puts %Q[Vagrant.configure("2") do |config|]
|
||||
f.puts %Q[ config.ssh.private_key_path = File.expand_path("../vagrant_private_key", __FILE__)]
|
||||
f.puts %Q[end]
|
||||
end
|
||||
end
|
||||
|
||||
# Path to the final box output file
|
||||
def tar_path
|
||||
File.expand_path(@env["package.output"], FileUtils.pwd)
|
||||
|
|
|
@ -49,7 +49,7 @@ module Vagrant
|
|||
# Run the action chain in a busy block, marking the environment as
|
||||
# interrupted if a SIGINT occurs, and exiting cleanly once the
|
||||
# chain has been run.
|
||||
ui = environment[:ui] if environment.has_key?(:ui)
|
||||
ui = environment[:ui] if environment.key?(:ui)
|
||||
int_callback = lambda do
|
||||
if environment[:interrupted]
|
||||
ui.error I18n.t("vagrant.actions.runner.exit_immediately") if ui
|
||||
|
@ -62,7 +62,7 @@ module Vagrant
|
|||
end
|
||||
|
||||
# We place a process lock around every action that is called
|
||||
@logger.info("Running action: #{callable_id}")
|
||||
@logger.info("Running action: #{environment[:action_name]} #{callable_id}")
|
||||
Util::Busy.busy(int_callback) { callable.call(environment) }
|
||||
|
||||
# Return the environment in case there are things in there that
|
||||
|
|
|
@ -29,7 +29,7 @@ module Vagrant
|
|||
# * BOX_NAME - The name of the box. This is a logical name given by
|
||||
# the user of Vagrant.
|
||||
# * PROVIDER - The provider that the box was built for (VirtualBox,
|
||||
# VMWare, etc.).
|
||||
# VMware, etc.).
|
||||
# * metadata.json - A simple JSON file that at the bare minimum
|
||||
# contains a "provider" key that matches the provider for the
|
||||
# box. This metadata JSON, however, can contain anything.
|
||||
|
|
|
@ -165,7 +165,7 @@ module Vagrant
|
|||
@cap_logger.debug("Checking in: #{host_name}")
|
||||
caps = @cap_caps[host_name]
|
||||
|
||||
if caps && caps.has_key?(cap_name)
|
||||
if caps && caps.key?(cap_name)
|
||||
@cap_logger.debug("Found cap: #{cap_name} in #{host_name}")
|
||||
return caps[cap_name]
|
||||
end
|
||||
|
|
|
@ -49,7 +49,7 @@ module Vagrant
|
|||
# Gather the procs for every source, since that is what we care about.
|
||||
procs = []
|
||||
sources.each do |source|
|
||||
if !@proc_cache.has_key?(source)
|
||||
if !@proc_cache.key?(source)
|
||||
# Load the procs for this source and cache them. This caching
|
||||
# avoids the issue where a file may have side effects when loading
|
||||
# and loading it multiple times causes unexpected behavior.
|
||||
|
@ -92,10 +92,10 @@ module Vagrant
|
|||
errors = []
|
||||
|
||||
order.each do |key|
|
||||
next if !@sources.has_key?(key)
|
||||
next if !@sources.key?(key)
|
||||
|
||||
@sources[key].each do |version, proc|
|
||||
if !@config_cache.has_key?(proc)
|
||||
if !@config_cache.key?(proc)
|
||||
@logger.debug("Loading from: #{key} (evaluating)")
|
||||
|
||||
# Get the proper version loader for this version and load
|
||||
|
|
|
@ -60,7 +60,7 @@ module Vagrant
|
|||
new_keys = new_state["keys"]
|
||||
keys = {}
|
||||
old_keys.each do |key, old_value|
|
||||
if new_keys.has_key?(key)
|
||||
if new_keys.key?(key)
|
||||
# We need to do a merge, which we expect to be available
|
||||
# on the config class itself.
|
||||
keys[key] = old_value.merge(new_keys[key])
|
||||
|
@ -72,7 +72,7 @@ module Vagrant
|
|||
|
||||
new_keys.each do |key, new_value|
|
||||
# Add in the keys that the new class has that we haven't merged.
|
||||
if !keys.has_key?(key)
|
||||
if !keys.key?(key)
|
||||
keys[key] = new_value.dup
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,7 +20,7 @@ module Vagrant
|
|||
# used for Vagrant and load the proper configuration classes for
|
||||
# each.
|
||||
def method_missing(name, *args)
|
||||
return @keys[name] if @keys.has_key?(name)
|
||||
return @keys[name] if @keys.key?(name)
|
||||
|
||||
config_klass = @config_map[name.to_sym]
|
||||
if config_klass
|
||||
|
|
|
@ -70,7 +70,7 @@ module Vagrant
|
|||
new_keys = new_state["keys"]
|
||||
keys = {}
|
||||
old_keys.each do |key, old_value|
|
||||
if new_keys.has_key?(key)
|
||||
if new_keys.key?(key)
|
||||
# We need to do a merge, which we expect to be available
|
||||
# on the config class itself.
|
||||
keys[key] = old_value.merge(new_keys[key])
|
||||
|
@ -82,7 +82,7 @@ module Vagrant
|
|||
|
||||
new_keys.each do |key, new_value|
|
||||
# Add in the keys that the new class has that we haven't merged.
|
||||
if !keys.has_key?(key)
|
||||
if !keys.key?(key)
|
||||
keys[key] = new_value.dup
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,7 +22,7 @@ module Vagrant
|
|||
# used for Vagrant and load the proper configuration classes for
|
||||
# each.
|
||||
def method_missing(name, *args)
|
||||
return @keys[name] if @keys.has_key?(name)
|
||||
return @keys[name] if @keys.key?(name)
|
||||
|
||||
config_klass = @config_map[name.to_sym]
|
||||
if config_klass
|
||||
|
@ -41,7 +41,7 @@ module Vagrant
|
|||
# mutate itself.
|
||||
def finalize!
|
||||
@config_map.each do |key, klass|
|
||||
if !@keys.has_key?(key)
|
||||
if !@keys.key?(key)
|
||||
@keys[key] = klass.new
|
||||
end
|
||||
end
|
||||
|
@ -102,9 +102,9 @@ module Vagrant
|
|||
# This sets the internal state. This is used by the core to do some
|
||||
# merging logic and shouldn't be used by the general public.
|
||||
def __set_internal_state(state)
|
||||
@config_map = state["config_map"] if state.has_key?("config_map")
|
||||
@keys = state["keys"] if state.has_key?("keys")
|
||||
@missing_key_calls = state["missing_key_calls"] if state.has_key?("missing_key_calls")
|
||||
@config_map = state["config_map"] if state.key?("config_map")
|
||||
@keys = state["keys"] if state.key?("keys")
|
||||
@missing_key_calls = state["missing_key_calls"] if state.key?("missing_key_calls")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -80,7 +80,7 @@ module Vagrant
|
|||
}.merge(opts || {})
|
||||
|
||||
# Set the default working directory to look for the vagrantfile
|
||||
opts[:cwd] ||= ENV["VAGRANT_CWD"] if ENV.has_key?("VAGRANT_CWD")
|
||||
opts[:cwd] ||= ENV["VAGRANT_CWD"] if ENV.key?("VAGRANT_CWD")
|
||||
opts[:cwd] ||= Dir.pwd
|
||||
opts[:cwd] = Pathname.new(opts[:cwd])
|
||||
if !opts[:cwd].directory?
|
||||
|
@ -94,7 +94,7 @@ module Vagrant
|
|||
# Set the Vagrantfile name up. We append "Vagrantfile" and "vagrantfile" so that
|
||||
# those continue to work as well, but anything custom will take precedence.
|
||||
opts[:vagrantfile_name] ||= ENV["VAGRANT_VAGRANTFILE"] if \
|
||||
ENV.has_key?("VAGRANT_VAGRANTFILE")
|
||||
ENV.key?("VAGRANT_VAGRANTFILE")
|
||||
opts[:vagrantfile_name] = [opts[:vagrantfile_name]] if \
|
||||
opts[:vagrantfile_name] && !opts[:vagrantfile_name].is_a?(Array)
|
||||
|
||||
|
@ -164,6 +164,15 @@ module Vagrant
|
|||
@local_data_path = Pathname.new(File.expand_path(opts[:local_data_path], @cwd))
|
||||
end
|
||||
|
||||
# If we have a root path, load the ".vagrantplugins" file.
|
||||
if root_path
|
||||
plugins_file = root_path.join(".vagrantplugins")
|
||||
if plugins_file.file?
|
||||
@logger.info("Loading plugins file: #{plugins_file}")
|
||||
load plugins_file
|
||||
end
|
||||
end
|
||||
|
||||
setup_local_data_path
|
||||
|
||||
# Setup the default private key
|
||||
|
@ -298,7 +307,7 @@ module Vagrant
|
|||
# @return [Symbol] Name of the default provider.
|
||||
def default_provider(**opts)
|
||||
opts[:exclude] = Set.new(opts[:exclude]) if opts[:exclude]
|
||||
opts[:force_default] = true if !opts.has_key?(:force_default)
|
||||
opts[:force_default] = true if !opts.key?(:force_default)
|
||||
|
||||
default = ENV["VAGRANT_DEFAULT_PROVIDER"]
|
||||
default = nil if default == ""
|
||||
|
@ -308,6 +317,30 @@ module Vagrant
|
|||
# that (the default behavior)
|
||||
return default if default && opts[:force_default]
|
||||
|
||||
# Determine the config to use to look for provider definitions. By
|
||||
# default it is the global but if we're targeting a specific machine,
|
||||
# then look there.
|
||||
root_config = vagrantfile.config
|
||||
if opts[:machine]
|
||||
machine_info = vagrantfile.machine_config(opts[:machine], nil, nil)
|
||||
root_config = machine_info[:config]
|
||||
end
|
||||
|
||||
# Get the list of providers within our configuration and assign
|
||||
# a priority to each in the order they exist so that we try these first.
|
||||
config = {}
|
||||
root_config.vm.__providers.reverse.each_with_index do |key, idx|
|
||||
config[key] = idx
|
||||
end
|
||||
|
||||
# Determine the max priority so that we can add the config priority
|
||||
# to that to make sure it is highest.
|
||||
max_priority = 0
|
||||
Vagrant.plugin("2").manager.providers.each do |key, data|
|
||||
priority = data[1][:priority]
|
||||
max_priority = priority if priority > max_priority
|
||||
end
|
||||
|
||||
ordered = []
|
||||
Vagrant.plugin("2").manager.providers.each do |key, data|
|
||||
impl = data[0]
|
||||
|
@ -316,10 +349,19 @@ module Vagrant
|
|||
# Skip excluded providers
|
||||
next if opts[:exclude] && opts[:exclude].include?(key)
|
||||
|
||||
# Skip providers that can't be defaulted
|
||||
next if popts.has_key?(:defaultable) && !popts[:defaultable]
|
||||
# Skip providers that can't be defaulted, unless they're in our
|
||||
# config, in which case someone made our decision for us.
|
||||
if !config.key?(key)
|
||||
next if popts.key?(:defaultable) && !popts[:defaultable]
|
||||
end
|
||||
|
||||
ordered << [popts[:priority], key, impl, popts]
|
||||
# The priority is higher if it is in our config. Otherwise, it is
|
||||
# the priority it set PLUS the length of the config to make sure it
|
||||
# is never higher than the configuration keys.
|
||||
priority = popts[:priority]
|
||||
priority = config[key] + max_priority if config.key?(key)
|
||||
|
||||
ordered << [priority, key, impl, popts]
|
||||
end
|
||||
|
||||
# Order the providers by priority. Higher values are tried first.
|
||||
|
@ -336,8 +378,8 @@ module Vagrant
|
|||
return key if impl.usable?(false)
|
||||
end
|
||||
|
||||
# If all else fails, return VirtualBox
|
||||
return :virtualbox
|
||||
# No providers available is a critical error for Vagrant.
|
||||
raise Errors::NoDefaultProvider
|
||||
end
|
||||
|
||||
# Returns the collection of boxes for the environment.
|
||||
|
@ -497,6 +539,41 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
# This executes the push with the given name, raising any exceptions that
|
||||
# occur.
|
||||
#
|
||||
# Precondition: the push is not nil and exists.
|
||||
def push(name)
|
||||
@logger.info("Getting push: #{name}")
|
||||
|
||||
name = name.to_sym
|
||||
|
||||
pushes = self.vagrantfile.config.push.__compiled_pushes
|
||||
if !pushes.key?(name)
|
||||
raise Vagrant::Errors::PushStrategyNotDefined,
|
||||
name: name,
|
||||
pushes: pushes.keys
|
||||
end
|
||||
|
||||
strategy, config = pushes[name]
|
||||
push_registry = Vagrant.plugin("2").manager.pushes
|
||||
klass, _ = push_registry.get(strategy)
|
||||
if klass.nil?
|
||||
raise Vagrant::Errors::PushStrategyNotLoaded,
|
||||
name: strategy,
|
||||
pushes: push_registry.keys
|
||||
end
|
||||
|
||||
klass.new(self, config).push
|
||||
end
|
||||
|
||||
# The list of pushes defined in this Vagrantfile.
|
||||
#
|
||||
# @return [Array<Symbol>]
|
||||
def pushes
|
||||
self.vagrantfile.config.push.__compiled_pushes.keys
|
||||
end
|
||||
|
||||
# This returns a machine with the proper provider for this environment.
|
||||
# The machine named by `name` must be in this environment.
|
||||
#
|
||||
|
@ -519,7 +596,7 @@ module Vagrant
|
|||
@machines.delete(cache_key)
|
||||
end
|
||||
|
||||
if @machines.has_key?(cache_key)
|
||||
if @machines.key?(cache_key)
|
||||
@logger.info("Returning cached machine: #{name} (#{provider})")
|
||||
return @machines[cache_key]
|
||||
end
|
||||
|
@ -782,7 +859,10 @@ module Vagrant
|
|||
# This upgrades a home directory that was in the v1.1 format to the
|
||||
# v1.5 format. It will raise exceptions if anything fails.
|
||||
def upgrade_home_path_v1_1
|
||||
if !ENV["VAGRANT_UPGRADE_SILENT_1_5"]
|
||||
@ui.ask(I18n.t("vagrant.upgrading_home_path_v1_5"))
|
||||
end
|
||||
|
||||
collection = BoxCollection.new(
|
||||
@home_path.join("boxes"), temp_dir_root: tmp_path)
|
||||
collection.upgrade_v1_1_v1_5
|
||||
|
|
|
@ -476,6 +476,10 @@ module Vagrant
|
|||
error_key(:nfs_client_not_installed_in_guest)
|
||||
end
|
||||
|
||||
class NoDefaultProvider < VagrantError
|
||||
error_key(:no_default_provider)
|
||||
end
|
||||
|
||||
class NoDefaultSyncedFolderImpl < VagrantError
|
||||
error_key(:no_default_synced_folder_impl)
|
||||
end
|
||||
|
@ -552,6 +556,22 @@ module Vagrant
|
|||
error_key(:plugin_uninstall_system)
|
||||
end
|
||||
|
||||
class PushesNotDefined < VagrantError
|
||||
error_key(:pushes_not_defined)
|
||||
end
|
||||
|
||||
class PushStrategyNotDefined < VagrantError
|
||||
error_key(:push_strategy_not_defined)
|
||||
end
|
||||
|
||||
class PushStrategyNotLoaded < VagrantError
|
||||
error_key(:push_strategy_not_loaded)
|
||||
end
|
||||
|
||||
class PushStrategyNotProvided < VagrantError
|
||||
error_key(:push_strategy_not_provided)
|
||||
end
|
||||
|
||||
class RSyncError < VagrantError
|
||||
error_key(:rsync_error)
|
||||
end
|
||||
|
@ -616,6 +636,10 @@ module Vagrant
|
|||
error_key(:ssh_invalid_shell)
|
||||
end
|
||||
|
||||
class SSHInsertKeyUnsupported < VagrantError
|
||||
error_key(:ssh_insert_key_unsupported)
|
||||
end
|
||||
|
||||
class SSHIsPuttyLink < VagrantError
|
||||
error_key(:ssh_is_putty_link)
|
||||
end
|
||||
|
@ -728,6 +752,14 @@ module Vagrant
|
|||
error_key(:virtualbox_no_name)
|
||||
end
|
||||
|
||||
class VirtualBoxNameExists < VagrantError
|
||||
error_key(:virtualbox_name_exists)
|
||||
end
|
||||
|
||||
class VirtualBoxVersionEmpty < VagrantError
|
||||
error_key(:virtualbox_version_empty)
|
||||
end
|
||||
|
||||
class VMBaseMacNotSpecified < VagrantError
|
||||
error_key(:no_base_mac, "vagrant.actions.vm.match_mac")
|
||||
end
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
require_relative "util/ssh"
|
||||
|
||||
require "digest/md5"
|
||||
require "thread"
|
||||
|
||||
|
@ -6,7 +8,7 @@ require "log4r"
|
|||
module Vagrant
|
||||
# This represents a machine that Vagrant manages. This provides a singular
|
||||
# API for querying the state and making state changes to the machine, which
|
||||
# is backed by any sort of provider (VirtualBox, VMWare, etc.).
|
||||
# is backed by any sort of provider (VirtualBox, VMware, etc.).
|
||||
class Machine
|
||||
# The box that is backing this machine.
|
||||
#
|
||||
|
@ -133,6 +135,12 @@ module Vagrant
|
|||
@logger.debug("Eager loading WinRM communicator to avoid GH-3390")
|
||||
communicate
|
||||
end
|
||||
|
||||
# If the ID is the special not created ID, then set our ID to
|
||||
# nil so that we destroy all our data.
|
||||
if state.id == MachineState::NOT_CREATED_ID
|
||||
self.id = nil
|
||||
end
|
||||
end
|
||||
|
||||
# This calls an action on the provider. The provider may or may not
|
||||
|
@ -142,9 +150,18 @@ module Vagrant
|
|||
# @param [Hash] extra_env This data will be passed into the action runner
|
||||
# as extra data set on the environment hash for the middleware
|
||||
# runner.
|
||||
def action(name, extra_env=nil)
|
||||
def action(name, opts=nil)
|
||||
@logger.info("Calling action: #{name} on provider #{@provider}")
|
||||
|
||||
opts ||= {}
|
||||
|
||||
# Determine whether we lock or not
|
||||
lock = true
|
||||
lock = opts.delete(:lock) if opts.key?(:lock)
|
||||
|
||||
# Extra env keys are the remaining opts
|
||||
extra_env = opts.dup
|
||||
|
||||
# Create a deterministic ID for this machine
|
||||
vf = nil
|
||||
vf = @env.vagrantfile_name[0] if @env.vagrantfile_name
|
||||
|
@ -155,7 +172,7 @@ module Vagrant
|
|||
# we will want to do more fine-grained unlocking in actions themselves
|
||||
# but for a 1.6.2 release this will work.
|
||||
locker = Proc.new { |*args, &block| block.call }
|
||||
locker = @env.method(:lock) if !name.to_s.start_with?("ssh")
|
||||
locker = @env.method(:lock) if lock && !name.to_s.start_with?("ssh")
|
||||
|
||||
# Lock this machine for the duration of this action
|
||||
locker.call("machine-action-#{id}") do
|
||||
|
@ -170,6 +187,7 @@ module Vagrant
|
|||
provider: @provider.to_s
|
||||
end
|
||||
|
||||
# Call the action
|
||||
action_raw(name, callable, extra_env)
|
||||
end
|
||||
rescue Errors::EnvironmentLockedError
|
||||
|
@ -342,6 +360,7 @@ module Vagrant
|
|||
|
||||
# This reloads the ID of the underlying machine.
|
||||
def reload
|
||||
old_id = @id
|
||||
@id = nil
|
||||
|
||||
if @data_dir
|
||||
|
@ -350,6 +369,13 @@ module Vagrant
|
|||
id_file = @data_dir.join("id")
|
||||
@id = id_file.read.chomp if id_file.file?
|
||||
end
|
||||
|
||||
if @id != old_id && @provider
|
||||
# It changed, notify the provider
|
||||
@provider.machine_id_changed
|
||||
end
|
||||
|
||||
@id
|
||||
end
|
||||
|
||||
# This returns the SSH info for accessing this machine. This SSH info
|
||||
|
@ -434,6 +460,14 @@ module Vagrant
|
|||
File.expand_path(path, @env.root_path)
|
||||
end
|
||||
|
||||
# Check that the private key permissions are valid
|
||||
info[:private_key_path].each do |path|
|
||||
key_path = Pathname.new(path)
|
||||
if key_path.exist?
|
||||
Vagrant::Util::SSH.check_key_permissions(key_path)
|
||||
end
|
||||
end
|
||||
|
||||
# Return the final compiled SSH info data
|
||||
info
|
||||
end
|
||||
|
@ -446,12 +480,6 @@ module Vagrant
|
|||
result = @provider.state
|
||||
raise Errors::MachineStateInvalid if !result.is_a?(MachineState)
|
||||
|
||||
# If the ID is the special not created ID, then set our ID to
|
||||
# nil so that we destroy all our data.
|
||||
if result.id == MachineState::NOT_CREATED_ID
|
||||
self.id = nil
|
||||
end
|
||||
|
||||
# Update our state cache if we have a UUID and an entry in the
|
||||
# master index.
|
||||
uuid = index_uuid
|
||||
|
|
|
@ -143,7 +143,7 @@ module Vagrant
|
|||
|
||||
# If we already have a newer version in our list of installed,
|
||||
# then ignore it
|
||||
next if installed_map.has_key?(spec.name) &&
|
||||
next if installed_map.key?(spec.name) &&
|
||||
installed_map[spec.name].version >= spec.version
|
||||
|
||||
installed_map[spec.name] = spec
|
||||
|
|
|
@ -61,7 +61,7 @@ module Vagrant
|
|||
#
|
||||
# @return [Boolean]
|
||||
def has_plugin?(name)
|
||||
@data["installed"].has_key?(name)
|
||||
@data["installed"].key?(name)
|
||||
end
|
||||
|
||||
# Remove a plugin that is installed from the state file.
|
||||
|
|
|
@ -16,6 +16,7 @@ module Vagrant
|
|||
autoload :Manager, "vagrant/plugin/v2/manager"
|
||||
autoload :Plugin, "vagrant/plugin/v2/plugin"
|
||||
autoload :Provider, "vagrant/plugin/v2/provider"
|
||||
autoload :Push, "vagrant/plugin/v2/push"
|
||||
autoload :Provisioner, "vagrant/plugin/v2/provisioner"
|
||||
autoload :SyncedFolder, "vagrant/plugin/v2/synced_folder"
|
||||
end
|
||||
|
|
|
@ -162,7 +162,7 @@ module Vagrant
|
|||
end
|
||||
|
||||
# Use the default provider if nothing else
|
||||
provider_to_use ||= @env.default_provider
|
||||
provider_to_use ||= @env.default_provider(machine: name)
|
||||
|
||||
# Get the right machine with the right provider
|
||||
@env.machine(name, provider_to_use)
|
||||
|
@ -224,6 +224,15 @@ module Vagrant
|
|||
|
||||
@logger.info("With machine: #{machine.name} (#{machine.provider.inspect})")
|
||||
yield machine
|
||||
|
||||
# Call the state method so that we update our index state. Don't
|
||||
# worry about exceptions here, since we just care about updating
|
||||
# the cache.
|
||||
begin
|
||||
# Called for side effects
|
||||
machine.state
|
||||
rescue Errors::VagrantError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -54,6 +54,11 @@ module Vagrant
|
|||
# @return [Hash<Symbol, Registry>]
|
||||
attr_reader :provider_capabilities
|
||||
|
||||
# This contains all the push implementations by name.
|
||||
#
|
||||
# @return [Registry<Symbol, Array<Class, Hash>>]
|
||||
attr_reader :pushes
|
||||
|
||||
# This contains all the synced folder implementations by name.
|
||||
#
|
||||
# @return [Registry<Symbol, Array<Class, Integer>>]
|
||||
|
@ -71,6 +76,7 @@ module Vagrant
|
|||
@host_capabilities = Hash.new { |h, k| h[k] = Registry.new }
|
||||
@providers = Registry.new
|
||||
@provider_capabilities = Hash.new { |h, k| h[k] = Registry.new }
|
||||
@pushes = Registry.new
|
||||
@synced_folders = Registry.new
|
||||
end
|
||||
end
|
||||
|
|
|
@ -172,6 +172,28 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
# This returns all registered pushes.
|
||||
#
|
||||
# @return [Registry]
|
||||
def pushes
|
||||
Registry.new.tap do |result|
|
||||
@registered.each do |plugin|
|
||||
result.merge!(plugin.components.pushes)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This returns all the config classes for the various pushes.
|
||||
#
|
||||
# @return [Registry]
|
||||
def push_configs
|
||||
Registry.new.tap do |result|
|
||||
@registered.each do |plugin|
|
||||
result.merge!(plugin.components.configs[:push])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# This returns all synced folder implementations.
|
||||
#
|
||||
# @return [Registry]
|
||||
|
|
|
@ -88,7 +88,7 @@ module Vagrant
|
|||
end
|
||||
|
||||
# By default, the command is primary
|
||||
opts[:primary] = true if !opts.has_key?(:primary)
|
||||
opts[:primary] = true if !opts.key?(:primary)
|
||||
|
||||
# Register the command
|
||||
components.commands.register(name.to_sym) do
|
||||
|
@ -221,6 +221,18 @@ module Vagrant
|
|||
data[:provisioners]
|
||||
end
|
||||
|
||||
# Registers additional pushes to be available.
|
||||
#
|
||||
# @param [String] name Name of the push.
|
||||
# @param [Hash] options List of options for the push.
|
||||
def self.push(name, options=nil, &block)
|
||||
components.pushes.register(name.to_sym) do
|
||||
[block.call, options]
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
# Registers additional synced folder implementations.
|
||||
#
|
||||
# @param [String] name Name of the implementation.
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
module Vagrant
|
||||
module Plugin
|
||||
module V2
|
||||
class Push
|
||||
attr_reader :env
|
||||
attr_reader :config
|
||||
|
||||
# Initializes the pusher with the given environment the push
|
||||
# configuration.
|
||||
#
|
||||
# @param [Environment] env
|
||||
# @param [Object] config Push configuration
|
||||
def initialize(env, config)
|
||||
@env = env
|
||||
@config = config
|
||||
end
|
||||
|
||||
# This is the method called when the actual pushing should be
|
||||
# done.
|
||||
#
|
||||
# No return value is expected.
|
||||
def push
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -22,8 +22,8 @@ module Vagrant
|
|||
# This will evaluate the block given to `register` and return the
|
||||
# resulting value.
|
||||
def get(key)
|
||||
return nil if !@items.has_key?(key)
|
||||
return @results_cache[key] if @results_cache.has_key?(key)
|
||||
return nil if !@items.key?(key)
|
||||
return @results_cache[key] if @results_cache.key?(key)
|
||||
@results_cache[key] = @items[key].call
|
||||
end
|
||||
alias :[] :get
|
||||
|
@ -31,9 +31,10 @@ module Vagrant
|
|||
# Checks if the given key is registered with the registry.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def has_key?(key)
|
||||
@items.has_key?(key)
|
||||
def key?(key)
|
||||
@items.key?(key)
|
||||
end
|
||||
alias_method :has_key?, :key?
|
||||
|
||||
# Returns an array populated with the keys of this object.
|
||||
#
|
||||
|
@ -49,6 +50,21 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
# Return the number of elements in this registry.
|
||||
#
|
||||
# @return [Fixnum]
|
||||
def length
|
||||
@items.keys.length
|
||||
end
|
||||
alias_method :size, :length
|
||||
|
||||
# Checks if this registry has any items.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def empty?
|
||||
@items.keys.empty?
|
||||
end
|
||||
|
||||
# Merge one registry with another and return a completely new
|
||||
# registry. Note that the result cache is completely busted, so
|
||||
# any gets on the new registry will result in a cache miss.
|
||||
|
|
|
@ -5,12 +5,12 @@ require "thread"
|
|||
module Vagrant
|
||||
@@global_lock = Mutex.new
|
||||
|
||||
# This is the default endpoint of the Vagrant Cloud in
|
||||
# This is the default endpoint of the Atlas in
|
||||
# use. API calls will be made to this for various functions
|
||||
# of Vagrant that may require remote access.
|
||||
#
|
||||
# @return [String]
|
||||
DEFAULT_SERVER_URL = "https://vagrantcloud.com"
|
||||
DEFAULT_SERVER_URL = "https://atlas.hashicorp.com"
|
||||
|
||||
# This holds a global lock for the duration of the block. This should
|
||||
# be invoked around anything that is modifying process state (such as
|
||||
|
@ -52,6 +52,13 @@ module Vagrant
|
|||
!!ENV["VAGRANT_I_KNOW_WHAT_IM_DOING_PLEASE_BE_QUIET"]
|
||||
end
|
||||
|
||||
# The current log level for Vagrant
|
||||
#
|
||||
# @return [String]
|
||||
def self.log_level
|
||||
ENV["VAGRANT_LOG"]
|
||||
end
|
||||
|
||||
# Returns the URL prefix to the server.
|
||||
#
|
||||
# @return [String]
|
||||
|
|
|
@ -136,9 +136,9 @@ module Vagrant
|
|||
|
||||
# Setup the options so that the new line is suppressed
|
||||
opts ||= {}
|
||||
opts[:echo] = true if !opts.has_key?(:echo)
|
||||
opts[:new_line] = false if !opts.has_key?(:new_line)
|
||||
opts[:prefix] = false if !opts.has_key?(:prefix)
|
||||
opts[:echo] = true if !opts.key?(:echo)
|
||||
opts[:new_line] = false if !opts.key?(:new_line)
|
||||
opts[:prefix] = false if !opts.key?(:prefix)
|
||||
|
||||
# Output the data
|
||||
say(:info, message, opts)
|
||||
|
@ -249,7 +249,7 @@ module Vagrant
|
|||
class_eval <<-CODE
|
||||
def #{method}(message, *args, **opts)
|
||||
super(message)
|
||||
if !@ui.opts.has_key?(:bold) && !opts.has_key?(:bold)
|
||||
if !@ui.opts.key?(:bold) && !opts.key?(:bold)
|
||||
opts[:bold] = #{method.inspect} != :detail && \
|
||||
#{method.inspect} != :ask
|
||||
end
|
||||
|
@ -284,7 +284,7 @@ module Vagrant
|
|||
opts = self.opts.merge(opts)
|
||||
|
||||
prefix = ""
|
||||
if !opts.has_key?(:prefix) || opts[:prefix]
|
||||
if !opts.key?(:prefix) || opts[:prefix]
|
||||
prefix = OUTPUT_PREFIX
|
||||
prefix = " " * OUTPUT_PREFIX.length if \
|
||||
type == :detail || type == :ask || opts[:prefix_spaces]
|
||||
|
@ -294,7 +294,7 @@ module Vagrant
|
|||
return message if prefix.empty?
|
||||
|
||||
target = @prefix
|
||||
target = opts[:target] if opts.has_key?(:target)
|
||||
target = opts[:target] if opts.key?(:target)
|
||||
|
||||
# Get the lines. The first default is because if the message
|
||||
# is an empty string, then we want to still use the empty string.
|
||||
|
|
|
@ -2,6 +2,7 @@ module Vagrant
|
|||
module Util
|
||||
autoload :Busy, 'vagrant/util/busy'
|
||||
autoload :Counter, 'vagrant/util/counter'
|
||||
autoload :Env, 'vagrant/util/env'
|
||||
autoload :HashWithIndifferentAccess, 'vagrant/util/hash_with_indifferent_access'
|
||||
autoload :Platform, 'vagrant/util/platform'
|
||||
autoload :Retryable, 'vagrant/util/retryable'
|
||||
|
|
|
@ -58,18 +58,15 @@ module Vagrant
|
|||
# If this method returns without an exception, the download
|
||||
# succeeded. An exception will be raised if the download failed.
|
||||
def download!
|
||||
options, subprocess_options = self.options
|
||||
options += ["--output", @destination]
|
||||
options << @source
|
||||
|
||||
# This variable can contain the proc that'll be sent to
|
||||
# the subprocess execute.
|
||||
data_proc = nil
|
||||
|
||||
extra_subprocess_opts = {}
|
||||
if @ui
|
||||
# If we're outputting progress, then setup the subprocess to
|
||||
# tell us output so we can parse it out.
|
||||
subprocess_options[:notify] = :stderr
|
||||
extra_subprocess_opts[:notify] = :stderr
|
||||
|
||||
progress_data = ""
|
||||
progress_regexp = /(\r(.+?))\r/
|
||||
|
@ -121,8 +118,31 @@ module Vagrant
|
|||
@logger.info(" -- Source: #{@source}")
|
||||
@logger.info(" -- Destination: #{@destination}")
|
||||
|
||||
retried = false
|
||||
begin
|
||||
# Get the command line args and the subprocess opts based
|
||||
# on our downloader settings.
|
||||
options, subprocess_options = self.options
|
||||
options += ["--output", @destination]
|
||||
options << @source
|
||||
|
||||
# Merge in any extra options we set
|
||||
subprocess_options.merge!(extra_subprocess_opts)
|
||||
|
||||
# Go!
|
||||
execute_curl(options, subprocess_options, &data_proc)
|
||||
rescue Errors::DownloaderError => e
|
||||
# If we already retried, raise it.
|
||||
raise if retried
|
||||
|
||||
# If its any error other than 33, it is an error.
|
||||
raise if e.extra_data[:exit_code].to_i != 33
|
||||
|
||||
# Exit code 33 means that the server doesn't support ranges.
|
||||
# In this case, try again without resume.
|
||||
@continue = false
|
||||
retried = true
|
||||
retry
|
||||
ensure
|
||||
# If we're outputting to the UI, clear the output to
|
||||
# avoid lingering progress meters.
|
||||
|
@ -177,7 +197,9 @@ module Vagrant
|
|||
@logger.warn("Downloader exit code: #{result.exit_code}")
|
||||
parts = result.stderr.split(/\n*curl:\s+\(\d+\)\s*/, 2)
|
||||
parts[1] ||= ""
|
||||
raise Errors::DownloaderError, message: parts[1].chomp
|
||||
raise Errors::DownloaderError,
|
||||
code: result.exit_code,
|
||||
message: parts[1].chomp
|
||||
end
|
||||
|
||||
result
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
module Vagrant
|
||||
module Util
|
||||
class Env
|
||||
#
|
||||
# Execute the given command, removing any Ruby-specific environment
|
||||
# variables. This is an "enhanced" version of `Bundler.with_clean_env`,
|
||||
# which only removes Bundler-specific values. We need to remove all
|
||||
# values, specifically:
|
||||
#
|
||||
# - _ORIGINAL_GEM_PATH
|
||||
# - GEM_PATH
|
||||
# - GEM_HOME
|
||||
# - GEM_ROOT
|
||||
# - BUNDLE_BIN_PATH
|
||||
# - BUNDLE_GEMFILE
|
||||
# - RUBYLIB
|
||||
# - RUBYOPT
|
||||
# - RUBY_ENGINE
|
||||
# - RUBY_ROOT
|
||||
# - RUBY_VERSION
|
||||
#
|
||||
# This will escape Vagrant's environment entirely, which is required if
|
||||
# calling an executable that lives in another Ruby environment. The
|
||||
# original environment restored at the end of this call.
|
||||
#
|
||||
# @param [Proc] block
|
||||
# the block to execute with the cleaned environment
|
||||
#
|
||||
def self.with_clean_env(&block)
|
||||
original = ENV.to_hash
|
||||
|
||||
ENV.delete('_ORIGINAL_GEM_PATH')
|
||||
ENV.delete_if { |k,_| k.start_with?('BUNDLE_') }
|
||||
ENV.delete_if { |k,_| k.start_with?('GEM_') }
|
||||
ENV.delete_if { |k,_| k.start_with?('RUBY') }
|
||||
|
||||
yield
|
||||
ensure
|
||||
ENV.replace(original.to_hash)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -29,7 +29,7 @@ module Vagrant
|
|||
break if !results || results[0].empty?
|
||||
|
||||
# Read!
|
||||
data << io.readpartial(READ_CHUNK_SIZE)
|
||||
data << io.readpartial(READ_CHUNK_SIZE).encode("UTF-8", Encoding.default_external)
|
||||
else
|
||||
# Do a simple non-blocking read on the IO object
|
||||
data << io.read_nonblock(READ_CHUNK_SIZE)
|
||||
|
|
|
@ -29,7 +29,8 @@ module Vagrant
|
|||
# to connect.
|
||||
return true
|
||||
end
|
||||
rescue Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH
|
||||
rescue Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, \
|
||||
Errno::ENETUNREACH, Errno::EACCES
|
||||
# Any of the above exceptions signal that the port is closed.
|
||||
return false
|
||||
end
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
require "base64"
|
||||
require "openssl"
|
||||
|
||||
module Vagrant
|
||||
module Util
|
||||
class Keypair
|
||||
# Creates an SSH keypair and returns it.
|
||||
#
|
||||
# @param [String] password Password for the key, or nil for no password.
|
||||
# @return [Array<String, String, String>] PEM-encoded public and private key,
|
||||
# respectively. The final element is the OpenSSH encoded public
|
||||
# key.
|
||||
def self.create(password=nil)
|
||||
rsa_key = OpenSSL::PKey::RSA.new(2048)
|
||||
public_key = rsa_key.public_key
|
||||
private_key = rsa_key.to_pem
|
||||
|
||||
if password
|
||||
cipher = OpenSSL::Cipher::Cipher.new('des3')
|
||||
private_key = rsa_key.to_pem(cipher, password)
|
||||
end
|
||||
|
||||
# Generate the binary necessary for the OpenSSH public key.
|
||||
binary = [7].pack("N")
|
||||
binary += "ssh-rsa"
|
||||
["e", "n"].each do |m|
|
||||
val = public_key.send(m)
|
||||
data = val.to_s(2)
|
||||
|
||||
first_byte = data[0,1].unpack("c").first
|
||||
if val < 0
|
||||
data[0] = [0x80 & first_byte].pack("c")
|
||||
elsif first_byte < 0
|
||||
data = 0.chr + data
|
||||
end
|
||||
|
||||
binary += [data.length].pack("N") + data
|
||||
end
|
||||
|
||||
openssh_key = "ssh-rsa #{Base64.encode64(binary).gsub("\n", "")} vagrant"
|
||||
public_key = public_key.to_pem
|
||||
return [public_key, private_key, openssh_key]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -148,7 +148,7 @@ module Vagrant
|
|||
# output.
|
||||
def terminal_supports_colors?
|
||||
if windows?
|
||||
return true if ENV.has_key?("ANSICON")
|
||||
return true if ENV.key?("ANSICON")
|
||||
return true if cygwin?
|
||||
return true if ENV["TERM"] == "cygwin"
|
||||
return false
|
||||
|
|
|
@ -102,12 +102,14 @@ module Vagrant
|
|||
options[:username] = ssh_info[:username]
|
||||
options[:private_key_path] = ssh_info[:private_key_path]
|
||||
|
||||
log_level = ssh_info[:log_level] || "FATAL"
|
||||
|
||||
# Command line options
|
||||
command_options = [
|
||||
"-p", options[:port].to_s,
|
||||
"-o", "Compression=yes",
|
||||
"-o", "DSAAuthentication=yes",
|
||||
"-o", "LogLevel=FATAL",
|
||||
"-o", "LogLevel=#{log_level}",
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "UserKnownHostsFile=/dev/null"]
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ module Vagrant
|
|||
def initialize(*command)
|
||||
@options = command.last.is_a?(Hash) ? command.pop : {}
|
||||
@command = command.dup
|
||||
@command.each { |s| s.encode(Encoding.default_external) }
|
||||
@command[0] = Which.which(@command[0]) if !File.file?(@command[0])
|
||||
if !@command[0]
|
||||
raise Errors::CommandUnavailableWindows, file: command[0] if Platform.windows?
|
||||
|
|
|
@ -114,6 +114,11 @@ module Vagrant
|
|||
name: name, provider: provider
|
||||
end
|
||||
|
||||
provider_plugin = nil
|
||||
provider_cls = nil
|
||||
provider_options = {}
|
||||
box_formats = nil
|
||||
if provider != nil
|
||||
provider_plugin = Vagrant.plugin("2").manager.providers[provider]
|
||||
if !provider_plugin
|
||||
raise Errors::ProviderNotFound,
|
||||
|
@ -133,6 +138,7 @@ module Vagrant
|
|||
provider: provider.to_s,
|
||||
message: e.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# Add the sub-machine configuration to the loader and keys
|
||||
vm_config_key = "#{object_id}_machine_#{name}"
|
||||
|
@ -153,7 +159,7 @@ module Vagrant
|
|||
local_keys = keys.dup
|
||||
|
||||
# Load the box Vagrantfile, if there is one
|
||||
if config.vm.box
|
||||
if config.vm.box && boxes
|
||||
box = boxes.find(config.vm.box, box_formats, config.vm.box_version)
|
||||
if box
|
||||
box_vagrantfile = find_vagrantfile(box.directory)
|
||||
|
|
|
@ -47,7 +47,7 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
o.separator ""
|
||||
o.separator "The box descriptor can be the name of a box on Vagrant Cloud,"
|
||||
o.separator "The box descriptor can be the name of a box on HashiCorp's Atlas,"
|
||||
o.separator "or a URL, or a local .box file, or a local .json file containing"
|
||||
o.separator "the catalog metadata."
|
||||
o.separator ""
|
||||
|
|
|
@ -32,7 +32,7 @@ module VagrantPlugins
|
|||
:destroy, force_confirm_destroy: options[:force])
|
||||
|
||||
total += 1
|
||||
declined += 1 if action_env.has_key?(:force_confirm_destroy_result) &&
|
||||
declined += 1 if action_env.key?(:force_confirm_destroy_result) &&
|
||||
action_env[:force_confirm_destroy_result] == false
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
require "rest_client"
|
||||
|
||||
module VagrantPlugins
|
||||
module LoginCommand
|
||||
class Client
|
||||
# Initializes a login client with the given Vagrant::Environment.
|
||||
#
|
||||
# @param [Vagrant::Environment] env
|
||||
def initialize(env)
|
||||
@logger = Log4r::Logger.new("vagrant::login::client")
|
||||
@env = env
|
||||
end
|
||||
|
||||
# Removes the token, effectively logging the user out.
|
||||
def clear_token
|
||||
@logger.info("Clearing token")
|
||||
token_path.delete if token_path.file?
|
||||
end
|
||||
|
||||
# Checks if the user is logged in by verifying their authentication
|
||||
# token.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def logged_in?
|
||||
token = self.token
|
||||
return false if !token
|
||||
|
||||
with_error_handling do
|
||||
url = "#{Vagrant.server_url}/api/v1/authenticate" +
|
||||
"?access_token=#{token}"
|
||||
RestClient.get(url, content_type: :json)
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
# Login logs a user in and returns the token for that user. The token
|
||||
# is _not_ stored unless {#store_token} is called.
|
||||
#
|
||||
# @param [String] user
|
||||
# @param [String] pass
|
||||
# @return [String] token The access token, or nil if auth failed.
|
||||
def login(user, pass)
|
||||
@logger.info("Logging in '#{user}'")
|
||||
|
||||
with_error_handling do
|
||||
url = "#{Vagrant.server_url}/api/v1/authenticate"
|
||||
request = { "user" => { "login" => user, "password" => pass } }
|
||||
response = RestClient.post(
|
||||
url, JSON.dump(request), content_type: :json)
|
||||
data = JSON.load(response.to_s)
|
||||
data["token"]
|
||||
end
|
||||
end
|
||||
|
||||
# Stores the given token locally, removing any previous tokens.
|
||||
#
|
||||
# @param [String] token
|
||||
def store_token(token)
|
||||
@logger.info("Storing token in #{token_path}")
|
||||
|
||||
token_path.open("w") do |f|
|
||||
f.write(token)
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
# Reads the access token if there is one. This will first read the
|
||||
# `ATLAS_TOKEN` environment variable and then fallback to the stored
|
||||
# access token on disk.
|
||||
#
|
||||
# @return [String]
|
||||
def token
|
||||
if ENV["ATLAS_TOKEN"] && !ENV["ATLAS_TOKEN"].empty?
|
||||
@logger.debug("Using authentication token from environment variable")
|
||||
return ENV["ATLAS_TOKEN"]
|
||||
end
|
||||
|
||||
if token_path.exist?
|
||||
@logger.debug("Using authentication token from disk at #{token_path}")
|
||||
return token_path.read.strip
|
||||
end
|
||||
|
||||
@logger.debug("No authentication token in environment or #{token_path}")
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def with_error_handling(&block)
|
||||
yield
|
||||
rescue RestClient::Unauthorized
|
||||
@logger.debug("Unauthorized!")
|
||||
false
|
||||
rescue RestClient::NotAcceptable => e
|
||||
@logger.debug("Got unacceptable response:")
|
||||
@logger.debug(e.message)
|
||||
@logger.debug(e.backtrace.join("\n"))
|
||||
|
||||
begin
|
||||
errors = JSON.parse(e.response)["errors"].join("\n")
|
||||
raise Errors::ServerError, errors: errors
|
||||
rescue JSON::ParserError; end
|
||||
|
||||
raise "An unexpected error occurred: #{e.inspect}"
|
||||
rescue SocketError
|
||||
@logger.info("Socket error")
|
||||
raise Errors::ServerUnreachable, url: Vagrant.server_url.to_s
|
||||
end
|
||||
|
||||
def token_path
|
||||
@env.data_dir.join("vagrant_login_token")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,102 @@
|
|||
module VagrantPlugins
|
||||
module LoginCommand
|
||||
class Command < Vagrant.plugin("2", "command")
|
||||
def self.synopsis
|
||||
"log in to HashiCorp's Atlas"
|
||||
end
|
||||
|
||||
def execute
|
||||
options = {}
|
||||
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant login"
|
||||
o.separator ""
|
||||
o.on("-c", "--check", "Only checks if you're logged in") do |c|
|
||||
options[:check] = c
|
||||
end
|
||||
|
||||
o.on("-k", "--logout", "Logs you out if you're logged in") do |k|
|
||||
options[:logout] = k
|
||||
end
|
||||
|
||||
o.on("-t", "--token TOKEN", String, "Set the Atlas token") do |t|
|
||||
options[:token] = t
|
||||
end
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
argv = parse_options(opts)
|
||||
return if !argv
|
||||
|
||||
@client = Client.new(@env)
|
||||
|
||||
# Determine what task we're actually taking based on flags
|
||||
if options[:check]
|
||||
return execute_check
|
||||
elsif options[:logout]
|
||||
return execute_logout
|
||||
elsif options[:token]
|
||||
return execute_token(options[:token])
|
||||
end
|
||||
|
||||
# Let the user know what is going on.
|
||||
@env.ui.output(I18n.t("login_command.command_header") + "\n")
|
||||
|
||||
# If it is a private cloud installation, show that
|
||||
if Vagrant.server_url != Vagrant::DEFAULT_SERVER_URL
|
||||
@env.ui.output("Atlas URL: #{Vagrant.server_url}")
|
||||
end
|
||||
|
||||
# Ask for the username
|
||||
login = nil
|
||||
password = nil
|
||||
while !login
|
||||
login = @env.ui.ask("Atlas Username: ")
|
||||
end
|
||||
|
||||
while !password
|
||||
password = @env.ui.ask("Password (will be hidden): ", echo: false)
|
||||
end
|
||||
|
||||
token = @client.login(login, password)
|
||||
if !token
|
||||
@env.ui.error(I18n.t("login_command.invalid_login"))
|
||||
return 1
|
||||
end
|
||||
|
||||
@client.store_token(token)
|
||||
@env.ui.success(I18n.t("login_command.logged_in"))
|
||||
0
|
||||
end
|
||||
|
||||
def execute_check
|
||||
if @client.logged_in?
|
||||
@env.ui.success(I18n.t("login_command.check_logged_in"))
|
||||
return 0
|
||||
else
|
||||
@env.ui.error(I18n.t("login_command.check_not_logged_in"))
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
def execute_logout
|
||||
@client.clear_token
|
||||
@env.ui.success(I18n.t("login_command.logged_out"))
|
||||
return 0
|
||||
end
|
||||
|
||||
def execute_token(token)
|
||||
@client.store_token(token)
|
||||
@env.ui.success(I18n.t("login_command.token_saved"))
|
||||
|
||||
if @client.logged_in?
|
||||
@env.ui.success(I18n.t("login_command.check_logged_in"))
|
||||
return 0
|
||||
else
|
||||
@env.ui.error(I18n.t("login_command.invalid_token"))
|
||||
return 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
module VagrantPlugins
|
||||
module LoginCommand
|
||||
module Errors
|
||||
class Error < Vagrant::Errors::VagrantError
|
||||
error_namespace("login_command.errors")
|
||||
end
|
||||
|
||||
class ServerError < Error
|
||||
error_key(:server_error)
|
||||
end
|
||||
|
||||
class ServerUnreachable < Error
|
||||
error_key(:server_unreachable)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,34 @@
|
|||
en:
|
||||
login_command:
|
||||
errors:
|
||||
server_error: |-
|
||||
The Atlas server responded with an not-OK response:
|
||||
|
||||
%{errors}
|
||||
server_unreachable: |-
|
||||
The Atlas server is not currently accepting connections. Please check
|
||||
your network connection and try again later.
|
||||
|
||||
check_logged_in: |-
|
||||
You are already logged in.
|
||||
check_not_logged_in: |-
|
||||
You are not currently logged in. Please run `vagrant login` and provide
|
||||
your login information to authenticate.
|
||||
command_header: |-
|
||||
In a moment we will ask for your username and password to HashiCorp's
|
||||
Atlas. After authenticating, we will store an access token locally on
|
||||
disk. Your login details will be transmitted over a secure connection, and
|
||||
are never stored on disk locally.
|
||||
|
||||
If you do not have an Atlas account, sign up at
|
||||
https://atlas.hashicorp.com.
|
||||
invalid_login: |-
|
||||
Invalid username or password. Please try again.
|
||||
invalid_token: |-
|
||||
Invalid token. Please try again.
|
||||
logged_in: |-
|
||||
You are now logged in.
|
||||
logged_out: |-
|
||||
You are logged out.
|
||||
token_saved: |-
|
||||
The token was successfully saved.
|
|
@ -0,0 +1,45 @@
|
|||
require "uri"
|
||||
|
||||
require_relative "../client"
|
||||
|
||||
module VagrantPlugins
|
||||
module LoginCommand
|
||||
class AddAuthentication
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
client = Client.new(env[:env])
|
||||
token = client.token
|
||||
|
||||
if token && Vagrant.server_url
|
||||
server_uri = URI.parse(Vagrant.server_url)
|
||||
|
||||
env[:box_urls].map! do |url|
|
||||
u = URI.parse(url)
|
||||
replace = u.host == server_uri.host
|
||||
if !replace
|
||||
# We need this in here for the transition we made from
|
||||
# Vagrant Cloud to Atlas. This preserves access tokens
|
||||
# appending to both without leaking access tokens to
|
||||
# unsavory URLs.
|
||||
replace = u.host == "vagrantcloud.com" &&
|
||||
server_uri.host == "atlas.hashicorp.com"
|
||||
end
|
||||
|
||||
if replace
|
||||
u.query ||= ""
|
||||
u.query += "&" if u.query != ""
|
||||
u.query += "access_token=#{token}"
|
||||
end
|
||||
|
||||
u.to_s
|
||||
end
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,35 @@
|
|||
require "vagrant"
|
||||
|
||||
module VagrantPlugins
|
||||
module LoginCommand
|
||||
autoload :Client, File.expand_path("../client", __FILE__)
|
||||
autoload :Errors, File.expand_path("../errors", __FILE__)
|
||||
|
||||
class Plugin < Vagrant.plugin("2")
|
||||
name "vagrant-login"
|
||||
description <<-DESC
|
||||
Provides the login command and internal API access to Atlas.
|
||||
DESC
|
||||
|
||||
command(:login) do
|
||||
require_relative "command"
|
||||
init!
|
||||
Command
|
||||
end
|
||||
|
||||
action_hook(:cloud_authenticated_boxes, :authenticate_box_url) do |hook|
|
||||
require_relative "middleware/add_authentication"
|
||||
hook.prepend(AddAuthentication)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def self.init!
|
||||
return if defined?(@_init)
|
||||
I18n.load_path << File.expand_path("../locales/en.yml", __FILE__)
|
||||
I18n.reload!
|
||||
@_init = true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -34,20 +34,29 @@ module VagrantPlugins
|
|||
system = ", system" if plugin && plugin["system"]
|
||||
env[:ui].info "#{spec.name} (#{spec.version}#{system})"
|
||||
env[:ui].machine("plugin-name", spec.name)
|
||||
env[:ui].machine("plugin-version", "#{spec.version}#{system}")
|
||||
env[:ui].machine(
|
||||
"plugin-version",
|
||||
"#{spec.version}#{system}",
|
||||
target: spec.name)
|
||||
|
||||
if plugin["gem_version"] && plugin["gem_version"] != ""
|
||||
env[:ui].info(I18n.t(
|
||||
"vagrant.commands.plugin.plugin_version",
|
||||
version: plugin["gem_version"]))
|
||||
env[:ui].machine("plugin-version-constraint", plugin["gem_version"])
|
||||
env[:ui].machine(
|
||||
"plugin-version-constraint",
|
||||
plugin["gem_version"],
|
||||
target: spec.name)
|
||||
end
|
||||
|
||||
if plugin["require"] && plugin["require"] != ""
|
||||
env[:ui].info(I18n.t(
|
||||
"vagrant.commands.plugin.plugin_require",
|
||||
require: plugin["require"]))
|
||||
env[:ui].machine("plugin-custom-entrypoint", plugin["require"])
|
||||
env[:ui].machine(
|
||||
"plugin-custom-entrypoint",
|
||||
plugin["require"],
|
||||
target: spec.name)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ module VagrantPlugins
|
|||
|
||||
def call(env)
|
||||
installed = Vagrant::Plugin::Manager.instance.installed_plugins
|
||||
if !installed.has_key?(env[:plugin_name])
|
||||
if !installed.key?(env[:plugin_name])
|
||||
raise Vagrant::Errors::PluginNotInstalled,
|
||||
name: env[:plugin_name]
|
||||
end
|
||||
|
|
|
@ -0,0 +1,75 @@
|
|||
require 'optparse'
|
||||
|
||||
module VagrantPlugins
|
||||
module CommandPush
|
||||
class Command < Vagrant.plugin("2", :command)
|
||||
def self.synopsis
|
||||
"deploys code in this environment to a configured destination"
|
||||
end
|
||||
|
||||
# @todo support multiple strategies if requested by the community
|
||||
def execute
|
||||
opts = OptionParser.new do |o|
|
||||
o.banner = "Usage: vagrant push [strategy] [options]"
|
||||
end
|
||||
|
||||
# Parse the options
|
||||
argv = parse_options(opts)
|
||||
return if !argv
|
||||
|
||||
name = validate_pushes!(@env.pushes, argv[0])
|
||||
|
||||
# Validate the configuration
|
||||
@env.machine(@env.machine_names.first, @env.default_provider).action_raw(
|
||||
:config_validate,
|
||||
Vagrant::Action::Builtin::ConfigValidate)
|
||||
|
||||
@logger.debug("'push' environment with strategy: `#{name}'")
|
||||
@env.push(name)
|
||||
|
||||
0
|
||||
end
|
||||
|
||||
# Validate that the given list of names corresponds to valid pushes.
|
||||
#
|
||||
# @raise Vagrant::Errors::PushesNotDefined
|
||||
# if there are no pushes defined
|
||||
# @raise Vagrant::Errors::PushStrategyNotProvided
|
||||
# if there are multiple push strategies defined and none were specified
|
||||
# @raise Vagrant::Errors::PushStrategyNotDefined
|
||||
# if the given push name do not correspond to a push strategy
|
||||
#
|
||||
# @param [Array<Symbol>] pushes
|
||||
# the list of pushes defined by the environment
|
||||
# @param [String] name
|
||||
# the name provided by the user on the command line
|
||||
#
|
||||
# @return [Symbol]
|
||||
# the compiled list of pushes
|
||||
#
|
||||
def validate_pushes!(pushes, name = nil)
|
||||
if pushes.nil? || pushes.empty?
|
||||
raise Vagrant::Errors::PushesNotDefined
|
||||
end
|
||||
|
||||
if name.nil?
|
||||
if pushes.length == 1
|
||||
return pushes.first.to_sym
|
||||
else
|
||||
raise Vagrant::Errors::PushStrategyNotProvided,
|
||||
pushes: pushes.map(&:to_s)
|
||||
end
|
||||
end
|
||||
|
||||
name = name.to_sym
|
||||
if !pushes.include?(name)
|
||||
raise Vagrant::Errors::PushStrategyNotDefined,
|
||||
name: name.to_s,
|
||||
pushes: pushes.map(&:to_s)
|
||||
end
|
||||
|
||||
return name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
require "vagrant"
|
||||
|
||||
module VagrantPlugins
|
||||
module CommandPush
|
||||
class Plugin < Vagrant.plugin("2")
|
||||
name "push command"
|
||||
description <<-DESC
|
||||
The `push` command deploys code in this environment.
|
||||
DESC
|
||||
|
||||
command("push") do
|
||||
require File.expand_path("../command", __FILE__)
|
||||
Command
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -58,12 +58,19 @@ module VagrantPlugins
|
|||
@env.batch(options[:parallel]) do |batch|
|
||||
names = argv
|
||||
if names.empty?
|
||||
autostart = false
|
||||
@env.vagrantfile.machine_names_and_options.each do |n, o|
|
||||
o[:autostart] = true if !o.has_key?(:autostart)
|
||||
autostart = true if o.key?(:autostart)
|
||||
o[:autostart] = true if !o.key?(:autostart)
|
||||
names << n.to_s if o[:autostart]
|
||||
end
|
||||
|
||||
# If we have an autostart key but no names, it means that
|
||||
# all machines are autostart: false and we don't start anything.
|
||||
names = nil if autostart && names.empty?
|
||||
end
|
||||
|
||||
if names
|
||||
with_target_vms(names, provider: options[:provider]) do |machine|
|
||||
@env.ui.info(I18n.t(
|
||||
"vagrant.commands.up.upping",
|
||||
|
@ -75,6 +82,12 @@ module VagrantPlugins
|
|||
batch.action(machine, :up, options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if machines.empty?
|
||||
@env.ui.info(I18n.t("vagrant.up_no_machines"))
|
||||
return 0
|
||||
end
|
||||
|
||||
# Output the post-up messages that we have, if any
|
||||
machines.each do |m|
|
||||
|
|
|
@ -19,6 +19,8 @@ module VagrantPlugins
|
|||
parser.on("--provision-with x,y,z", Array,
|
||||
"Enable only certain provisioners, by type.") do |list|
|
||||
options[:provision_types] = list.map { |type| type.to_sym }
|
||||
options[:provision_enabled] = true
|
||||
options[:provision_ignore_sentinel] = true
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -11,9 +11,9 @@ require 'net/scp'
|
|||
|
||||
require 'vagrant/util/ansi_escape_code_remover'
|
||||
require 'vagrant/util/file_mode'
|
||||
require 'vagrant/util/keypair'
|
||||
require 'vagrant/util/platform'
|
||||
require 'vagrant/util/retryable'
|
||||
require 'vagrant/util/ssh'
|
||||
|
||||
module VagrantPlugins
|
||||
module CommunicatorSSH
|
||||
|
@ -85,6 +85,12 @@ module VagrantPlugins
|
|||
raise
|
||||
rescue Vagrant::Errors::SSHKeyTypeNotSupported
|
||||
raise
|
||||
rescue Vagrant::Errors::SSHKeyBadOwner
|
||||
raise
|
||||
rescue Vagrant::Errors::SSHKeyBadPermissions
|
||||
raise
|
||||
rescue Vagrant::Errors::SSHInsertKeyUnsupported
|
||||
raise
|
||||
rescue Vagrant::Errors::VagrantError => e
|
||||
# Ignore it, SSH is not ready, some other error.
|
||||
end
|
||||
|
@ -139,20 +145,41 @@ module VagrantPlugins
|
|||
|
||||
# If we used a password, then insert the insecure key
|
||||
ssh_info = @machine.ssh_info
|
||||
if ssh_info[:password] && ssh_info[:private_key_path].empty?
|
||||
@logger.info("Inserting insecure key to avoid password")
|
||||
@machine.ui.info(I18n.t("vagrant.inserting_insecure_key"))
|
||||
@machine.guest.capability(
|
||||
:insert_public_key,
|
||||
Vagrant.source_root.join("keys", "vagrant.pub").read.chomp)
|
||||
insert = ssh_info[:password] && ssh_info[:private_key_path].empty?
|
||||
ssh_info[:private_key_path].each do |pk|
|
||||
if insecure_key?(pk)
|
||||
insert = true
|
||||
@machine.ui.detail("\n"+I18n.t("vagrant.inserting_insecure_detected"))
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if insert
|
||||
# If we don't have the power to insert/remove keys, then its an error
|
||||
cap = @machine.guest.capability?(:insert_public_key) &&
|
||||
@machine.guest.capability?(:remove_public_key)
|
||||
raise Vagrant::Errors::SSHInsertKeyUnsupported if !cap
|
||||
|
||||
_pub, priv, openssh = Vagrant::Util::Keypair.create
|
||||
|
||||
@logger.info("Inserting key to avoid password: #{openssh}")
|
||||
@machine.ui.detail("\n"+I18n.t("vagrant.inserting_random_key"))
|
||||
@machine.guest.capability(:insert_public_key, openssh)
|
||||
|
||||
# Write out the private key in the data dir so that the
|
||||
# machine automatically picks it up.
|
||||
@machine.data_dir.join("private_key").open("w+") do |f|
|
||||
f.write(Vagrant.source_root.join("keys", "vagrant").read)
|
||||
f.write(priv)
|
||||
end
|
||||
|
||||
@machine.ui.info(I18n.t("vagrant.inserted_key"))
|
||||
# Remove the old key if it exists
|
||||
@machine.ui.detail(I18n.t("vagrant.inserting_remove_key"))
|
||||
@machine.guest.capability(
|
||||
:remove_public_key,
|
||||
Vagrant.source_root.join("keys", "vagrant.pub").read.chomp)
|
||||
|
||||
# Done, restart.
|
||||
@machine.ui.detail(I18n.t("vagrant.inserted_key"))
|
||||
@connection.close
|
||||
@connection = nil
|
||||
|
||||
|
@ -265,8 +292,13 @@ module VagrantPlugins
|
|||
# There is a chance that the socket is closed despite us checking
|
||||
# 'closed?' above. To test this we need to send data through the
|
||||
# socket.
|
||||
#
|
||||
# We wrap the check itself in a 5 second timeout because there
|
||||
# are some cases where this will just hang.
|
||||
begin
|
||||
Timeout.timeout(5) do
|
||||
@connection.exec!("")
|
||||
end
|
||||
rescue Exception => e
|
||||
@logger.info("Connection errored, not re-using. Will reconnect.")
|
||||
@logger.debug(e.inspect)
|
||||
|
@ -288,7 +320,7 @@ module VagrantPlugins
|
|||
raise Vagrant::Errors::SSHNotReady if ssh_info.nil?
|
||||
|
||||
# Default some options
|
||||
opts[:retries] = 5 if !opts.has_key?(:retries)
|
||||
opts[:retries] = 5 if !opts.key?(:retries)
|
||||
|
||||
# Build the options we'll use to initiate the connection via Net::SSH
|
||||
common_connect_opts = {
|
||||
|
@ -305,11 +337,6 @@ module VagrantPlugins
|
|||
verbose: :debug,
|
||||
}
|
||||
|
||||
# Check that the private key permissions are valid
|
||||
ssh_info[:private_key_path].each do |path|
|
||||
Vagrant::Util::SSH.check_key_permissions(Pathname.new(path))
|
||||
end
|
||||
|
||||
# Connect to SSH, giving it a few tries
|
||||
connection = nil
|
||||
begin
|
||||
|
@ -594,6 +621,16 @@ module VagrantPlugins
|
|||
# Otherwise, just raise the error up
|
||||
raise
|
||||
end
|
||||
|
||||
# This will test whether path is the Vagrant insecure private key.
|
||||
#
|
||||
# @param [String] path
|
||||
def insecure_key?(path)
|
||||
return false if !path
|
||||
return false if !File.file?(path)
|
||||
source_path = Vagrant.source_root.join("keys", "vagrant")
|
||||
return File.read(path).chomp == source_path.read.chomp
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,22 +25,89 @@ module VagrantPlugins
|
|||
@logger.info("Initializing WinRMCommunicator")
|
||||
end
|
||||
|
||||
def wait_for_ready(timeout)
|
||||
Timeout.timeout(timeout) do
|
||||
# Wait for winrm_info to be ready
|
||||
winrm_info = nil
|
||||
while true
|
||||
winrm_info = Helper.winrm_info(@machine)
|
||||
break if winrm_info
|
||||
sleep 0.5
|
||||
end
|
||||
|
||||
# Got it! Let the user know what we're connecting to.
|
||||
@machine.ui.detail("WinRM address: #{shell.host}:#{shell.port}")
|
||||
@machine.ui.detail("WinRM username: #{shell.username}")
|
||||
@machine.ui.detail("WinRM transport: #{shell.config.transport}")
|
||||
|
||||
last_message = nil
|
||||
last_message_repeat_at = 0
|
||||
while true
|
||||
message = nil
|
||||
begin
|
||||
begin
|
||||
return true if ready?
|
||||
rescue Vagrant::Errors::VagrantError => e
|
||||
@logger.info("WinRM not ready: #{e.inspect}")
|
||||
raise
|
||||
end
|
||||
rescue Errors::ConnectionTimeout
|
||||
message = "Connection timeout."
|
||||
rescue Errors::AuthenticationFailed
|
||||
message = "Authentication failure."
|
||||
rescue Errors::Disconnected
|
||||
message = "Remote connection disconnect."
|
||||
rescue Errors::ConnectionRefused
|
||||
message = "Connection refused."
|
||||
rescue Errors::ConnectionReset
|
||||
message = "Connection reset."
|
||||
rescue Errors::HostDown
|
||||
message = "Host appears down."
|
||||
rescue Errors::NoRoute
|
||||
message = "Host unreachable."
|
||||
rescue Errors::TransientError => e
|
||||
# Any other retriable errors
|
||||
message = e.message
|
||||
end
|
||||
|
||||
# If we have a message to show, then show it. We don't show
|
||||
# repeated messages unless they've been repeating longer than
|
||||
# 10 seconds.
|
||||
if message
|
||||
message_at = Time.now.to_f
|
||||
show_message = true
|
||||
if last_message == message
|
||||
show_message = (message_at - last_message_repeat_at) > 10.0
|
||||
end
|
||||
|
||||
if show_message
|
||||
@machine.ui.detail("Warning: #{message} Retrying...")
|
||||
last_message = message
|
||||
last_message_repeat_at = message_at
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue Timeout::Error
|
||||
return false
|
||||
end
|
||||
|
||||
def ready?
|
||||
@logger.info("Checking whether WinRM is ready...")
|
||||
|
||||
Timeout.timeout(@machine.config.winrm.timeout) do
|
||||
result = Timeout.timeout(@machine.config.winrm.timeout) do
|
||||
shell(true).powershell("hostname")
|
||||
end
|
||||
|
||||
@logger.info("WinRM is ready!")
|
||||
return true
|
||||
rescue Vagrant::Errors::VagrantError => e
|
||||
# We catch a `VagrantError` which would signal that something went
|
||||
# wrong expectedly in the `connect`, which means we didn't connect.
|
||||
rescue Errors::TransientError => e
|
||||
# We catch a `TransientError` which would signal that something went
|
||||
# that might work if we wait and retry.
|
||||
@logger.info("WinRM not up: #{e.inspect}")
|
||||
|
||||
# We reset the shell to trigger calling of winrm_finder again.
|
||||
# This resolves a problem when using vSphere where the ssh_info was not refreshing
|
||||
# This resolves a problem when using vSphere where the winrm_info was not refreshing
|
||||
# thus never getting the correct hostname.
|
||||
@shell = nil
|
||||
return false
|
||||
|
@ -79,12 +146,21 @@ module VagrantPlugins
|
|||
alias_method :sudo, :execute
|
||||
|
||||
def test(command, opts=nil)
|
||||
# If this is a *nix command with no Windows equivilant, assume failure
|
||||
# If this is a *nix command (which we know about) with no Windows
|
||||
# equivilant, assume failure
|
||||
command = @cmd_filter.filter(command)
|
||||
return false if command.empty?
|
||||
|
||||
opts = { error_check: false }.merge(opts || {})
|
||||
execute(command, opts) == 0
|
||||
opts = {
|
||||
command: command,
|
||||
elevated: false,
|
||||
error_check: false,
|
||||
}.merge(opts || {})
|
||||
|
||||
# If we're passed a *nix command which PS can't parse we get exit code
|
||||
# 0, but output in stderr. We need to check both exit code and stderr.
|
||||
output = shell.send(:powershell, command)
|
||||
return output[:exitcode] == 0 && flatten_stderr(output).length == 0
|
||||
end
|
||||
|
||||
def upload(from, to)
|
||||
|
@ -106,11 +182,8 @@ module VagrantPlugins
|
|||
|
||||
WinRMShell.new(
|
||||
winrm_info[:host],
|
||||
@machine.config.winrm.username,
|
||||
@machine.config.winrm.password,
|
||||
port: winrm_info[:port],
|
||||
timeout_in_seconds: @machine.config.winrm.timeout,
|
||||
max_tries: @machine.config.winrm.max_tries,
|
||||
winrm_info[:port],
|
||||
@machine.config.winrm
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -156,8 +229,8 @@ module VagrantPlugins
|
|||
def raise_execution_error(output, opts)
|
||||
# WinRM can return multiple stderr and stdout entries
|
||||
error_opts = opts.merge(
|
||||
stdout: output[:data].collect { |e| e[:stdout] }.join,
|
||||
stderr: output[:data].collect { |e| e[:stderr] }.join
|
||||
stdout: flatten_stdout(output),
|
||||
stderr: flatten_stderr(output)
|
||||
)
|
||||
|
||||
# Use a different error message key if the caller gave us one,
|
||||
|
@ -167,6 +240,20 @@ module VagrantPlugins
|
|||
# Raise the error, use the type the caller gave us or the comm default
|
||||
raise opts[:error_class], error_opts
|
||||
end
|
||||
|
||||
|
||||
# TODO: Replace with WinRM Output class when WinRM 1.3 is released
|
||||
def flatten_stderr(output)
|
||||
output[:data].map do | line |
|
||||
line[:stderr]
|
||||
end.compact.join
|
||||
end
|
||||
|
||||
def flatten_stdout(output)
|
||||
output[:data].map do | line |
|
||||
line[:flatten_stdout]
|
||||
end.compact.join
|
||||
end
|
||||
end #WinRM class
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,7 +7,10 @@ module VagrantPlugins
|
|||
attr_accessor :port
|
||||
attr_accessor :guest_port
|
||||
attr_accessor :max_tries
|
||||
attr_accessor :retry_delay
|
||||
attr_accessor :timeout
|
||||
attr_accessor :transport
|
||||
attr_accessor :ssl_peer_verification
|
||||
|
||||
def initialize
|
||||
@username = UNSET_VALUE
|
||||
|
@ -16,17 +19,24 @@ module VagrantPlugins
|
|||
@port = UNSET_VALUE
|
||||
@guest_port = UNSET_VALUE
|
||||
@max_tries = UNSET_VALUE
|
||||
@retry_delay = UNSET_VALUE
|
||||
@timeout = UNSET_VALUE
|
||||
@transport = UNSET_VALUE
|
||||
@ssl_peer_verification = UNSET_VALUE
|
||||
end
|
||||
|
||||
def finalize!
|
||||
@username = "vagrant" if @username == UNSET_VALUE
|
||||
@password = "vagrant" if @password == UNSET_VALUE
|
||||
@transport = :plaintext if @transport == UNSET_VALUE
|
||||
@host = nil if @host == UNSET_VALUE
|
||||
@port = 5985 if @port == UNSET_VALUE
|
||||
@guest_port = 5985 if @guest_port == UNSET_VALUE
|
||||
is_ssl = @transport == :ssl
|
||||
@port = (is_ssl ? 5986 : 5985) if @port == UNSET_VALUE
|
||||
@guest_port = (is_ssl ? 5986 : 5985) if @guest_port == UNSET_VALUE
|
||||
@max_tries = 20 if @max_tries == UNSET_VALUE
|
||||
@retry_delay = 2 if @retry_delay == UNSET_VALUE
|
||||
@timeout = 1800 if @timeout == UNSET_VALUE
|
||||
@ssl_peer_verification = true if @ssl_peer_verification == UNSET_VALUE
|
||||
end
|
||||
|
||||
def validate(machine)
|
||||
|
@ -37,7 +47,11 @@ module VagrantPlugins
|
|||
errors << "winrm.port cannot be nil." if @port.nil?
|
||||
errors << "winrm.guest_port cannot be nil." if @guest_port.nil?
|
||||
errors << "winrm.max_tries cannot be nil." if @max_tries.nil?
|
||||
errors << "winrm.retry_delay cannot be nil." if @max_tries.nil?
|
||||
errors << "winrm.timeout cannot be nil." if @timeout.nil?
|
||||
unless @ssl_peer_verification == true || @ssl_peer_verification == false
|
||||
errors << "winrm.ssl_peer_verification must be a boolean."
|
||||
end
|
||||
|
||||
{ "WinRM" => errors }
|
||||
end
|
||||
|
|
|
@ -6,8 +6,11 @@ module VagrantPlugins
|
|||
error_namespace("vagrant_winrm.errors")
|
||||
end
|
||||
|
||||
class AuthError < WinRMError
|
||||
error_key(:auth_error)
|
||||
class TransientError < WinRMError
|
||||
end
|
||||
|
||||
class AuthenticationFailed < WinRMError
|
||||
error_key(:authentication_failed)
|
||||
end
|
||||
|
||||
class ExecutionError < WinRMError
|
||||
|
@ -29,6 +32,38 @@ module VagrantPlugins
|
|||
class WinRMFileTransferError < WinRMError
|
||||
error_key(:winrm_file_transfer_error)
|
||||
end
|
||||
|
||||
class InvalidTransport < WinRMError
|
||||
error_key(:invalid_transport)
|
||||
end
|
||||
|
||||
class SSLError < WinRMError
|
||||
error_key(:ssl_error)
|
||||
end
|
||||
|
||||
class ConnectionTimeout < TransientError
|
||||
error_key(:connection_timeout)
|
||||
end
|
||||
|
||||
class Disconnected < TransientError
|
||||
error_key(:disconnected)
|
||||
end
|
||||
|
||||
class ConnectionRefused < TransientError
|
||||
error_key(:connection_refused)
|
||||
end
|
||||
|
||||
class ConnectionReset < TransientError
|
||||
error_key(:connection_reset)
|
||||
end
|
||||
|
||||
class HostDown < TransientError
|
||||
error_key(:host_down)
|
||||
end
|
||||
|
||||
class NoRoute < TransientError
|
||||
error_key(:no_route)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,163 +0,0 @@
|
|||
require "log4r"
|
||||
|
||||
module VagrantPlugins
|
||||
module CommunicatorWinRM
|
||||
# Manages the file system on the remote guest allowing for file tranfer
|
||||
# between the guest and host.
|
||||
class FileManager
|
||||
def initialize(shell)
|
||||
@logger = Log4r::Logger.new("vagrant::communication::filemanager")
|
||||
@shell = shell
|
||||
end
|
||||
|
||||
# Uploads the given file or directory from the host to the guest (recursively).
|
||||
#
|
||||
# @param [String] The source file or directory path on the host
|
||||
# @param [String] The destination file or directory path on the host
|
||||
def upload(host_src_file_path, guest_dest_file_path)
|
||||
@logger.debug("Upload: #{host_src_file_path} -> #{guest_dest_file_path}")
|
||||
if File.directory?(host_src_file_path)
|
||||
upload_directory(host_src_file_path, guest_dest_file_path)
|
||||
else
|
||||
upload_file(host_src_file_path, guest_dest_file_path)
|
||||
end
|
||||
end
|
||||
|
||||
# Downloads the given file from the guest to the host.
|
||||
# NOTE: This currently only supports single file download
|
||||
#
|
||||
# @param [String] The source file path on the guest
|
||||
# @param [String] The destination file path on the host
|
||||
def download(guest_src_file_path, host_dest_file_path)
|
||||
@logger.debug("#{guest_src_file_path} -> #{host_dest_file_path}")
|
||||
|
||||
output = @shell.powershell("[System.convert]::ToBase64String([System.IO.File]::ReadAllBytes(\"#{guest_src_file_path}\"))")
|
||||
contents = output[:data].map!{|line| line[:stdout]}.join.gsub("\\n\\r", '')
|
||||
out = Base64.decode64(contents)
|
||||
IO.binwrite(host_dest_file_path, out)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Recursively uploads the given directory from the host to the guest
|
||||
#
|
||||
# @param [String] The source file or directory path on the host
|
||||
# @param [String] The destination file or directory path on the host
|
||||
def upload_directory(host_src_file_path, guest_dest_file_path)
|
||||
glob_patt = File.join(host_src_file_path, '**/*')
|
||||
Dir.glob(glob_patt).select { |f| !File.directory?(f) }.each do |host_file_path|
|
||||
guest_file_path = guest_file_path(host_src_file_path, guest_dest_file_path, host_file_path)
|
||||
upload_file(host_file_path, guest_file_path)
|
||||
end
|
||||
end
|
||||
|
||||
# Uploads the given file, but only if the target file doesn't exist
|
||||
# or its MD5 checksum doens't match the host's source checksum.
|
||||
#
|
||||
# @param [String] The source file path on the host
|
||||
# @param [String] The destination file path on the guest
|
||||
def upload_file(host_src_file_path, guest_dest_file_path)
|
||||
if should_upload_file?(host_src_file_path, guest_dest_file_path)
|
||||
tmp_file_path = upload_to_temp_file(host_src_file_path)
|
||||
decode_temp_file(tmp_file_path, guest_dest_file_path)
|
||||
else
|
||||
@logger.debug("Up to date: #{guest_dest_file_path}")
|
||||
end
|
||||
end
|
||||
|
||||
# Uploads the given file to a new temp file on the guest
|
||||
#
|
||||
# @param [String] The source file path on the host
|
||||
# @return [String] The temp file path on the guest
|
||||
def upload_to_temp_file(host_src_file_path)
|
||||
tmp_file_path = File.join(guest_temp_dir, "winrm-upload-#{rand()}")
|
||||
@logger.debug("Uploading '#{host_src_file_path}' to temp file '#{tmp_file_path}'")
|
||||
|
||||
base64_host_file = Base64.encode64(IO.binread(host_src_file_path)).gsub("\n",'')
|
||||
base64_host_file.chars.to_a.each_slice(8000-tmp_file_path.size) do |chunk|
|
||||
out = @shell.cmd("echo #{chunk.join} >> \"#{tmp_file_path}\"")
|
||||
raise_upload_error_if_failed(out, host_src_file_path, tmp_file_path)
|
||||
end
|
||||
|
||||
tmp_file_path
|
||||
end
|
||||
|
||||
# Moves and decodes the given file temp file on the guest to its
|
||||
# permanent location
|
||||
#
|
||||
# @param [String] The source base64 encoded temp file path on the guest
|
||||
# @param [String] The destination file path on the guest
|
||||
def decode_temp_file(guest_tmp_file_path, guest_dest_file_path)
|
||||
@logger.debug("Decoding temp file '#{guest_tmp_file_path}' to '#{guest_dest_file_path}'")
|
||||
out = @shell.powershell <<-EOH
|
||||
$tmp_file_path = [System.IO.Path]::GetFullPath('#{guest_tmp_file_path}')
|
||||
$dest_file_path = [System.IO.Path]::GetFullPath('#{guest_dest_file_path}')
|
||||
|
||||
if (Test-Path $dest_file_path) {
|
||||
rm $dest_file_path
|
||||
}
|
||||
else {
|
||||
$dest_dir = ([System.IO.Path]::GetDirectoryName($dest_file_path))
|
||||
New-Item -ItemType directory -Force -Path $dest_dir
|
||||
}
|
||||
|
||||
$base64_string = Get-Content $tmp_file_path
|
||||
$bytes = [System.Convert]::FromBase64String($base64_string)
|
||||
[System.IO.File]::WriteAllBytes($dest_file_path, $bytes)
|
||||
EOH
|
||||
raise_upload_error_if_failed(out, guest_tmp_file_path, guest_dest_file_path)
|
||||
end
|
||||
|
||||
# Checks to see if the target file on the guest is missing or out of date.
|
||||
#
|
||||
# @param [String] The source file path on the host
|
||||
# @param [String] The destination file path on the guest
|
||||
# @return [Boolean] True if the file is missing or out of date
|
||||
def should_upload_file?(host_src_file_path, guest_dest_file_path)
|
||||
local_md5 = Digest::MD5.file(host_src_file_path).hexdigest
|
||||
cmd = <<-EOH
|
||||
$dest_file_path = [System.IO.Path]::GetFullPath('#{guest_dest_file_path}')
|
||||
|
||||
if (Test-Path $dest_file_path) {
|
||||
$crypto_provider = new-object -TypeName System.Security.Cryptography.MD5CryptoServiceProvider
|
||||
try {
|
||||
$file = [System.IO.File]::Open($dest_file_path, [System.IO.Filemode]::Open, [System.IO.FileAccess]::Read)
|
||||
$guest_md5 = ([System.BitConverter]::ToString($crypto_provider.ComputeHash($file))).Replace("-","").ToLower()
|
||||
}
|
||||
finally {
|
||||
$file.Dispose()
|
||||
}
|
||||
if ($guest_md5 -eq '#{local_md5}') {
|
||||
exit 0
|
||||
}
|
||||
}
|
||||
Write-Host "should upload file $dest_file_path"
|
||||
exit 1
|
||||
EOH
|
||||
@shell.powershell(cmd)[:exitcode] == 1
|
||||
end
|
||||
|
||||
# Creates a guest file path equivalent from a host file path
|
||||
#
|
||||
# @param [String] The base host directory we're going to copy from
|
||||
# @param [String] The base guest directory we're going to copy to
|
||||
# @param [String] A full path to a file on the host underneath host_base_dir
|
||||
# @return [String] The guest file path equivalent
|
||||
def guest_file_path(host_base_dir, guest_base_dir, host_file_path)
|
||||
rel_path = File.dirname(host_file_path[host_base_dir.length, host_file_path.length])
|
||||
File.join(guest_base_dir, rel_path, File.basename(host_file_path))
|
||||
end
|
||||
|
||||
def guest_temp_dir
|
||||
@guest_temp ||= (@shell.cmd('echo %TEMP%'))[:data][0][:stdout].chomp
|
||||
end
|
||||
|
||||
def raise_upload_error_if_failed(out, from, to)
|
||||
raise Errors::WinRMFileTransferError,
|
||||
from: from,
|
||||
to: to,
|
||||
message: out.inspect if out[:exitcode] != 0
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -27,7 +27,7 @@ $task_xml = @'
|
|||
<StartWhenAvailable>false</StartWhenAvailable>
|
||||
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
|
||||
<IdleSettings>
|
||||
<StopOnIdleEnd>true</StopOnIdleEnd>
|
||||
<StopOnIdleEnd>false</StopOnIdleEnd>
|
||||
<RestartOnIdle>false</RestartOnIdle>
|
||||
</IdleSettings>
|
||||
<AllowStartOnDemand>true</AllowStartOnDemand>
|
||||
|
|
|
@ -9,7 +9,7 @@ Vagrant::Util::SilenceWarnings.silence! do
|
|||
require "winrm"
|
||||
end
|
||||
|
||||
require_relative "file_manager"
|
||||
require "winrm-fs/file_manager"
|
||||
|
||||
module VagrantPlugins
|
||||
module CommunicatorWinRM
|
||||
|
@ -22,6 +22,7 @@ module VagrantPlugins
|
|||
@@exceptions_to_retry_on = [
|
||||
HTTPClient::KeepAliveDisconnected,
|
||||
WinRM::WinRMHTTPTransportError,
|
||||
WinRM::WinRMAuthorizationError,
|
||||
Errno::EACCES,
|
||||
Errno::EADDRINUSE,
|
||||
Errno::ECONNREFUSED,
|
||||
|
@ -32,23 +33,21 @@ module VagrantPlugins
|
|||
]
|
||||
|
||||
attr_reader :logger
|
||||
attr_reader :username
|
||||
attr_reader :password
|
||||
attr_reader :host
|
||||
attr_reader :port
|
||||
attr_reader :timeout_in_seconds
|
||||
attr_reader :max_tries
|
||||
attr_reader :username
|
||||
attr_reader :password
|
||||
attr_reader :config
|
||||
|
||||
def initialize(host, username, password, options = {})
|
||||
def initialize(host, port, config)
|
||||
@logger = Log4r::Logger.new("vagrant::communication::winrmshell")
|
||||
@logger.debug("initializing WinRMShell")
|
||||
|
||||
@host = host
|
||||
@port = options[:port] || 5985
|
||||
@username = username
|
||||
@password = password
|
||||
@timeout_in_seconds = options[:timeout_in_seconds] || 60
|
||||
@max_tries = options[:max_tries] || 20
|
||||
@port = port
|
||||
@username = config.username
|
||||
@password = config.password
|
||||
@config = config
|
||||
end
|
||||
|
||||
def powershell(command, &block)
|
||||
|
@ -67,11 +66,13 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def upload(from, to)
|
||||
FileManager.new(self).upload(from, to)
|
||||
file_manager = WinRM::FS::FileManager.new(session)
|
||||
file_manager.upload(from, to)
|
||||
end
|
||||
|
||||
def download(from, to)
|
||||
FileManager.new(self).download(from, to)
|
||||
file_manager = WinRM::FS::FileManager.new(session)
|
||||
file_manager.download(from, to)
|
||||
end
|
||||
|
||||
protected
|
||||
|
@ -87,41 +88,80 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def execute_shell_with_retry(command, shell, &block)
|
||||
retryable(tries: @max_tries, on: @@exceptions_to_retry_on, sleep: 10) do
|
||||
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
|
||||
|
||||
@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
|
||||
end
|
||||
end
|
||||
|
||||
return output
|
||||
end
|
||||
end
|
||||
|
||||
def raise_winrm_exception(winrm_exception, shell, command)
|
||||
# If the error is a 401, we can return a more specific error message
|
||||
if winrm_exception.message.include?("401")
|
||||
raise Errors::AuthError,
|
||||
user: @username,
|
||||
password: @password,
|
||||
def raise_winrm_exception(exception, shell = nil, command = nil)
|
||||
case exception
|
||||
when WinRM::WinRMAuthorizationError
|
||||
raise Errors::AuthenticationFailed,
|
||||
user: @config.username,
|
||||
password: @config.password,
|
||||
endpoint: endpoint,
|
||||
message: winrm_exception.message
|
||||
end
|
||||
|
||||
message: exception.message
|
||||
when WinRM::WinRMHTTPTransportError
|
||||
raise Errors::ExecutionError,
|
||||
shell: shell,
|
||||
command: command,
|
||||
message: winrm_exception.message
|
||||
message: exception.message
|
||||
when OpenSSL::SSL::SSLError
|
||||
raise Errors::SSLError, message: exception.message
|
||||
when HTTPClient::TimeoutError
|
||||
raise Errors::ConnectionTimeout, message: exception.message
|
||||
when Errno::ECONNREFUSED
|
||||
# This is raised if we failed to connect the max amount of times
|
||||
raise Errors::ConnectionRefused
|
||||
when Errno::ECONNRESET
|
||||
# This is raised if we failed to connect the max number of times
|
||||
# due to an ECONNRESET.
|
||||
raise Errors::ConnectionReset
|
||||
when Errno::EHOSTDOWN
|
||||
# This is raised if we get an ICMP DestinationUnknown error.
|
||||
raise Errors::HostDown
|
||||
when Errno::EHOSTUNREACH
|
||||
# This is raised if we can't work out how to route traffic.
|
||||
raise Errors::NoRoute
|
||||
else
|
||||
raise Errors::ExecutionError,
|
||||
shell: shell,
|
||||
command: command,
|
||||
message: exception.message
|
||||
end
|
||||
end
|
||||
|
||||
def new_session
|
||||
@logger.info("Attempting to connect to WinRM...")
|
||||
@logger.info(" - Host: #{@host}")
|
||||
@logger.info(" - Port: #{@port}")
|
||||
@logger.info(" - Username: #{@username}")
|
||||
@logger.info(" - Username: #{@config.username}")
|
||||
@logger.info(" - Transport: #{@config.transport}")
|
||||
|
||||
client = ::WinRM::WinRMWebService.new(endpoint, :plaintext, endpoint_options)
|
||||
client.set_timeout(@timeout_in_seconds)
|
||||
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
|
||||
end
|
||||
|
@ -131,7 +171,14 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def endpoint
|
||||
case @config.transport.to_sym
|
||||
when :ssl
|
||||
"https://#{@host}:#{@port}/wsman"
|
||||
when :plaintext
|
||||
"http://#{@host}:#{@port}/wsman"
|
||||
else
|
||||
raise Errors::WinRMInvalidTransport, transport: @config.transport
|
||||
end
|
||||
end
|
||||
|
||||
def endpoint_options
|
||||
|
@ -139,8 +186,8 @@ module VagrantPlugins
|
|||
pass: @password,
|
||||
host: @host,
|
||||
port: @port,
|
||||
operation_timeout: @timeout_in_seconds,
|
||||
basic_auth_only: true }
|
||||
basic_auth_only: true,
|
||||
no_ssl_peer_verification: !@config.ssl_peer_verification }
|
||||
end
|
||||
end #WinShell class
|
||||
end
|
||||
|
|
|
@ -10,9 +10,15 @@ 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
|
||||
|
||||
networks.each do |network|
|
||||
entry = TemplateRenderer.render("guests/arch/network_#{network[:type]}",
|
||||
options: network)
|
||||
network[:device] = interfaces[network[:interface]]
|
||||
|
||||
entry = TemplateRenderer.render("guests/arch/network_#{network[:type]}", options: network)
|
||||
|
||||
temp = Tempfile.new("vagrant")
|
||||
temp.binmode
|
||||
|
@ -20,26 +26,8 @@ module VagrantPlugins
|
|||
temp.close
|
||||
|
||||
machine.communicate.upload(temp.path, "/tmp/vagrant_network")
|
||||
machine.communicate.sudo("ln -sf /dev/null /etc/udev/rules.d/80-net-name-slot.rules")
|
||||
machine.communicate.sudo("udevadm control --reload")
|
||||
machine.communicate.sudo("mv /tmp/vagrant_network /etc/netctl/eth#{network[:interface]}")
|
||||
|
||||
# Only consider nth line of sed's output below. There's always an
|
||||
# offset of two lines in the below sed command given the current
|
||||
# interface number -> 1: lo, 2: nat device,
|
||||
snth = network[:interface] + 2
|
||||
|
||||
# A hack not to rely on udev rule 80-net-name-slot.rules masking
|
||||
# (ln -sf /dev/null /etc/udev/80-net-name-slot.rules).
|
||||
# I assume this to be the most portable solution because
|
||||
# otherwise we would need to rely on the Virtual Machine implementation
|
||||
# to provide details on the configured interfaces, e.g mac address
|
||||
# to write a custom udev rule. Templating the netcfg files and
|
||||
# replacing the correct interface name within ruby seems more
|
||||
# complicted too (I'm far from being a ruby expert though).
|
||||
machine.communicate.sudo("sed -i \"s/eth#{network[:interface]}/`ip link | sed -n 's/.*:\\s\\(.*\\): <.*/\\1/p' | sed -n #{snth}p`/g\" /etc/netctl/eth#{network[:interface]}")
|
||||
machine.communicate.sudo("ip link set eth#{network[:interface]} down")
|
||||
machine.communicate.sudo("netctl start eth#{network[:interface]}")
|
||||
machine.communicate.sudo("mv /tmp/vagrant_network /etc/netctl/#{network[:device]}")
|
||||
machine.communicate.sudo("ip link set #{network[:device]} down && netctl start #{network[:device]}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,7 +4,9 @@ module VagrantPlugins
|
|||
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}")
|
||||
machine.communicate.sudo("scutil --set LocalHostName #{name}")
|
||||
machine.communicate.sudo("hostname #{name}")
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
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
|
|
@ -0,0 +1,21 @@
|
|||
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
|
|
@ -17,7 +17,7 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def self.rsync_post(machine, opts)
|
||||
if opts.has_key?(:chown) && !opts[:chown]
|
||||
if opts.key?(:chown) && !opts[:chown]
|
||||
return
|
||||
end
|
||||
|
||||
|
|
|
@ -26,6 +26,11 @@ module VagrantPlugins
|
|||
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
|
||||
|
@ -36,6 +41,11 @@ module VagrantPlugins
|
|||
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
|
||||
require_relative "cap/rsync"
|
||||
Cap::RSync
|
||||
|
|
|
@ -13,9 +13,8 @@ module VagrantPlugins
|
|||
machine.communicate.tap do |comm|
|
||||
# First, remove any previous network modifications
|
||||
# from the interface file.
|
||||
comm.sudo("sed -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' /etc/network/interfaces > /tmp/vagrant-network-interfaces")
|
||||
comm.sudo("su -c 'cat /tmp/vagrant-network-interfaces > /etc/network/interfaces'")
|
||||
comm.sudo("rm -f /tmp/vagrant-network-interfaces")
|
||||
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 | tail -n +2 > /tmp/vagrant-network-interfaces.post")
|
||||
|
||||
# Accumulate the configurations to add to the interfaces file as
|
||||
# well as what interfaces we're actually configuring since we use that
|
||||
|
@ -47,8 +46,8 @@ module VagrantPlugins
|
|||
comm.sudo("/sbin/ip addr flush dev eth#{interface} 2> /dev/null")
|
||||
end
|
||||
|
||||
comm.sudo("cat /tmp/vagrant-network-entry >> /etc/network/interfaces")
|
||||
comm.sudo("rm -f /tmp/vagrant-network-entry")
|
||||
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|
|
||||
|
|
|
@ -2,7 +2,7 @@ module VagrantPlugins
|
|||
module GuestDebian
|
||||
class Guest < Vagrant.plugin("2", :guest)
|
||||
def detect?(machine)
|
||||
machine.communicate.test("cat /etc/issue | grep 'Debian'")
|
||||
machine.communicate.test("cat /etc/issue | grep 'Debian' | grep -v '8'")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
module VagrantPlugins
|
||||
module GuestDebian8
|
||||
module Cap
|
||||
class Halt
|
||||
def self.halt(machine)
|
||||
begin
|
||||
machine.communicate.sudo("shutdown -h -H")
|
||||
rescue IOError
|
||||
# Do nothing, because it probably means the machine shut down
|
||||
# and SSH connection was lost.
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,9 @@
|
|||
module VagrantPlugins
|
||||
module GuestDebian8
|
||||
class Guest < Vagrant.plugin("2", :guest)
|
||||
def detect?(machine)
|
||||
machine.communicate.test("cat /etc/issue | grep 'Debian' | grep '8'")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,21 @@
|
|||
require "vagrant"
|
||||
|
||||
module VagrantPlugins
|
||||
module GuestDebian8
|
||||
class Plugin < Vagrant.plugin("2")
|
||||
name "Debian Jessie guest"
|
||||
description "Debian Jessie guest support."
|
||||
|
||||
guest("debian8", "debian") do
|
||||
require File.expand_path("../guest", __FILE__)
|
||||
Guest
|
||||
end
|
||||
|
||||
guest_capability("debian8", "halt") do
|
||||
require_relative "cap/halt"
|
||||
Cap::Halt
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -26,7 +26,7 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
interface_names = networks.map do |network|
|
||||
"eth#{network[:interface]}"
|
||||
"#{interface_names[network[:interface]]}"
|
||||
end
|
||||
else
|
||||
machine.communicate.sudo("/usr/sbin/biosdevname -d | grep Kernel | cut -f2 -d: | sed -e 's/ //;'") do |_, result|
|
||||
|
|
|
@ -4,7 +4,7 @@ module VagrantPlugins
|
|||
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
|
||||
# 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"
|
||||
|
|
|
@ -4,7 +4,7 @@ module VagrantPlugins
|
|||
module GuestFedora
|
||||
class Guest < Vagrant.plugin("2", :guest)
|
||||
def detect?(machine)
|
||||
machine.communicate.test("grep 'Fedora release [12][67890]' /etc/redhat-release")
|
||||
machine.communicate.test("grep 'Fedora release [12][678901]' /etc/redhat-release")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,8 +4,15 @@ module VagrantPlugins
|
|||
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 '#{ip}:#{opts[:hostpath]}' '#{opts[:guestpath]}'", {shell: "sh"})
|
||||
|
||||
machine.communicate.sudo(
|
||||
"mount -t nfs #{nfs_version_mount_option} " +
|
||||
"'#{ip}:#{opts[:hostpath]}' '#{opts[:guestpath]}'", {shell: "sh"})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
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 '/^.*#{contents}.*$/d' ~/.ssh/authorized_keys")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -31,7 +31,7 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def self.rsync_post(machine, opts)
|
||||
if opts.has_key?(:chown) && !opts[:chown]
|
||||
if opts.key?(:chown) && !opts[:chown]
|
||||
return
|
||||
end
|
||||
|
||||
|
|
|
@ -36,6 +36,11 @@ module VagrantPlugins
|
|||
Cap::MountNFSFolder
|
||||
end
|
||||
|
||||
guest_capability("freebsd", "remove_public_key") do
|
||||
require_relative "cap/remove_public_key"
|
||||
Cap::RemovePublicKey
|
||||
end
|
||||
|
||||
guest_capability("freebsd", "rsync_install") do
|
||||
require_relative "cap/rsync"
|
||||
Cap::RSync
|
||||
|
|
|
@ -28,7 +28,7 @@ module VagrantPlugins
|
|||
temp.binmode
|
||||
temp.write(entry)
|
||||
temp.close
|
||||
comm.upload(temp.path, "/tmp/vagrant-network-entry-#{ifFile}")
|
||||
comm.upload(temp.path, "/tmp/vagrant-#{ifFile}")
|
||||
comm.sudo("cp /tmp/vagrant-#{ifFile} /etc/conf.d/#{ifFile}")
|
||||
comm.sudo("chmod 0644 /etc/conf.d/#{ifFile}")
|
||||
comm.sudo("ln -fs /etc/init.d/netif.tmpl /etc/init.d/#{ifFile}")
|
||||
|
|
|
@ -51,10 +51,15 @@ module VagrantPlugins
|
|||
while true
|
||||
success = true
|
||||
|
||||
stderr = ""
|
||||
mount_commands.each do |command|
|
||||
no_such_device = false
|
||||
stderr = ""
|
||||
status = machine.communicate.sudo(command, error_check: false) do |type, data|
|
||||
no_such_device = true if type == :stderr && data =~ /No such device/i
|
||||
if type == :stderr
|
||||
no_such_device = true if data =~ /No such device/i
|
||||
stderr += data.to_s
|
||||
end
|
||||
end
|
||||
|
||||
success = status == 0 && !no_such_device
|
||||
|
@ -69,7 +74,8 @@ module VagrantPlugins
|
|||
command.gsub!(smb_password, "PASSWORDHIDDEN")
|
||||
|
||||
raise Vagrant::Errors::LinuxMountFailed,
|
||||
command: command
|
||||
command: command,
|
||||
output: stderr
|
||||
end
|
||||
|
||||
sleep 2
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
require "vagrant/util/shell_quote"
|
||||
|
||||
module VagrantPlugins
|
||||
module GuestLinux
|
||||
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
|
|
@ -17,7 +17,7 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def self.rsync_post(machine, opts)
|
||||
if opts.has_key?(:chown) && !opts[:chown]
|
||||
if opts.key?(:chown) && !opts[:chown]
|
||||
return
|
||||
end
|
||||
|
||||
|
|
|
@ -62,6 +62,11 @@ module VagrantPlugins
|
|||
Cap::ReadIPAddress
|
||||
end
|
||||
|
||||
guest_capability("linux", "remove_public_key") do
|
||||
require_relative "cap/remove_public_key"
|
||||
Cap::RemovePublicKey
|
||||
end
|
||||
|
||||
guest_capability("linux", "rsync_installed") do
|
||||
require_relative "cap/rsync"
|
||||
Cap::RSync
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue