Compare commits

..

1 Commits

Author SHA1 Message Date
Chris Roberts a69ff5054d Update generated ssh private key file permissions on create
This updates the permissions on the automatically generated private
key file to only be readable by the user. Includes support for file
permission modification on Windows platform.
2018-04-09 15:12:24 -07:00
722 changed files with 3928 additions and 33122 deletions

View File

@ -1,20 +0,0 @@
#!/usr/bin/env bash
csource="${BASH_SOURCE[0]}"
while [ -h "$csource" ] ; do csource="$(readlink "$csource")"; done
root="$( cd -P "$( dirname "$csource" )/../" && pwd )"
. "${root}/.ci/init.sh"
pushd "${root}" > "${output}"
# Build our gem
wrap gem build *.gemspec \
"Failed to build Vagrant RubyGem"
# Get the path of our new gem
g=(vagrant*.gem)
gem=$(printf "%s" "${g}")
wrap aws s3 cp "${gem}" "${ASSETS_PRIVATE_BUCKET}/${repository}/vagrant-master.gem" \
"Failed to store Vagrant RubyGem master build"

View File

@ -1,432 +0,0 @@
# last-modified: Tue Jan 14 20:37:58 UTC 2020
#!/usr/bin/env bash
# Path to file used for output redirect
# and extracting messages for warning and
# failure information sent to slack
function output_file() {
printf "/tmp/.ci-output"
}
# Write failure message, send error to configured
# slack, and exit with non-zero status. If an
# "$(output_file)" file exists, the last 5 lines will be
# included in the slack message.
#
# $1: Failure message
function fail() {
(>&2 echo "ERROR: ${1}")
if [ -f ""$(output_file)"" ]; then
slack -s error -m "ERROR: ${1}" -f "$(output_file)" -T 5
else
slack -s error -m "ERROR: ${1}"
fi
exit 1
}
# Write warning message, send warning to configured
# slack
#
# $1: Warning message
function warn() {
(>&2 echo "WARN: ${1}")
if [ -f ""$(output_file)"" ]; then
slack -s warn -m "WARNING: ${1}" -f "$(output_file)"
else
slack -s warn -m "WARNING: ${1}"
fi
}
# Execute command while redirecting all output to
# a file (file is used within fail mesage on when
# command is unsuccessful). Final argument is the
# error message used when the command fails.
#
# $@{1:$#-1}: Command to execute
# $@{$#}: Failure message
function wrap() {
i=$(("${#}" - 1))
wrap_raw "${@:1:$i}"
if [ $? -ne 0 ]; then
cat "$(output_file)"
fail "${@:$#}"
fi
rm "$(output_file)"
}
# Execute command while redirecting all output to
# a file. Exit status is returned.
function wrap_raw() {
rm -f "$(output_file)"
"${@}" > "$(output_file)" 2>&1
return $?
}
# Execute command while redirecting all output to
# a file (file is used within fail mesage on when
# command is unsuccessful). Command output will be
# streamed during execution. Final argument is the
# error message used when the command fails.
#
# $@{1:$#-1}: Command to execute
# $@{$#}: Failure message
function wrap_stream() {
i=$(("${#}" - 1))
wrap_stream_raw "${@:1:$i}"
if [ $? -ne 0 ]; then
fail "${@:$#}"
fi
rm "$(output_file)"
}
# Execute command while redirecting all output
# to a file. Command output will be streamed
# during execution. Exit status is returned
function wrap_stream_raw() {
rm -f "$(output_file)"
"${@}" > "$(output_file)" 2>&1 &
pid=$!
until [ -f "$(output_file)" ]; do
sleep 0.1
done
tail -f --quiet --pid "${pid}" "$(output_file)"
wait "${pid}"
return $?
}
# Send command to packet device and wrap
# execution
# $@{1:$#-1}: Command to execute
# $@{$#}: Failure message
function pkt_wrap() {
wrap packet-exec run -quiet -- "${@}"
}
# Send command to packet device and wrap
# execution
# $@: Command to execute
function pkt_wrap_raw() {
wrap_raw packet-exec run -quiet -- "${@}"
}
# Send command to packet device and wrap
# execution with output streaming
# $@{1:$#-1}: Command to execute
# $@{$#}: Failure message
function pkt_wrap_stream() {
wrap_stream packet-exec run -quiet -- "${@}"
}
# Send command to packet device and wrap
# execution with output streaming
# $@: Command to execute
function pkt_wrap_stream_raw() {
wrap_stream_raw packet-exec run -quiet -- "${@}"
}
# Generates location within the asset storage
# bucket to retain built assets.
function asset_location() {
if [ "${tag}" = "" ]; then
dst="${ASSETS_PRIVATE_LONGTERM}/${repository}/${ident_ref}/${short_sha}"
else
if [[ "${tag}" = *"+"* ]]; then
dst="${ASSETS_PRIVATE_LONGTERM}/${repository}/${tag}"
else
dst="${ASSETS_PRIVATE_BUCKET}/${repository}/${tag}"
fi
fi
echo -n "${dst}"
}
# Upload assets to the asset storage bucket.
#
# $1: Path to asset file or directory to upload
function upload_assets() {
if [ "${1}" = "" ]; then
fail "Parameter required for asset upload"
fi
if [ -d "${1}" ]; then
wrap aws s3 cp --recursive "${1}" "$(asset_location)/" \
"Upload to asset storage failed"
else
wrap aws s3 cp "${1}" "$(asset_location)/" \
"Upload to asset storage failed"
fi
}
# Download assets from the asset storage bucket. If
# destination is not provided, remote path will be
# used locally.
#
# $1: Path to asset or directory to download
# $2: Optional destination for downloaded assets
function download_assets() {
if [ "${1}" = "" ]; then
fail "At least one parameter required for asset download"
fi
if [ "${2}" = "" ]; then
dst="${1#/}"
else
dst="${2}"
fi
mkdir -p "${dst}"
src="$(asset_location)/${1#/}"
remote=$(aws s3 ls "${src}")
if [[ "${remote}" = *" PRE "* ]]; then
mkdir -p "${dst}"
wrap aws s3 cp --recursive "${src%/}/" "${dst}" \
"Download from asset storage failed"
else
mkdir -p "$(dirname "${dst}")"
wrap aws s3 cp "${src}" "${dst}" \
"Download from asset storage failed"
fi
}
# Upload assets to the cache storage bucket.
#
# $1: Path to asset file or directory to upload
function upload_cache() {
if [ "${1}" = "" ]; then
fail "Parameter required for cache upload"
fi
if [ -d "${1}" ]; then
wrap aws s3 cp --recursive "${1}" "${asset_cache}/" \
"Upload to cache failed"
else
wrap aws s3 cp "${1}" "${asset_cache}/" \
"Upload to cache failed"
fi
}
# Download assets from the cache storage bucket. If
# destination is not provided, remote path will be
# used locally.
#
# $1: Path to asset or directory to download
# $2: Optional destination for downloaded assets
function download_cache() {
if [ "${1}" = "" ]; then
fail "At least one parameter required for cache download"
fi
if [ "${2}" = "" ]; then
dst="${1#/}"
else
dst="${2}"
fi
mkdir -p "${dst}"
src="${asset_cache}/${1#/}"
remote=$(aws s3 ls "${src}")
if [[ "${remote}" = *" PRE "* ]]; then
mkdir -p "${dst}"
wrap aws s3 cp --recursive "${src%/}/" "${dst}" \
"Download from cache storage failed"
else
mkdir -p "$(dirname "${dst}")"
wrap aws s3 cp "${src}" "${dst}" \
"Download from cache storage failed"
fi
}
# Validate arguments for GitHub release. Checks for
# two arguments and that second argument is an exiting
# file asset, or directory.
#
# $1: GitHub tag name
# $2: Asset file or directory of assets
function release_validate() {
if [ "${1}" = "" ]; then
fail "Missing required position 1 argument (TAG) for release"
fi
if [ "${2}" = "" ]; then
fail "Missing required position 2 argument (PATH) for release"
fi
if [ ! -e "${2}" ]; then
fail "Path provided for release (${2}) does not exist"
fi
}
# Generate a GitHub release
#
# $1: GitHub tag name
# $2: Asset file or directory of assets
function release() {
release_validate "${@}"
wrap_raw ghr -u "${repo_owner}" -r "${repo_name}" -c "${full_sha}" -n "${1}" -delete
if [ $? -ne 0 ]; then
wrap ghr -u "${repo_owner}" -r "${repo_name}" -c "${full_sha}" -n "${1}" \
"${1}" "${2}" "Failed to create release for version ${1}"
fi
}
# Generate a GitHub prerelease
#
# $1: GitHub tag name
# $2: Asset file or directory of assets
function prerelease() {
release_validate "${@}"
if [[ "${1}" != *"+"* ]]; then
ptag="${1}+${short_sha}"
else
ptag="${1}"
fi
wrap_raw ghr -u "${repo_owner}" -r "${repo_name}" -c "${full_sha}" -n "${ptag}" \
-delete -prerelease "${ptag}" "${2}"
if [ $? -ne 0 ]; then
wrap ghr -u "${repo_owner}" -r "${repo_name}" -c "${full_sha}" -n "${ptag}" \
-prerelease "${ptag}" "${2}" \
"Failed to create prerelease for version ${1}"
fi
echo -n "${ptag}"
}
# Check if version string is valid for release
#
# $1: Version
# Returns: 0 if valid, 1 if invalid
function valid_release_version() {
if [[ "${1}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
return 0
else
return 1
fi
}
# Validate arguments for HashiCorp release. Ensures asset
# directory exists, and checks that the SHASUMS and SHASUM.sig
# files are present.
#
# $1: Asset directory
function hashicorp_release_validate() {
directory="${1}"
# Directory checks
if [ "${directory}" = "" ]; then
fail "No asset directory was provided for HashiCorp release"
fi
if [ ! -d "${directory}" ]; then
fail "Asset directory for HashiCorp release does not exist"
fi
# SHASUMS checks
if [ ! -e "${directory}/"*SHASUMS ]; then
fail "Asset directory is missing SHASUMS file"
fi
if [ ! -e "${directory}/"*SHASUMS.sig ]; then
fail "Asset directory is missing SHASUMS signature file"
fi
}
# Verify release assets by validating checksum properly match
# and that signature file is valid
#
# $1: Asset directory
function hashicorp_release_verify() {
directory="${1}"
pushd "${directory}" > "${output}"
# First do a checksum validation
wrap shasum -a 256 -c *_SHA256SUMS \
"Checksum validation of release assets failed"
# Next check that the signature is valid
gpghome=$(mktemp -qd)
export GNUPGHOME="${gpghome}"
wrap gpg --import "${HASHICORP_PUBLIC_GPG_KEY}" \
"Failed to import HashiCorp public GPG key"
wrap gpg --verify *SHA256SUMS.sig *SHA256SUMS \
"Validation of SHA256SUMS signature failed"
rm -rf "${gpghome}" > "${output}" 2>&1
popd > "${output}"
}
# Generate a HashiCorp release
#
# $1: Asset directory
function hashicorp_release() {
directory="${1}"
hashicorp_release_validate "${directory}"
hashicorp_release_verify "${directory}"
oid="${AWS_ACCESS_KEY_ID}"
okey="${AWS_SECRET_ACCESS_KEY}"
export AWS_ACCESS_KEY_ID="${RELEASE_AWS_ACCESS_KEY_ID}"
export AWS_SECRET_ACCESS_KEY="${RELEASE_AWS_SECRET_ACCESS_KEY}"
wrap_stream hc-releases upload "${directory}" \
"Failed to upload HashiCorp release assets"
wrap_stream hc-releases publish \
"Failed to publish HashiCorp release"
export AWS_ACCESS_KEY_ID="${oid}"
export AWS_SECRET_ACCESS_KEY="${okey}"
}
# Configures git for hashibot usage
function hashibot_git() {
wrap git config user.name "${HASHIBOT_USERNAME}" \
"Failed to setup git for hashibot usage (username)"
wrap git config user.email "${HASHIBOT_EMAIL}" \
"Failed to setup git for hashibot usage (email)"
wrap git remote set-url origin "https://${HASHIBOT_USERNAME}:${HASHIBOT_TOKEN}@github.com/${repository}" \
"Failed to setup git for hashibot usage (remote)"
}
# Stub cleanup method which can be redefined
# within actual script
function cleanup() {
(>&2 echo "** No cleanup tasks defined")
}
trap cleanup EXIT
# Enable debugging. This needs to be enabled with
# extreme caution when used on public repositories.
# Output with debugging enabled will likely include
# secret values which should not be publicly exposed.
#
# If repository is public, FORCE_PUBLIC_DEBUG environment
# variable must also be set.
is_private=$(curl -H "Authorization: token ${HASHIBOT_TOKEN}" -s "https://api.github.com/repos/${GITHUB_REPOSITORY}" | jq .private)
if [ "${DEBUG}" != "" ]; then
if [ "${is_private}" = "false" ]; then
if [ "${FORCE_PUBLIC_DEBUG}" != "" ]; then
set -x
output="/dev/stdout"
else
fail "Cannot enable debug mode on public repository unless forced"
fi
else
set -x
output="/dev/stdout"
fi
else
output="/dev/null"
fi
# Check if we are running a public repository on private runners
if [ "${VAGRANT_PRIVATE}" != "" ] && [ "${is_private}" = "false" ]; then
fail "Cannot run public repositories on private Vagrant runners. Disable runners now!"
fi
# Common variables
full_sha="${GITHUB_SHA}"
short_sha="${full_sha:0:8}"
ident_ref="${GITHUB_REF#*/*/}"
if [[ "${GITHUB_REF}" == *"refs/tags/"* ]]; then
tag="${GITHUB_REF##*tags/}"
valid_release_version "${tag}"
if [ $? -eq 0 ]; then
release=1
fi
fi
repository="${GITHUB_REPOSITORY}"
repo_owner="${repository%/*}"
repo_name="${repository#*/}"
asset_cache="${ASSETS_PRIVATE_SHORTTERM}/${repository}/${GITHUB_ACTION}"
job_id="${GITHUB_ACTION}"

View File

@ -1,6 +0,0 @@
#!/usr/bin/env bash
. "${root}/.ci/common.sh"
export DEBIAN_FRONTEND="noninteractive"
export PATH="${PATH}:${root}/.ci"

View File

@ -1,62 +0,0 @@
#!/usr/bin/env bash
ghr_version="0.13.0"
# NOTE: This release will generate a new release on the installers
# repository which in turn triggers a full package build
target_owner="hashicorp"
target_repository="vagrant-builders"
csource="${BASH_SOURCE[0]}"
while [ -h "$csource" ] ; do csource="$(readlink "$csource")"; done
root="$( cd -P "$( dirname "$csource" )/../" && pwd )"
. "${root}/.ci/init.sh"
pushd "${root}" > "${output}"
# Install ghr
wrap curl -Lso /tmp/ghr.tgz "https://github.com/tcnksm/ghr/releases/download/v${ghr_version}/ghr_v${ghr_version}_linux_amd64.tar.gz" \
"Failed to download ghr utility"
wrap tar -C /tmp/ -xf /tmp/ghr.tgz \
"Failed to unpack ghr archive"
wrap mv "/tmp/ghr_v${ghr_version}_linux_amd64/ghr" "${root}/.ci/" \
"Failed to install ghr utility"
# Build our gem
wrap gem build *.gemspec \
"Failed to build Vagrant RubyGem"
# Get the path of our new gem
g=(vagrant*.gem)
gem=$(printf "%s" "${g}")
# Determine the version of the release
vagrant_version="$(gem specification "${gem}" version)"
vagrant_version="${vagrant_version##*version: }"
# We want to release into the builders repository so
# update the repository variable with the desired destination
repo_owner="${target_owner}"
repo_name="${target_repository}"
full_sha="master"
export GITHUB_TOKEN="${HASHIBOT_TOKEN}"
if [ "${tag}" = "" ]; then
echo "Generating Vagrant RubyGem pre-release... "
version="v${vagrant_version}+${short_sha}"
prerelease "${version}" "${gem}"
else
# Validate this is a proper release version
valid_release_version "${vagrant_version}"
if [ $? -ne 0 ]; then
fail "Invalid version format for Vagrant release: ${vagrant_version}"
fi
echo "Generating Vagrant RubyGem release... "
version="v${vagrant_version}"
release "${version}" "${gem}"
fi
slack -m "New Vagrant installers release triggered: *${version}*"

176
.ci/slack
View File

@ -1,176 +0,0 @@
#!/usr/bin/env ruby
require "optparse"
require "net/https"
require "uri"
require "json"
OPTIONS = [:channel, :username, :icon, :state, :message,
:message_file, :file, :title, :tail, :webhook].freeze
options = {}
OptionParser.new do |opts|
opts.banner = "Usage: #{File.basename(__FILE__)} [options]"
opts.on("-c", "--channel CHAN", "Send to channel") do |c|
options[:channel] = c
end
opts.on("-u", "--username USER", "Send as username") do |u|
options[:username] = u
end
opts.on("-i", "--icon URL", "User icon image") do |i|
options[:icon] = i
end
opts.on("-s", "--state STATE", "Message state (success, warn, error, or color code)") do |s|
options[:state] = s
end
opts.on("-m", "--message MESSAGE", "Message to send") do |m|
options[:message] = m
end
opts.on("-M", "--message-file MESSAGE_FILE", "Use file contents as message") do |m|
options[:message_file] = m
end
opts.on("-f", "--file MESSAGE_FILE", "Send raw contents of file in message") do |f|
options[:file] = f
end
opts.on("-t", "--title TITLE", "Message title") do |t|
options[:title] = t
end
opts.on("-T", "--tail N", "Send last N lines of content from raw message file") do |t|
options[:tail] = t
end
opts.on("-w", "--webhook HOOK", "Slack webhook") do |w|
options[:webhook] = w
end
opts.on("-h", "--help", "Print help") do
puts opts
exit
end
end.parse!
OPTIONS.each do |key|
if !options.key?(key)
env_key = "SLACK_#{key.to_s.upcase}"
if ENV[env_key]
options[key] = ENV[env_key]
end
end
end
if !options[:webhook]
$stderr.puts "ERROR: Webhook is required!"
exit 1
end
if ENV["CIRCLECI"]
options[:icon] = "https://emoji.slack-edge.com/TF1GCKJNM/circleci/054b58d488e65138.png" unless options[:icon]
options[:username] = "circleci" unless options[:username]
options[:footer] = "CircleCI - <#{ENV["CIRCLE_BUILD_URL"]}|#{ENV["CIRCLE_PROJECT_USERNAME"]}/#{ENV["CIRCLE_PROJECT_REPONAME"]}>"
options[:footer_icon] = "https://emoji.slack-edge.com/TF1GCKJNM/circleci/054b58d488e65138.png"
end
if ENV["GITHUB_ACTIONS"]
options[:icon] = "https://ca.slack-edge.com/T024UT03C-WG8NDATGT-f82ae03b9fca-48" unless options[:icon]
options[:username] = "github" unless options[:username]
options[:footer] = "Actions - <https://github.com/#{ENV["GITHUB_REPOSITORY"]}/commit/#{ENV["GITHUB_SHA"]}/checks|#{ENV["GITHUB_REPOSITORY"]}>"
options[:footer_icon] = "https://ca.slack-edge.com/T024UT03C-WG8NDATGT-f82ae03b9fca-48"
end
options[:state] = "success" unless options[:state]
case options[:state]
when "success", "good"
options[:state] = "good"
when "warn", "warning"
options[:state] = "warning"
when "error", "danger"
options[:state] = "danger"
else
if !options[:state].start_with?("#")
$stderr.puts "ERROR: Invalid value for `state` (#{options[:state]})"
exit 1
end
end
msg = options[:message]
# NOTE: Message provided from CLI argument will end up with
# double escaped newlines so remove one
msg.gsub!("\\n", "\n") if msg
if options[:message_file]
if !File.exist?(options[:message_file])
$stderr.puts "ERROR: Message file does not exist `#{options[:message_file]}`"
exit 1
end
msg_c = File.read(options[:message_file])
msg = msg ? "#{msg}\n\n#{msg_c}" : msg_c
end
if options[:file]
if !File.exist?(options[:file])
$stderr.puts "ERROR: Message file does not exist `#{options[:file]}`"
exit 1
end
if (tail = options[:tail].to_i) > 0
content = ""
buffer = 0
File.open(options[:file], "r") do |f|
until (content.split("\n").size > tail) || buffer >= f.size
buffer += 1000
buffer = f.size if buffer > f.size
f.seek(f.size - buffer)
content = f.read
end
end
parts = content.split("\n")
if parts.size > tail
parts = parts.slice(-tail, tail)
end
fmsg = parts ? parts.join("\n") : ""
else
fmsg = File.read(options[:file])
end
fmsg = "```\n#{fmsg}\n```"
if msg
msg = msg << "\n\n" << fmsg
end
end
if msg.to_s.empty?
$stderr.puts "ERROR: No message content provided!"
exit 1
end
attach = {text: msg, fallback: msg, color: options[:state], mrkdn: true}
attach[:title] = options[:title] if options[:title]
attach[:footer] = options[:footer] if options[:footer]
attach[:footer_icon] = options[:footer_icon] if options[:footer_icon]
attach[:ts] = Time.now.to_i
payload = {}.tap do |pd|
pd[:username] = options.fetch(:username, "packet-exec")
pd[:channel] = options[:channel] if options[:channel]
pd[:icon_url] = options[:icon] if options[:icon]
pd[:attachments] = [attach]
end
result = Net::HTTP.post(URI(options[:webhook]), payload.to_json, "Content-Type" => "application/json")
if !result.code.start_with?("2")
$stderr.puts "Failed to send slack message"
exit 1
else
$stdout.puts "ok"
end

View File

@ -1,27 +0,0 @@
#!/usr/bin/env bash
csource="${BASH_SOURCE[0]}"
while [ -h "$csource" ] ; do csource="$(readlink "$csource")"; done
root="$( cd -P "$( dirname "$csource" )/../" && pwd )"
pushd "${root}" > /dev/null
export DEBIAN_FRONTEND="noninteractive"
# Install required dependencies
sudo apt-get update || exit 1
sudo apt-get install -yq bsdtar || exit 1
# Ensure bundler is installed
gem install --no-document bundler || exit 1
# Install the bundle
bundle install || exit 1
# Run tests
bundle exec rake test:unit
result=$?
popd > /dev/null
exit $result

View File

@ -1,33 +0,0 @@
version: 2
jobs:
build-website:
# setting the working_directory along with the checkout path allows us to not have
# to cd into the website/ directory for commands
working_directory: ~/project/website
docker:
- image: hashicorp/middleman-hashicorp:0.3.35
steps:
- checkout:
path: ~/project
- restore_cache:
key: static-site-gems-v1-{{ checksum "Gemfile.lock" }}
- run:
command: bundle check || bundle install --path vendor/bundle
- save_cache:
key: static-site-gems-v1-{{ checksum "Gemfile.lock" }}
paths:
- ~/project/website/vendor/bundle
- run:
command: bundle exec middleman build
- run:
command: ./scripts/deploy.sh
workflows:
version: 2
website:
jobs:
- build-website:
context: static-sites
filters:
branches:
only: stable-website

View File

@ -1,5 +0,0 @@
# Code of Conduct
HashiCorp Community Guidelines apply to you when interacting with the community here on GitHub and contributing code.
Please read the full text at https://www.hashicorp.com/community-guidelines

View File

@ -1,79 +1,41 @@
# Contributing to Vagrant # How to contribute
**First:** We like to encourage you to contribute to the repository. If you're unsure or afraid of _anything_, just ask or submit the issue or pull request anyways. You won't be yelled at for giving your best effort. The worst that can happen is that you'll be politely asked to change something. We appreciate any sort of contributions, and don't want a wall of rules to get in the way of that. We like to encourage you to contribute to the repository.
This should be as easy as possible for you but there are a few things to consider when contributing.
The following guidelines for contribution should be followed if you want to submit a pull request.
However, for those individuals who want a bit more guidance on the best way to contribute to the project, read on. This document will cover what we're looking for. By addressing all the points we're looking for, it raises the chances we can quickly merge or address your contributions. ## How to prepare
Before opening a new issue or pull request, we do appreciate if you take some time to search for [possible duplicates](https://github.com/hashicorp/vagrant/issues?q=sort%3Aupdated-desc), or similar discussions in the [mailing list](https://groups.google.com/forum/#!forum/vagrant-up). On GitHub, you can scope searches by labels to narrow things down. * You need a [GitHub account](https://github.com/signup/free)
* Submit an [issue ticket](https://github.com/hashicorp/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
with the report using [gist](https://gist.github.com/) or some other paste
service by prepending `VAGRANT_LOG=debug` to your `vagrant` commands.
* Fork the repository on GitHub
## Issues ## Make Changes
### Reporting an Issue
**Tip:** We have provided a [GitHub issue template](https://github.com/hashicorp/vagrant/blob/master/.github/ISSUE_TEMPLATE.md). By respecting the proposed format and filling all the relevant sections, you'll strongly help the Vagrant collaborators to handle your request the best possible way.
### Issue Lifecycle
1. The issue is reported.
2. The issue is verified and categorized by Vagrant collaborator(s). Categorization is done via GitHub tags for different dimensions (like issue type, affected components, pending actions, etc.)
3. Unless it is critical, the issue is left for a period of time, giving outside contributors a chance to address the issue. Later, the issue may be assigned to a Vagrant collaborator and planned for a specific release [milestone](https://github.com/hashicorp/vagrant/milestones)
4. The issue is addressed in a pull request or commit. The issue will be referenced in the commit message so that the code that fixes it is clearly linked.
5. The issue is closed. Sometimes, valid issues will be closed to keep the issue tracker clean. The issue is still indexed and available for future viewers, or can be re-opened if necessary.
## Pull Requests
Thank you for contributing! Here you'll find information on what to include in your Pull Request (“PR” for short) to ensure it is reviewed quickly, and possibly accepted.
Before starting work on a new feature or anything besides a minor bug fix, it is highly recommended to first initiate a discussion with the Vagrant community (either via a GitHub issue, the [mailing list](https://groups.google.com/forum/#!forum/vagrant-up), IRC freenode `#vagrant` or [Gitter](https://gitter.im/mitchellh/vagrant)). This will save you from wasting days implementing a feature that could be rejected in the end.
No pull request template is provided on GitHub. The expected changes are often already described and validated in an existing issue, that obviously should be referenced. The Pull Request thread should be mainly used for the code review.
**Tip:** Make it small! A focused PR gives you the best chance of having it accepted. Then, repeat if you have more to propose!
### How to prepare
Once you're confident that your upcoming changes will be accepted:
* In your forked repository, create a topic branch for your upcoming patch. * In your forked repository, create a topic branch for your upcoming patch.
* Usually this is based on the master branch. * Usually this is based on the master branch.
* Checkout a new branch based on master; `git checkout -b my-contrib master` * Create a branch based on master; `git branch
Please avoid working directly on the `master` branch. fix/master/my_contribution master` then checkout the new branch with `git
* Make focused commits of logical units and describe them properly. checkout fix/master/my_contribution`. Please avoid working directly on the `master` branch.
* Avoid re-formatting of the existing code * Make commits of logical units and describe them properly.
* Check for unnecessary whitespace with `git diff --check` before committing. * Check for unnecessary whitespace with `git diff --check` before committing.
* If possible, submit tests along with your topic branch. It will help a lot to get your your patch / new feature accepted, and should prevent unwanted breaking changes to silently happen in future developments.
* Assure nothing is broken by running manual tests, and all the automated tests.
### Submit Changes * If possible, submit tests to your patch / new feature so it can be tested easily.
* Assure nothing is broken by running all the tests.
## Submit Changes
* Push your changes to a topic branch in your fork of the repository. * Push your changes to a topic branch in your fork of the repository.
* Open a PR to the original repository and choose the right original branch you want to patch (master for most cases). * Open a pull request to the original repository and choose the right original branch you want to patch.
* If not done in commit messages (which you really should do) please reference and update your issue with the code changes. * If not done in commit messages (which you really should do) please reference and update your issue with the code changes.
* Even if you have write access to the repository, do not directly push or merge your own pull requests. Let another team member review your PR and approve. * Even if you have write access to the repository, do not directly push or merge pull-requests. Let another team member review your pull request and approve.
### Pull Request Lifecycle
1. You are welcome to submit your PR for commentary or review before it is fully completed. Please prefix the title of your PR with "[WIP]" to indicate this. It's also a good idea to include specific questions or items you'd like feedback on.
2. Sign the [HashiCorp CLA](#hashicorp-cla). If you haven't signed the CLA yet, a bot will ask you to do so. You only need to sign the CLA once. If you've already signed the CLA, the CLA status will be green automatically.
3. The PR is categorized by Vagrant collaborator(s), applying GitHub tags similarly to issues triage.
4. Once you believe your PR is ready to be merged, you can remove any
"[WIP]" prefix from the title and a Vagrant collaborator will review.
5. One of the Vagrant collaborators will look over your contribution and either provide comments letting you know if there is anything left to do. We do our best to provide feedback in a timely manner, but it may take some time for us to respond.
6. Once all outstanding comments have been addressed, your contribution will be merged! Merged PRs will be included in the next Vagrant release. The Vagrant contributors will take care of updating the CHANGELOG as they merge.
7. We might decide that a PR should be closed. We'll make sure to provide clear reasoning when this happens.
## HashiCorp CLA
We require all contributors to sign the [HashiCorp CLA](https://www.hashicorp.com/cla).
In simple terms, the CLA affirms that the work you're contributing is original, that you grant HashiCorp permission to use that work (including license to any patents necessary), and that HashiCorp may relicense your work for our commercial products if necessary. Note that this description is a summary and the specific legal terms should be read directly in the [CLA](https://www.hashicorp.com/cla).
The CLA does not change the terms of the standard open source license used by our software such as MPL2 or MIT. You are still free to use our projects within your own projects or businesses, republish modified source, and more. Please reference the appropriate license of this project to learn more.
To sign the CLA, open a pull request as usual. If you haven't signed the CLA yet, a bot will respond with a link asking you to sign the CLA. We cannot merge any pull request until the CLA is signed. You only need to sign the CLA once. If you've signed the CLA before, the bot will not respond to your PR and your PR will be allowed to merge.
# Additional Resources # Additional Resources
* [HashiCorp Community Guidelines](https://www.hashicorp.com/community-guidelines)
* [General GitHub documentation](https://help.github.com/) * [General GitHub documentation](https://help.github.com/)
* [GitHub pull request documentation](https://help.github.com/send-pull-requests/) * [GitHub pull request documentation](https://help.github.com/send-pull-requests/)

View File

@ -1,8 +1,7 @@
Please note that the Vagrant issue tracker is in priority reserved for bug reports and enhancements. For general usage questions, please use the Vagrant mailing list: Please note that the Vagrant issue tracker is reserved for bug reports and
enhancements. For general usage questions, please use the Vagrant mailing list:
https://groups.google.com/forum/#!forum/vagrant-up. Thank you! https://groups.google.com/forum/#!forum/vagrant-up. Thank you!
**Tip:** Before submitting your issue, don't hesitate to remove the above introductory text, possible empty sections (e.g. References), and this tip.
### Vagrant version ### Vagrant version
Run `vagrant -v` to show the version. If you are not running the latest version Run `vagrant -v` to show the version. If you are not running the latest version
of Vagrant, please upgrade before submitting an issue. of Vagrant, please upgrade before submitting an issue.

View File

@ -1,37 +0,0 @@
on:
push:
branches:
- master
paths-ignore:
- 'CHANGELOG.md'
- 'website/**'
jobs:
build-gem:
name: Build Vagrant RubyGem
runs-on: ubuntu-18.04
steps:
- name: Code Checkout
uses: actions/checkout@v1
- name: Set Ruby
uses: actions/setup-ruby@v1
with:
ruby-version: '2.6'
- name: Build RubyGem
run: ./.ci/build.sh
working-directory: ${{github.workspace}}
env:
ASSETS_LONGTERM_PREFIX: elt
ASSETS_PRIVATE_BUCKET: ${{ secrets.ASSETS_PRIVATE_BUCKET }}
ASSETS_PRIVATE_LONGTERM: ${{ secrets.ASSETS_PRIVATE_LONGTERM }}
ASSETS_PRIVATE_SHORTTERM: ${{ secrets.ASSETS_PRIVATE_SHORTTERM }}
ASSETS_PUBLIC_BUCKET: ${{ secrets.ASSETS_PUBLIC_BUCKET }}
ASSETS_PUBLIC_LONGTERM: ${{ secrets.ASSETS_PUBLIC_LONGTERM }}
ASSETS_PUBLIC_SHORTTERM: ${{ secrets.ASSETS_PUBLIC_SHORTTERM }}
ASSETS_SHORTTERM_PREFIX: est
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
HASHIBOT_EMAIL: ${{ secrets.HASHIBOT_EMAIL }}
HASHIBOT_TOKEN: ${{ secrets.HASHIBOT_TOKEN }}
HASHIBOT_USERNAME: ${{ secrets.HASHIBOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

View File

@ -1,38 +0,0 @@
on:
push:
branches:
- 'build-*'
tags: '*'
paths-ignore:
- 'CHANGELOG.md'
- 'website/**'
jobs:
trigger-release:
name: Trigger Installers Build
runs-on: ubuntu-18.04
steps:
- name: Code Checkout
uses: actions/checkout@v1
- name: Set Ruby
uses: actions/setup-ruby@v1
with:
ruby-version: '2.6'
- name: Create Builders Release
run: ./.ci/release.sh
working-directory: ${{github.workspace}}
env:
ASSETS_LONGTERM_PREFIX: elt
ASSETS_PRIVATE_BUCKET: est
ASSETS_PRIVATE_LONGTERM: ${{ secrets.ASSETS_PRIVATE_LONGTERM }}
ASSETS_PRIVATE_SHORTTERM: ${{ secrets.ASSETS_PRIVATE_SHORTTERM }}
ASSETS_PUBLIC_BUCKET: ${{ secrets.ASSETS_PUBLIC_BUCKET }}
ASSETS_PUBLIC_LONGTERM: ${{ secrets.ASSETS_PUBLIC_LONGTERM }}
ASSETS_PUBLIC_SHORTTERM: ${{ secrets.ASSETS_PUBLIC_SHORTTERM }}
ASSETS_SHORTTERM_PREFIX: ${{ secrets.ASSETS_SHORTTERM_PREFIX }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
HASHIBOT_EMAIL: ${{ secrets.HASHIBOT_EMAIL }}
HASHIBOT_TOKEN: ${{ secrets.HASHIBOT_TOKEN }}
HASHIBOT_USERNAME: ${{ secrets.HASHIBOT_USERNAME }}
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}

View File

@ -1,29 +0,0 @@
on:
push:
branches:
- master
- 'test-*'
paths-ignore:
- 'CHANGELOG.md'
- 'website/**'
pull_request:
branches:
- master
jobs:
unit-tests:
runs-on: ubuntu-18.04
strategy:
matrix:
ruby: [ '2.4.x', '2.5.x', '2.6.x' ]
name: Vagrant unit tests on Ruby ${{ matrix.ruby }}
steps:
- name: Code Checkout
uses: actions/checkout@v1
- name: Setup Ruby
uses: actions/setup-ruby@v1
with:
ruby-version: ${{matrix.ruby}}
architecture: 'x64'
- name: Run Tests
run: .ci/test.sh

View File

@ -1,12 +0,0 @@
poll "closed_issue_locker" "locker" {
schedule = "0 50 1 * * *"
closed_for = "720h" # 30 days
max_issues = 500
sleep_between_issues = "5s"
message = <<-EOF
I'm going to lock this issue because it has been closed for _30 days_ . This helps our maintainers find and focus on the active issues.
If you have found a problem that seems similar to this, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further.
EOF
}

25
.travis.yml Normal file
View File

@ -0,0 +1,25 @@
language: ruby
sudo: false
cache: bundler
addons:
apt:
packages:
- bsdtar
rvm:
- 2.3.6
- 2.4.3
- 2.5.0
branches:
only:
- master
env:
global:
- NOKOGIRI_USE_SYSTEM_LIBRARIES=true
script: bundle exec rake test:unit

View File

@ -2,432 +2,10 @@
FEATURES: FEATURES:
IMPROVEMENTS:
BUG FIXES:
## 2.2.7 (January 27, 2020)
IMPROVEMENTS:
- guest/opensuse: Check for basename hostname prior to setting hostname [GH-11170]
- host/linux: Check for modinfo in /sbin if it's not on PATH [GH-11178]
- core: Show guest name in hostname error message [GH-11175]
- provisioners/shell: Linux guests now support `reboot` option [GH-11194]
- darwin/nfs: Put each NFS export on its own line [GH-11216]
- contrib/bash: Add more completion flags to up command [GH-11223]
- provider/virtualbox: Add VirtualBox provider support for version 6.1.x [GH-11250]
- box/outdated: Allow to force check for box updates and ignore cached check [GH-11231]
- guest/alpine: Update apk cache when installing rsync [GH-11220]
- provider/virtualbox: Improve error message when machine folder is inaccessible [GH-11239]
- provisioner/ansible_local: Add pip install method for arch guests [GH-11265]
- communicators/winssh: Use Windows shell for `vagrant ssh -c` [GH-11258]
BUG FIXES:
- command/snapshot/save: Fix regression that prevented snapshot of all guests in environment [GH-11152]
- core: Update UI to properly retain newlines when adding prefix [GH-11126]
- core: Check if box update is available locally [GH-11188]
- core: Ensure Vagrant::Errors are loaded in file_checksum util [GH-11183]
- cloud/publish: Improve argument handling for missing arguments to command [GH-11184]
- core: Get latest version for current provider during outdated check [GH-11192]
- linux/nfs: avoid adding extra newlines to /etc/exports [GH-11201]
- guest/darwin: Fix VMware synced folders on APFS [GH-11267]
- guest/redhat: Ensure `nfs-server` is restarted when installing nfs client [GH-11212]
- core: Do not validate checksums if options are empty string [GH-11211]
- provider/docker: Enhance docker build method to match against buildkit output [GH-11205]
- provisioner/ansible_local: Don't prompt for input when installing Ansible on Ubuntu and Debian [GH-11191]
- provisioner/ansible_local: Ensure all guest caps accept all passed in arguments [GH-11265]
- host/windows: Fix regression that prevented port collisions from being detected [GH-11244]
- core/provisioner: Set top level provisioner name if set in a provisioner config [GH-11295]
## 2.2.6 (October 14, 2019)
FEATURES:
- core/provisioners: Introduce new Provisioner options: before and after [GH-11043]
- guest/alpine: Integrate the vagrant-alpine plugin into Vagrant core [GH-10975]
IMPROVEMENTS:
- command/box/prune: Allow prompt skip while preserving actively in use boxes [GH-10908]
- command/cloud: Support providing checksum information with boxes [GH-11101]
- dev: Fixed Vagrantfile for Vagrant development [GH-11012]
- guest/alt: Improve handling for using network tools when setting hostname [GH-11000]
- guest/suse: Add ipv6 network config templates for SUSE based distributions [GH-11013]
- guest/windows: Retry on connection timeout errors for the reboot capability [GH-11093]
- host/bsd: Use host resolve path capability to modify local paths if required [GH-11108]
- host/darwin: Add host resolve path capability to provide real paths for firmlinks [GH-11108]
- provisioners/chef: Update pkg install flags for chef on FreeBSD guests [GH-11075]
- provider/hyperv: Improve error message when VMMS is not running [GH-10978]
- provider/virtualbox: Raise additional errors for incomplete virtualbox installation on usable check [GH-10938]
- util/filechecksum: Add support for more checksum types [GH-11101]
BUG FIXES:
- command/rsync-auto: Fix path watcher bug so that all subdirectories are synced when changed [GH-11089]
- command/snapshot/save: Ensure VM id is passed to list snapshots for hyper-v provider [GH-11097]
- core: Ensure proper paths are shown in config loading exceptions [GH-11056]
- guest/suse: Use hostnamectl instead of hostname to set the hostname under SUSE [GH-11100]
- provider/docker: Fix default provider validation if password is used [GH-11053]
- provider/docker: Fix Docker providers usable? check [GH-11068]
- provisioner/ansible_local: Ensure pip_install_cmd is finalized to emptry string [GH-11098]
- provisioner/file: Ensure relative path for file provisioner source is relative to guest machines cwd [GH-11099]
- provider/docker: Ensure docker build_args option is properly set in docker compose config yaml [GH-11106]
- guest/suse: Update nfs & service daemon names for suse based hosts and guests [GH-11076]
- provider/docker: Determine ip address prefix workaround for docker public networks [GH-11111]
- provider/docker: Only return interfaces where addr is not nil for networks [GH-11116]
## 2.2.5 (June 19, 2019)
FEATURES:
- providers/docker: Private and Public networking support [GH-10702]
IMPROVEMENTS:
- command/global-status: Provide machine-readable information [GH-10506]
- command/snapshot: Separate snapshot names for guests when listing snapshots [GH-10828]
- command/box/update: Ignore missing metadata files when updating all boxes [GH-10829]
- core: Use consistent settings when unpacking boxes as root [GH-10707]
- core: Write metadata.json file when packaging box [GH-10706]
- core: Remove whitespace from id file on load [GH-10727]
- core/bundler: Support resolution when installed within system [GH-10894]
- guest/coreos: Update network configuration and hostname setting [GH-10752]
- guest/freebsd: Add proper VirtualBox share folders support for FreeBSD guests [GH-10717]
- guest/freebsd: Add unmount share folder for VirtualBox guests [GH-10761]
- guest/freebsd: Simplify network interface listing when configuring networks [GH-10763]
- providers/docker: Add usable? check to docker provider [GH-10890]
- synced_folder/smb: Remove configuration information from synced folder data [GH-10811]
BUG FIXES:
- command/box/update: Ensure the right version is picked when updating specific boxes [GH-10810]
- command/cloud: Properly set variable from CLI argument parsing for `username` field [GH-10726]
- command/rsync_auto: Use relative paths to machines folder path for file path Listener [GH-10902]
- communicator/ssh: Remove net/sftp loading to prevent loading errors [GH-10745]
- contrib/bash: Search for running_vm_list only in `machines` folder [GH-10841]
- core/bundler: Properly parse multiple constants when installing plugins [GH-10896]
- core/environment: Support plugin configuration within box Vagrantfiles [GH-10889]
- core/triggers: Fix typo in UI output [GH-10748]
- core/triggers: Properly exit with abort option [GH-10824]
- core/triggers: Ensure guest names are string when filtering trigger configs [GH-10854]
- core/triggers: Abort after all running processes have completed when parallel is enabled [GH-10891]
- guest/void: Fix NFS capability detection [GH-10713]
- guest/bsd: Properly set BSD options order for /etc/exports [GH-10909]
- host/windows: Fix rubygems error when host has directory named `c` [GH-10803]
- provider/virtualbox: Ensure non-existent machines do not attempt to list snapshots [GH-10784]
- provider/docker: Properly set docker-compose config file with volume names [GH-10820]
- provisioner/ansible: Fix pip installer hardcoded curl get_pip.py piped to python [GH-10625]
- provisioner/chef: Update chef install check for guests [GH-10917]
- synced_folders/rsync: Remove rsync__excludes from command if array is empty [GH-10901]
## 2.2.4 (February 27, 2019)
FEATURES:
- core/triggers: Introduce new option `:type` for actions, hooks, and commands [GH-10615]
IMPROVEMENTS:
- communicator/ssh: Update `#upload` behavior to work properly with new sshd path checks [GH-10698]
- communicator/winrm: Update `#upload` behavior to match ssh communicator upload behavior [GH-10698]
- guest/windows: Add reboot output to guest capability [GH-10638]
- provisioner/file: Refactor path modification rules and allow communicator to handle details [GH-10698]
BUG FIXES:
- core: Fix format finalization of plugins in Vagrantfile [GH-10664]
- core: Fix SIGINT behavior and prevent backtrace [GH-10666]
- core: Change remaining box_client_cert refs to box_download_client_cert [GH-10622]
- core: Move over AddAuthentication middleware and hooks out of deprecated class [GH-10686]
- guest/debian: Properly set DHCP for systemd-networkd ips [GH-10586]
- guest/solaris11: Create interface if required before configuration [GH-10595]
- installers/appimage: Use ld path with appimage libs on suffix [GH-10647]
- providers/docker: Expand paths when comparing synced folders on reload [GH-10645]
- providers/virtualbox: Fix import paths on Windows with VirtualBox 6 [GH-10629]
- synced_folders/rsync: Properly clean up tmp folder created during rsync [GH-10690]
## 2.2.3 (January 9, 2019)
FEATURES:
- host/void: Add host support for void linux [GH-10012]
IMPROVEMENTS:
- command/rsync-auto: Prevent crash on post-rsync command failure [GH-10515]
- command/snapshot: Raise error for bad subcommand [GH-10470]
- command/package: Ensure temp dir for package command is cleaned up [GH-10479]
- command/powershell: Support running elevated commands [GH-10528]
- communicator/ssh: Add `config` and `remote_user` options [GH-10496]
- core: Display version update on stderr instead of stdout [GH-10482]
- core: Add experimental feature flag [GH-10485]
- core: Show box version during box outdated check [GH-10573]
- guest/windows: Modify elevated username only on username failure [GH-10488]
- host/windows: Prevent SMB setup commands from becoming too long [GH-10489]
- host/windows: Automatically answer yes when pruning SMB shares [GH-10524]
- provisioners/file: Show source and destination locations with file provisioner [GH-10570]
- provisioners/salt: Validate that `install_type` is set if `version` is specified [GH-10474]
- provisioners/salt: Update default install version [GH-10537]
- provisioners/shell: Add `reboot` option for rebooting supported guest [GH-10532]
- synced_folders/rsync: Support using rsync `--chown` option [GH-10529]
- util/guest_inspection: Validate hostnamectl command works when detected [GH-10512]
- util/platform: Use wslpath command for customized root on WSL [GH-10574]
BUG FIXES:
- command/cloud publish: Ensure box file exists before path expanding [GH-10468]
- command/cloud publish: Catch InvalidVersion errors from vagrant_cloud client [GH-10513]
- command/snapshot: Retain consistent provisioning behavior across all commands [GH-10490]
- command/validate: Bypass install checks for validating configs with the `--ignore-provider` flag [GH-10467]
- communicator/ssh: Fix garbage output detection [GH-10571]
- guest/alt: Fix network configuration errors [GH-10527]
- guest/coreos: Fix grep command for network interface of CoreOS guest [GH-10554]
- guest/freebsd: Fix defaultrouter rcvar in static network template [GH-10469]
- guest/redhat: Fix network configuration errors [GH-10527]
- providers/virtualbox: Adjust version requirement for NIC warning [GH-10486]
- util/powershell: Use correct Base64 encoding for encoded commands [GH-10487]
## 2.2.2 (November 27, 2018)
BUG FIXES:
- providers/virtualbox: Update default_nic_type implementation and add warning [GH-10450]
## 2.2.1 (November 15, 2018)
FEATURES:
- core/plugins: Add reset! method to communicator [GH-10399]
- providers/virtualbox: Add support for VirtualBox 6.0 [GH-10379]
IMPROVEMENTS:
- command/validate: Allow validation of config while ignoring provider [GH-10351]
- communicators/ssh: Prevent overly verbose output waiting for connection [GH-10321]
- communicators/ssh: Support ed25519 keys [GH-10365]
- communicators/ssh: Add reset! implementation [GH-10399]
- communicators/winrm: Add reset! implementation [GH-10399]
- core: Limit number of automatic box update checks [GH-10359]
- host/windows: Remove PATH check in WSL detection [GH-10313]
- providers/hyperv: Disable automatic checkpoints before deletion [GH-10406]
- providers/virtualbox: Add `automount` flag if specified with synced_folder [GH-10326]
- providers/virtualbox: Refactor host only network settings [GH-7699]
- providers/virtualbox: Support setting default NIC type for network adapters [GH-10383]
- providers/virtualbox: Update ssh_port helper to handle multiple matches [GH-10409]
- provisioners/shell: Add :reset option to allow communicator reset [GH-10399]
- synced_folders/smb: Allow for 'default' smb_username in prompt if set [GH-10319]
- util/network_ip: Simplify `network_address` helper [GH-7693]
- util/platform: Prevent hard failure during hyper-v enabled check [GH-10332]
BUG FIXES:
- command/login: Only show deprecation warning when command is invoked [GH-10374]
- core: Fallback to Vagrantfile defined box information [GH-10368]
- core/bundler: Update source ordering to properly resolve with new RubyGems [GH-10364]
- core/triggers: Only split inline script if host is non-Windows [GH-10405]
- communicator/winrm: Prepend computer name to username when running elevated commands [GH-10387]
- guest/debian: Fix halting issue when setting hostname by restarting networking on guest [GH-10301, GH-10330]
- guest/linux: Fix vagrant user access to docker after install [GH-10399]
- guest/windows: Add reboot capability to fix hostname race condition [GH-10347]
- guest/windows: Allow for reading key paths with spaces [GH-10389]
- host/windows: Fix powershell to properly handle paths with spaces [GH-10390]
- providers/docker: Deterministic host VM synced folder location for Docker VM [GH-10311]
- providers/hyperv: Fix network vlan configuration script [GH-10366]
- providers/hyperv: Properly output error message on failed guest import [GH-10404]
- providers/hyperv: Fix typo in network configuration detection script [GH-10410]
## 2.2.0 (October 16, 2018)
FEATURES:
- command/cloud: Introduce `vagrant cloud` subcommand to Vagrant [GH-10148]
- command/upload: Add command for uploading files to guest [GH-10263]
- command/winrm: Add command for executing guest commands via WinRM [GH-10263]
- command/winrm-config: Add command for providing WinRM configuration [GH-10263]
IMPROVEMENTS:
- core: Ensure file paths are identical when checking for cwd [GH-10220]
- core: Add config option `ignore_box_vagrantfile` for ignoring vagrantfile inside box [GH-10242]
- core/triggers: Add abort option to core triggers [GH-10232]
- core/triggers: Introduce `ruby` option for trigger [GH-10267]
- contrib/bash: Add completion for snapshot names for vagrant snapshot restore|delete [GH-9054]
- providers/docker: Build docker from git repo [GH-10221]
- providers/hyperv: Update Hyper-V admin check and allow override via ENV variable [GH-10275]
- providers/virtualbox: Allow base_mac to be optional [GH-10255]
- provisioners/salt: bootstrap-salt.sh: use -s with curl [GH-9432]
- provisioners/salt: remove leading space with bootstrap_options [GH-9431]
BUG FIXES:
- core/environment: Provide rgloader for local plugin installations [GH-10279]
- contrib/sudoers/osx: Fix missing comma and add remove export alias [GH-10235]
- guest/redhat: Update restart logic in redhat change_host_name cap [GH-10223]
- guest/windows: Allow special characters in SMB password field [GH-10219]
- providers/hyperv: Only use AutomaticCheckpointsEnabled when available [GH-10264]
- providers/hyperv: Only use CheckpointType when available [GH-10265]
- provisioners/ansible: Fix remote directory creation [GH-10259, GH-10258]
- provisioners/puppet: Properly set env variables for puppet provisioner on windows [GH-10218]
- provisioners/salt: Properly set salt pillar variables for windows guests [GH-10215]
- synced_folders/rsync: Ensure unique tmp dirs for ControlPath with rsync [GH-10291]
## 2.1.5 (September 12, 2018)
IMPROVEMENTS:
- core: Add `Vagrant.version?` helper method [GH-10191]
- core: Scrub sensitive values from logger output [GH-10200]
- core: Prevent multiple evaluations of Vagrantfile [GH-10199]
- command/init: Support VAGRANT_DEFAULT_TEMPLATE env var [GH-10171]
- command/powershell: Improve doc help string and fix winrm locales error [GH-10189]
- contrib/bash: autocomplete running VM names for destroy subcommand [GH-10168]
- guest/debian: Use `sudo` to determine if systemd is in use for hardened systems [GH-10198]
- guest/openbsd: Add IPv6 network template for OpenBSD machines [GH-8912]
- provisioners/salt: Allow non-windows hosts to pass along version [GH-10194]
BUG FIXES:
- core: Fix Vagrant.has_plugin? behavior before plugins are initialized [GH-10165]
- core: Check verify_host_key for falsey or :never values when generating ssh config [GH-10182]
- guest/linux: Filter out empty strings and loopback interfaces when constructing list of network interfaces [GH-10092]
- provider/hyper-v: Check for automatic checkpoint support before configuring [GH-10181]
## 2.1.4 (August 30, 2018)
BUG FIXES:
- core: Fix local plugin installation prompt answer parsing [GH-10154]
- core: Reset internal environment after plugin loading [GH-10155]
- host/windows: Fix SMB list parsing when extra fields are included [GH-10156]
- provisioners/ansible_local: Fix umask setting permission bug [GH-10140]
## 2.1.3 (August 29, 2018)
FEATURES:
- core: Support for project specific plugins [GH-10037]
IMPROVEMENTS:
- command/reload: Add `--force` flag to reload command [GH-10123]
- communicator/winrm: Display warning if vagrant-winrm plugin is detected [GH-10076]
- contrib/bash: Replace -VAGRANTSLASH- with literal slash in completion [GH-9987]
- core: Show installed version of Vagrant when displaying version check [GH-9968]
- core: Retain information of original box backing active guest [GH-10083]
- core: Only write box info if provider supports box objects [GH-10126]
- core: Update net-ssh dependency constraint to ~> 5.0.0 [GH-10066]
- core/triggers: Catch and allow for non-standard exit codes with triggers `run` options [GH-10005]
- core/triggers: Allow for spaces in `path` for trigger run option [GH-10118]
- guest/debian: Isolate network interface configuration to individual files for systemd [GH-9889]
- guest/redhat: Use libnfs-utils package if available [GH-9878]
- provider/docker: Support Docker volume consistency for synced folders [GH-9811]
- provider/hyperv: Disable synced folders on non-DrvFs file systems by default [GH-10001]
- util/downloader: Support custom suffix on user agent string [GH-9966]
- util/downloader: Prevent false positive matches on Location header [GH-10041]
- util/subprocess: Force system library paths for executables external to AppImage [GH-10078]
BUG FIXES:
- core: Disable Vagrantfile loading with plugin commands [GH-10030]
- core: Ensure the SecureRandom library is loaded for the trigger class [GH-10063]
- core/triggers: Allow trigger run args option to be a single string [GH-10116]
- util/powershell: Properly `join` commands from passed in array [GH-10115]
- guest/solaris: Add back guest detection check for Solaris derived guests [GH-10081]
- guest/windows: Be more explicit when invoking cmd.exe with mount_volume script [GH-9976]
- host/linux: Fix sudo usage in NFS capability when modifying exports file [GH-10084]
- host/windows: Remove localization dependency from SMB list generation [GH-10043]
- provider/docker: Convert windows paths for volume mounts on docker driver [GH-10100]
- provider/hyperv: Fix checkpoint configuration and properly disable automatic checkpoints by default [GH-9999]
- provider/hyperv: Remove localization dependency from access check [GH-10000]
- provider/hyperv: Enable ExposeVirtualizationExtensions only when available [GH-10079]
- provider/virtualbox: Skip link-local when fixing IPv6 route [GH-9639, GH-10077]
- push/ftp: Custom error when attempting to push too many files [GH-9952]
- util/downloader: Prevent errors when Location header contains relative path [GH-10017]
- util/guest_inspection: Prevent nmcli check from hanging when pty is enabled [GH-9926]
- util/platform: Always force string type conversion on path [GH-9998]
## 2.1.2 (June 26, 2018)
IMPROVEMENTS:
- commands/suspend: Introduce flag for suspending all machines [GH-9829]
- commands/global-status: Improve message about removing stale entries [GH-9856]
- provider/hyperv: Attempt to determine import failure cause [GH-9936]
- provider/hyperv: Update implementation. Include support for modifications on reload [GH-9872]
- provider/hyperv: Validate maxmemory configuration setting [GH-9932]
- provider/hyperv: Enable provider within WSL [GH-9943]
- provider/hyperv: Add Hyper-V accessibility check on data directory path [GH-9944]
- provisioners/ansible_local: Improve installation from PPA on Ubuntu guests.
The compatibility is maintained only for active long-term support (LTS) versions,
i.e. Ubuntu 12.04 (Precise Pangolin) is no longer supported. [GH-9879]
BUG FIXES:
- communicator/ssh: Update ssh private key file permission handling on Windows [GH-9923, GH-9900]
- core: Display plugin commands in help [GH-9808]
- core: Ensure guestpath or name is set with synced_folder option and dont set guestpath if not provided [GH-9692]
- guest/debian: Fix netplan generation when using DHCP [GH-9855]
- guest/debain: Update priority of network configuration file when using networkd [GH-9867]
- guest/ubuntu: Update netplan config generation to detect NetworkManager [GH-9824]
- guest/ubuntu: Fix failing Ansible installation from PPA on Bionic Beaver (18.04 LTS) [GH-9796]
- host/windows: Prevent processing of last SMB line when using net share [GH-9917]
- provisioner/chef: Prevent node_name set on configuration with chef_apply [GH-9916]
- provisioner/salt: Remove usage of masterless? config attribute [GH-9833]
## 2.1.1 (May 7, 2018)
IMPROVEMENTS:
- guest/linux: Support builtin vboxsf module for shared folders [GH-9800]
- host/windows: Update SMB capability to work without Get-SmbShare cmdlet [GH-9785]
BUG FIXES:
- core/triggers: Initialize internal trigger object for machine before initializing provider [GH-9784]
- core/triggers: Ensure internal trigger fire does not get called if plugin installed [GH-9799]
- provider/hyperv: Call import script with switchid instead of switchname [GH-9781]
## 2.1.0 (May 3, 2018)
FEATURES:
- core: Integrate vagrant-triggers plugin functionality into core Vagrant [GH-9713]
IMPROVEMENTS:
- core: Improve messaging around not finding requested provider [GH-9735]
- core: Disable exception reports by default [GH-9738]
- core: Continue on if vagrant fails to parse metadata box for update [GH-9760]
- hosts/linux: Support RDP capability within WSL [GH-9758]
- hosts/windows: Add SMB default mount options capability and set default version to 2.0 [GH-9734]
- provider/hyperv: Include neighbor check for MAC on guest IP detection [GH-9737]
- provider/virtualbox: Do not require VirtualBox availability within WSL [GH-9759]
- provisioner/chef_zero: Support arrays for data_bags_path [GH-9669]
- util/downloader: Don't raise error if response is HTTP 416 [GH-9729]
- util/platform: Update Hyper-V enabled check [GH-9746]
BUG FIXES:
- communicators/ssh: Log error and proceed on Windows private key permissions [GH-9769]
- middleware/authentication: Prevent URL modification when no changes are required [GH-9730]
- middleware/authentication: Ignore URLs which cannot be parsed [GH-9739]
- provider/hyperv: Reference switches by ID instead of name [GH-9747]
- provider/docker: Use Util::SafeExec if docker-exec is run with `-t` option [GH-9761]
- provisioner/chef: Trim drive letter from path on Windows [GH-9766]
- provisioner/puppet: Properly finalize structured_facts config option [GH-9720]
- util/platform: Fix original WSL to Windows path for "root" directory [GH-9696]
## 2.0.4 (April 20, 2018)
FEATURES:
- core: Vagrant aliases [GH-9504] - core: Vagrant aliases [GH-9504]
IMPROVEMENTS: IMPROVEMENTS:
- communicators/ssh: Update file permissions when generating new key pairs [GH-9676]
- core: Make resolv-replace usage opt-in instead of opt-out [GH-9644] - core: Make resolv-replace usage opt-in instead of opt-out [GH-9644]
- core: Suppress error messages from checkpoint runs [GH-9645] - core: Suppress error messages from checkpoint runs [GH-9645]
- guests/coreos: Identify operating systems closely related to CoreOS [GH-9600] - guests/coreos: Identify operating systems closely related to CoreOS [GH-9600]
@ -435,22 +13,14 @@ IMPROVEMENTS:
- guests/photon: Less specific string grep to fix PhotonOS 2.0 detection [GH-9528] - guests/photon: Less specific string grep to fix PhotonOS 2.0 detection [GH-9528]
- guests/windows: Fix slow timeout when updating windows hostname [GH-9578] - guests/windows: Fix slow timeout when updating windows hostname [GH-9578]
- hosts/windows: Make powershell version detection timeout configurable [GH-9506] - hosts/windows: Make powershell version detection timeout configurable [GH-9506]
- providers/virtualbox: Improve network collision error message [GH-9685]
- provisioner/chef_solo: Improve Windows drive letter removal hack for remote paths[GH-9490]
- provisioner/chef_zero: File path expand all chef_zero config path options [GH-9690]
- provisioner/puppet: Puppet structured facts toyaml on provisioner [GH-9670] - provisioner/puppet: Puppet structured facts toyaml on provisioner [GH-9670]
- provisioner/salt: Add master_json_config & minion_json_config options [GH-9420]
- util/platform: Warn on ArgumentError exceptions from encoding [GH-9506] - util/platform: Warn on ArgumentError exceptions from encoding [GH-9506]
BUG FIXES: BUG FIXES:
- commands/package: Fix uninitialized constant error [GH-9654] - commands/package: Fix uninitialized constant error [GH-9654]
- communicators/winrm: Fix command filter to properly parse commands [GH-9673]
- hosts/windows: Properly respect the VAGRANT_PREFER_SYSTEM_BIN environment variable [GH-9503] - hosts/windows: Properly respect the VAGRANT_PREFER_SYSTEM_BIN environment variable [GH-9503]
- hosts/windows: Fix virtualbox shared folders path for windows guests [GH-8099]
- guests/freebsd: Fix typo in command that manages configuring networks [GH-9705]
- util/checkpoint_client: Respect VAGRANT_CHECKPOINT_DISABLE environment variable [GH-9659] - util/checkpoint_client: Respect VAGRANT_CHECKPOINT_DISABLE environment variable [GH-9659]
- util/platform: Use `--version` instead of `version` for WSL validation [GH-9674]
## 2.0.3 (March 15, 2018) ## 2.0.3 (March 15, 2018)
@ -552,7 +122,7 @@ BUG FIXES:
- core: Rescue more exceptions when checking if port is open [GH-8517] - core: Rescue more exceptions when checking if port is open [GH-8517]
- guests/solaris11: Inherit from Solaris guest and keep solaris11 specific methods [GH-9034] - guests/solaris11: Inherit from Solaris guest and keep solaris11 specific methods [GH-9034]
- guests/windows: Split out cygwin path helper for msys2/cygwin paths and ensure cygpath exists [GH-8972] - guests/windows: Split out cygwin path helper for msys2/cygwin paths and ensure cygpath exists [GH-8972]
- guests/windows: Specify expected shell when executing on guest (fixes einssh communicator usage) [GH-9012] - guests/windows: Specify expected shell when executing on guest (fixes winssh communicator usage) [GH-9012]
- guests/windows: Include WinSSH Communicator when using insert_public_key [GH-9105] - guests/windows: Include WinSSH Communicator when using insert_public_key [GH-9105]
- hosts/windows: Check for vagrant.exe when validating versions within WSL [GH-9107, GH-8962] - hosts/windows: Check for vagrant.exe when validating versions within WSL [GH-9107, GH-8962]
- providers/docker: Isolate windows check within executor to handle running through VM [GH-8921] - providers/docker: Isolate windows check within executor to handle running through VM [GH-8921]

View File

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

View File

@ -19,8 +19,6 @@ between Windows, Mac OS X, and Linux.
## Quick Start ## Quick Start
Package dependencies: Vagrant requires `bsdtar` to be available on your system PATH to run successfully.
For the quick-start, we'll bring up a development machine on For the quick-start, we'll bring up a development machine on
[VirtualBox](https://www.virtualbox.org/) because it is free and works [VirtualBox](https://www.virtualbox.org/) because it is free and works
on all major platforms. Vagrant can, however, work with almost any on all major platforms. Vagrant can, however, work with almost any
@ -33,11 +31,11 @@ installed. After this,
To build your first virtual environment: To build your first virtual environment:
vagrant init hashicorp/bionic64 vagrant init hashicorp/precise32
vagrant up vagrant up
Note: The above `vagrant up` command will also trigger Vagrant to download the Note: The above `vagrant up` command will also trigger Vagrant to download the
`bionic64` box via the specified URL. Vagrant only does this if it detects that `precise32` box via the specified URL. Vagrant only does this if it detects that
the box doesn't already exist on your system. the box doesn't already exist on your system.
## Getting Started Guide ## Getting Started Guide
@ -45,30 +43,25 @@ the box doesn't already exist on your system.
To learn how to build a fully functional development environment, follow the To learn how to build a fully functional development environment, follow the
[getting started guide](https://www.vagrantup.com/docs/getting-started/index.html). [getting started guide](https://www.vagrantup.com/docs/getting-started/index.html).
## Installing from Source ## Installing the Gem from Git
If you want the bleeding edge version of Vagrant, we try to keep master pretty stable If you want the bleeding edge version of Vagrant, we try to keep master pretty stable
and you're welcome to give it a shot. Please review the installation page [here](https://www.vagrantup.com/docs/installation/source.html). and you're welcome to give it a shot. Please review the installation page [here](https://www.vagrantup.com/docs/installation/source.html).
## Contributing to Vagrant ## Contributing to Vagrant
Once your Vagrant bundle is installed from Git repository, you can run the test suite with: To install Vagrant from source, please [follow the guide in the Wiki](https://github.com/hashicorp/vagrant/wiki/Installing-Vagrant-from-Source).
You can run the test suite with:
bundle exec rake bundle exec rake
This will run the unit test suite, which should come back all green! This will run the unit test suite, which should come back all green! Then you're good to go!
If you are developing Vagrant on a machine that already has a Vagrant package installation present, both will attempt to use the same folder for their configuration (location of this folder depends on system). This can cause errors when Vagrant attempts to load plugins. In this case, override the `VAGRANT_HOME` environment variable for your development version of Vagrant before running any commands, to be some new folder within the project or elsewhere on your machine. For example, in Bash: If you want to run Vagrant without having to install the gem, you may use `bundle exec`,
like so:
export VAGRANT_HOME=~/.vagrant-dev bundle exec vagrant help
You can now run Vagrant commands against the development version:
bundle exec vagrant
Please take time to read the [HashiCorp Community Guidelines](https://www.hashicorp.com/community-guidelines) and the [Vagrant Contributing Guide](https://github.com/hashicorp/vagrant/blob/master/.github/CONTRIBUTING.md).
Then you're good to go!
### Acceptance Tests ### Acceptance Tests

View File

@ -15,19 +15,33 @@ targeted at Vagrant core members who have the ability to cut a release.
$ git push --tags $ git push --tags
``` ```
1. This will automatically trigger an installer creation, upload the artifacts, 1. Trigger an installer creation run within the HashiCorp Bamboo installation.
and publish the release. This will take around 45 minutes.
1. After the release has been published update the `website/config.rb` to point 1. Download all the resulting artifacts into the `pkg/dist` folder relative to
to the latest version, commit, and push. the Vagrant repository on your local machine.
1. Publish the webiste by deleting the `stable-website` branch, recreate the branch, 1. Run `./scripts/sign.sh` with the version that is being created. This must be
and force push. From the `master` branch, run: run from the Vagrant repo root. This will GPG sign and checksum the files.
1. Run the following command to upload the binaries to the releases site:
``` ```
$ git branch -D stable-website $ hc-releases upload pkg/dist
$ git branch -b stable-website ```
$ git push -f origin stable-website
1. Publish the new index files to the releases site:
```
$ hc-releases publish
```
1. Update `website/config.rb` to point to the latest version, commit, and push.
1. Tell HashiBot to deploy in ``#deploys`
```
hashibot deploy vagrant
``` ```
1. Update `version.txt` to append `.dev` and add a new blank entry in the 1. Update `version.txt` to append `.dev` and add a new blank entry in the

39
Vagrantfile vendored
View File

@ -4,13 +4,13 @@
# Ruby, run unit tests, etc. # Ruby, run unit tests, etc.
Vagrant.configure("2") do |config| Vagrant.configure("2") do |config|
config.vm.box = "hashicorp/bionic64" config.vm.box = "hashicorp/precise64"
config.vm.hostname = "vagrant" config.vm.hostname = "vagrant"
config.ssh.shell = "bash -c 'BASH_ENV=/etc/profile exec bash'" config.ssh.shell = "bash -c 'BASH_ENV=/etc/profile exec bash'"
["vmware_desktop", "virtualbox", "hyperv"].each do |provider| ["vmware_fusion", "vmware_workstation", "virtualbox"].each do |provider|
config.vm.provider provider do |v, override| config.vm.provider provider do |v, override|
v.memory = "2048" v.memory = "1024"
end end
end end
@ -29,32 +29,32 @@ $shell = <<-'CONTENTS'
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
MARKER_FILE="/usr/local/etc/vagrant_provision_marker" MARKER_FILE="/usr/local/etc/vagrant_provision_marker"
RUBY_VER_REQ=$(awk '$1 == "s.required_ruby_version" { print $4 }' /vagrant/vagrant.gemspec | tr -d '"') RUBY_VER_REQ=$(awk '$1 == "s.required_ruby_version" { print $4 }' /vagrant/vagrant.gemspec | tr -d '"')
BUNDLER_VER_REQ=$(awk '/s.add_dependency "bundler"/ { print $4 }' /vagrant/vagrant.gemspec | tr -d '"')
# Only provision once # Only provision once
if [ -f "${MARKER_FILE}" ]; then if [ -f "${MARKER_FILE}" ]; then
exit 0 exit 0
fi fi
# Add ubuntu_rvm repo
apt-add-repository -y ppa:rael-gc/rvm
# Update apt # Update apt
apt-get update --quiet apt-get update --quiet
# Add vagrant user to sudo group: # Install basic dependencies
# ubuntu_rvm only adds users in group sudo to group rvm apt-get install -qy build-essential bsdtar curl
usermod -a -G sudo vagrant
# Install basic dependencies and RVM
apt-get install -qy build-essential bsdtar rvm
# Import the mpapis public key to verify downloaded releases # Import the mpapis public key to verify downloaded releases
su -l -c 'gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3' vagrant su -l -c 'gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3' vagrant
# Install next-to-last Ruby that complies with Vagrant's version constraint # Install RVM
RUBY_VER=$(su -l -c 'rvm list known' vagrant | tr '[]-' ' ' | awk "/^ ruby ${RUBY_VER_REQ:0:1}\./ { print \$2 }" | sort -r | sed -n '2p') su -l -c 'curl -sL https://get.rvm.io | bash -s stable' vagrant
su -l -c "rvm install ${RUBY_VER}" vagrant
su -l -c "rvm --default use ${RUBY_VER}" vagrant # Add the vagrant user to the RVM group
#usermod -a -G rvm vagrant
# Install latest Ruby that complies with Vagrant's version constraint
RUBY_VER_LATEST=$(su -l -c 'rvm list known' vagrant | tr '[]-' ' ' | awk "/^ ruby ${RUBY_VER_REQ:0:1}\./ { print \$2 }" | sort | tail -n1)
su -l -c "rvm install ${RUBY_VER_LATEST}" vagrant
su -l -c "rvm --default use ${RUBY_VER_LATEST}" vagrant
# Output the Ruby version (for sanity) # Output the Ruby version (for sanity)
su -l -c 'ruby --version' vagrant su -l -c 'ruby --version' vagrant
@ -63,14 +63,15 @@ su -l -c 'ruby --version' vagrant
apt-get install -qy git apt-get install -qy git
# Upgrade Rubygems # Upgrade Rubygems
su -l -c "rvm ${RUBY_VER} do gem update --system" vagrant su -l -c "rvm ${RUBY_VER_LATEST} do gem update --system" vagrant
# Prepare to run unit tests # Install bundler and prepare to run unit tests
su -l -c "gem install bundler -v ${BUNDLER_VER_REQ}" vagrant
su -l -c 'cd /vagrant; bundle install' vagrant su -l -c 'cd /vagrant; bundle install' vagrant
# Automatically move into the shared folder, but only add the command # Automatically move into the shared folder, but only add the command
# if it's not already there. # if it's not already there.
grep -q 'cd /vagrant' /home/vagrant/.bash_profile 2>/dev/null || echo 'cd /vagrant' >> /home/vagrant/.bash_profile 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 the marker file so we don't do this again
touch ${MARKER_FILE} touch ${MARKER_FILE}

View File

@ -5,11 +5,6 @@
# initializing which have historically resulted in stack traces. # initializing which have historically resulted in stack traces.
Signal.trap("INT") { abort } Signal.trap("INT") { abort }
# Disable exception reporting by default if available
if Thread.respond_to?(:report_on_exception=)
Thread.report_on_exception = false
end
# Split arguments by "--" if its there, we'll recombine them later # Split arguments by "--" if its there, we'll recombine them later
argv = ARGV.dup argv = ARGV.dup
argv_extra = [] argv_extra = []
@ -37,22 +32,14 @@ argv.each_index do |i|
arg = argv[i] arg = argv[i]
if !arg.start_with?("-") if !arg.start_with?("-")
if arg == "box" && argv[i+1] == "list" if ["plugin", "help"].include?(arg) || (arg == "box" && argv[i+1] == "list")
opts[:vagrantfile_name] = "" opts[:vagrantfile_name] = ""
ENV['VAGRANT_NO_PLUGINS'] = "1" ENV['VAGRANT_NO_PLUGINS'] = "1"
end end
# Do not load plugins when performing plugin operations if arg == "plugin" && argv[i+1] != "list"
if arg == "plugin"
if argv.none?{|a| a == "--local" } && !ENV["VAGRANT_LOCAL_PLUGINS_LOAD"]
opts[:vagrantfile_name] = ""
end
ENV['VAGRANT_NO_PLUGINS'] = "1"
# Only initialize plugins when listing installed plugins
if argv[i+1] != "list"
ENV['VAGRANT_DISABLE_PLUGIN_INIT'] = "1" ENV['VAGRANT_DISABLE_PLUGIN_INIT'] = "1"
end end
end
break break
end end
@ -89,7 +76,6 @@ begin
require 'vagrant/bundler' require 'vagrant/bundler'
require 'vagrant/cli' require 'vagrant/cli'
require 'vagrant/util/platform' require 'vagrant/util/platform'
require 'vagrant/util/experimental'
# Schedule the cleanup of things # Schedule the cleanup of things
at_exit(&Vagrant::Bundler.instance.method(:deinit)) at_exit(&Vagrant::Bundler.instance.method(:deinit))
@ -160,23 +146,6 @@ begin
env.ui.warn(I18n.t("vagrant.general.not_in_installer") + "\n", prefix: false) env.ui.warn(I18n.t("vagrant.general.not_in_installer") + "\n", prefix: false)
end end
# Acceptable experimental flag values include:
#
# Unset - Disables experimental features
# 0 - Disables experimental features
# 1 - Enables all features
# String - Enables one or more features, separated by commas
if Vagrant::Util::Experimental.enabled?
experimental = Vagrant::Util::Experimental.features_requested
ui = Vagrant::UI::Prefixed.new(env.ui, "vagrant")
logger.debug("Experimental flag is enabled")
if Vagrant::Util::Experimental.global_enabled?
ui.warn(I18n.t("vagrant.general.experimental.all"), bold: true, prefix: true, channel: :error)
else
ui.warn(I18n.t("vagrant.general.experimental.features", features: experimental.join(", ")), bold: true, prefix: true, channel: :error)
end
end
begin begin
# Execute the CLI interface, and exit with the proper error code # Execute the CLI interface, and exit with the proper error code
exit_status = env.cli(argv) exit_status = env.cli(argv)

View File

@ -53,7 +53,7 @@ __vagrantinvestigate() {
_vagrant() { _vagrant() {
cur="${COMP_WORDS[COMP_CWORD]}" cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}" prev="${COMP_WORDS[COMP_CWORD-1]}"
commands="box cloud connect destroy docker-exec docker-logs docker-run global-status halt help init list-commands login package plugin provision push rdp reload resume rsync rsync-auto share snapshot ssh ssh-config status suspend up version" commands="box connect destroy docker-exec docker-logs docker-run global-status halt help init list-commands login package plugin provision push rdp reload resume rsync rsync-auto share snapshot ssh ssh-config status suspend up version"
if [ $COMP_CWORD == 1 ] if [ $COMP_CWORD == 1 ]
then then
@ -65,7 +65,7 @@ _vagrant() {
then then
case "$prev" in case "$prev" in
"init") "init")
local box_list=$(find "${VAGRANT_HOME:-${HOME}/.vagrant.d}/boxes" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sed -e 's/-VAGRANTSLASH-/\//') local box_list=$(find "${VAGRANT_HOME:-${HOME}/.vagrant.d}/boxes" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
COMPREPLY=($(compgen -W "${box_list}" -- ${cur})) COMPREPLY=($(compgen -W "${box_list}" -- ${cur}))
return 0 return 0
;; ;;
@ -75,29 +75,17 @@ _vagrant() {
then then
local vm_list=$(find "${vagrant_state_file}/machines" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;) local vm_list=$(find "${vagrant_state_file}/machines" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
fi fi
local up_commands="\ local up_commands="--no-provision"
--provision \
--no-provision \
--provision-with \
--destroy-on-error \
--no-destroy-on-error \
--parallel \
--no-parallel
--provider \
--install-provider \
--no-install-provider \
-h \
--help"
COMPREPLY=($(compgen -W "${up_commands} ${vm_list}" -- ${cur})) COMPREPLY=($(compgen -W "${up_commands} ${vm_list}" -- ${cur}))
return 0 return 0
;; ;;
"destroy"|"ssh"|"provision"|"reload"|"halt"|"suspend"|"resume"|"ssh-config") "ssh"|"provision"|"reload"|"halt"|"suspend"|"resume"|"ssh-config")
vagrant_state_file=$(__vagrantinvestigate) || return 1 vagrant_state_file=$(__vagrantinvestigate) || return 1
if [[ -f "${vagrant_state_file}" ]] if [[ -f "${vagrant_state_file}" ]]
then then
running_vm_list=$(grep 'active' "${vagrant_state_file}" | sed -e 's/"active"://' | tr ',' '\n' | cut -d '"' -f 2 | tr '\n' ' ') running_vm_list=$(grep 'active' "${vagrant_state_file}" | sed -e 's/"active"://' | tr ',' '\n' | cut -d '"' -f 2 | tr '\n' ' ')
else else
running_vm_list=$(find "${vagrant_state_file}/machines" -type f -name "id" | awk -F"/" '{print $(NF-2)}') running_vm_list=$(find "${vagrant_state_file}" -type f -name "id" | awk -F"/" '{print $(NF-2)}')
fi fi
COMPREPLY=($(compgen -W "${running_vm_list}" -- ${cur})) COMPREPLY=($(compgen -W "${running_vm_list}" -- ${cur}))
return 0 return 0
@ -107,11 +95,6 @@ _vagrant() {
COMPREPLY=($(compgen -W "${box_commands}" -- ${cur})) COMPREPLY=($(compgen -W "${box_commands}" -- ${cur}))
return 0 return 0
;; ;;
"cloud")
cloud_commands="auth box search provider publish version"
COMPREPLY=($(compgen -W "${cloud_commands}" -- ${cur}))
return 0
;;
"plugin") "plugin")
plugin_commands="install license list uninstall update" plugin_commands="install license list uninstall update"
COMPREPLY=($(compgen -W "${plugin_commands}" -- ${cur})) COMPREPLY=($(compgen -W "${plugin_commands}" -- ${cur}))
@ -149,7 +132,7 @@ _vagrant() {
"box") "box")
case "$prev" in case "$prev" in
"remove"|"repackage") "remove"|"repackage")
local box_list=$(find "${VAGRANT_HOME:-${HOME}/.vagrant.d}/boxes" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sed -e 's/-VAGRANTSLASH-/\//') local box_list=$(find "${VAGRANT_HOME:-${HOME}/.vagrant.d}/boxes" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
COMPREPLY=($(compgen -W "${box_list}" -- ${cur})) COMPREPLY=($(compgen -W "${box_list}" -- ${cur}))
return 0 return 0
;; ;;
@ -157,15 +140,6 @@ _vagrant() {
;; ;;
esac esac
;; ;;
"snapshot")
case "$prev" in
"restore"|"delete")
local snapshot_list=$(vagrant snapshot list)
COMPREPLY=($(compgen -W "${snapshot_list}" -- ${cur}))
return 0
;;
esac
;;
*) *)
;; ;;
esac esac

View File

@ -7,4 +7,4 @@ Cmnd_Alias VAGRANT_SMB_LIST = /usr/sbin/sharing -l
Cmnd_Alias VAGRANT_SMB_PLOAD = /bin/launchctl load -w /System/Library/LaunchDaemons/com.apple.smb.preferences.plist Cmnd_Alias VAGRANT_SMB_PLOAD = /bin/launchctl load -w /System/Library/LaunchDaemons/com.apple.smb.preferences.plist
Cmnd_Alias VAGRANT_SMB_DLOAD = /bin/launchctl load -w /System/Library/LaunchDaemons/com.apple.smbd.plist Cmnd_Alias VAGRANT_SMB_DLOAD = /bin/launchctl load -w /System/Library/LaunchDaemons/com.apple.smbd.plist
Cmnd_Alias VAGRANT_SMB_DSTART = /bin/launchctl start com.apple.smbd Cmnd_Alias VAGRANT_SMB_DSTART = /bin/launchctl start com.apple.smbd
%admin ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD, VAGRANT_EXPORTS_REMOVE, VAGRANT_SMB_ADD, VAGRANT_SMB_REMOVE, VAGRANT_SMB_LIST, VAGRANT_SMB_PLOAD, VAGRANT_SMB_DLOAD, VAGRANT_SMB_DSTART %admin ALL=(root) NOPASSWD: VAGRANT_EXPORTS_ADD, VAGRANT_NFSD, VAGRANT_SMB_ADD, VAGRANT_SMB_REMOVE, VAGRANT_SMB_LIST, VAGRANT_SMB_PLOAD, VAGRANT_SMB_DLOAD VAGRANT_SMB_DSTART

View File

@ -1,18 +1,8 @@
require "log4r"
require "vagrant/util/credential_scrubber"
# Update the default formatter within the log4r library to ensure
# sensitive values are being properly scrubbed from logger data
class Log4r::BasicFormatter
alias_method :vagrant_format_object, :format_object
def format_object(obj)
Vagrant::Util::CredentialScrubber.desensitize(vagrant_format_object(obj))
end
end
require "vagrant/shared_helpers" require "vagrant/shared_helpers"
require "rubygems" require "rubygems"
require "log4r"
require "vagrant/util" require "vagrant/util"
require "vagrant/plugin/manager"
# Enable logging if it is requested. We do this before # Enable logging if it is requested. We do this before
# anything else so that we can setup the output before # anything else so that we can setup the output before
@ -49,16 +39,7 @@ if ENV["VAGRANT_LOG"] && ENV["VAGRANT_LOG"] != ""
# Set the logging level on all "vagrant" namespaced # Set the logging level on all "vagrant" namespaced
# logs as long as we have a valid level. # logs as long as we have a valid level.
if level if level
# NOTE: We must do this little hack to allow logger = Log4r::Logger.new("vagrant")
# rest-client to write using the `<<` operator.
# See https://github.com/rest-client/rest-client/issues/34#issuecomment-290858
# for more information
class VagrantLogger < Log4r::Logger
def << (msg)
debug(msg.strip)
end
end
logger = VagrantLogger.new("vagrant")
logger.outputters = Log4r::Outputter.stderr logger.outputters = Log4r::Outputter.stderr
logger.level = level logger.level = level
base_formatter = Log4r::BasicFormatter.new base_formatter = Log4r::BasicFormatter.new
@ -68,11 +49,6 @@ if ENV["VAGRANT_LOG"] && ENV["VAGRANT_LOG"] != ""
date_pattern: "%F %T" date_pattern: "%F %T"
) )
end end
# Vagrant Cloud gem uses RestClient to make HTTP requests, so
# log them if debug is enabled and use Vagrants logger
require 'rest_client'
RestClient.log = logger
Log4r::Outputter.stderr.formatter = Vagrant::Util::LoggingFormatter.new(base_formatter) Log4r::Outputter.stderr.formatter = Vagrant::Util::LoggingFormatter.new(base_formatter)
logger = nil logger = nil
end end
@ -89,6 +65,12 @@ require 'i18n'
# there are issues with ciphers not being properly loaded. # there are issues with ciphers not being properly loaded.
require 'openssl' require 'openssl'
# If we are on Windows, load in File helpers
if Vagrant::Util::Platform.windows?
require "win32/file"
require "win32/file/security"
end
# Always make the version available # Always make the version available
require 'vagrant/version' require 'vagrant/version'
global_logger = Log4r::Logger.new("vagrant::global") global_logger = Log4r::Logger.new("vagrant::global")
@ -174,9 +156,16 @@ module Vagrant
return true if plugin("2").manager.registered.any? { |p| p.name == name } return true if plugin("2").manager.registered.any? { |p| p.name == name }
end end
# Make the requirement object
version = Gem::Requirement.new([version]) if version
# Now check the plugin gem names # Now check the plugin gem names
require "vagrant/plugin/manager" require "vagrant/plugin/manager"
Plugin::Manager.instance.plugin_installed?(name, version) 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 end
# Returns a superclass to use when creating a plugin for Vagrant. # Returns a superclass to use when creating a plugin for Vagrant.
@ -218,17 +207,6 @@ module Vagrant
puts "be removed in the next version of Vagrant." puts "be removed in the next version of Vagrant."
end end
# This checks if Vagrant is installed in a specific version.
#
# Example:
#
# Vagrant.version?(">= 2.1.0")
#
def self.version?(*requirements)
req = Gem::Requirement.new(*requirements)
req.satisfied_by?(Gem::Version.new(VERSION))
end
# This allows a Vagrantfile to specify the version of Vagrant that is # This allows a Vagrantfile to specify the version of Vagrant that is
# required. You can specify a list of requirements which will all be checked # required. You can specify a list of requirements which will all be checked
# against the running Vagrant version. # against the running Vagrant version.
@ -245,7 +223,8 @@ module Vagrant
logger = Log4r::Logger.new("vagrant::root") logger = Log4r::Logger.new("vagrant::root")
logger.info("Version requirements from Vagrantfile: #{requirements.inspect}") logger.info("Version requirements from Vagrantfile: #{requirements.inspect}")
if version?(*requirements) req = Gem::Requirement.new(*requirements)
if req.satisfied_by?(Gem::Version.new(VERSION))
logger.info(" - Version requirements satisfied!") logger.info(" - Version requirements satisfied!")
return return
end end
@ -289,6 +268,35 @@ else
global_logger.warn("resolv replacement has not been enabled!") global_logger.warn("resolv replacement has not been enabled!")
end end
# Setup the plugin manager and load any defined plugins
require_relative "vagrant/plugin/manager"
plugins = Vagrant::Plugin::Manager.instance.installed_plugins
global_logger.info("Plugins:")
plugins.each do |plugin_name, plugin_info|
installed_version = plugin_info["installed_gem_version"]
version_constraint = plugin_info["gem_version"]
installed_version = 'undefined' if installed_version.to_s.empty?
version_constraint = '> 0' if version_constraint.to_s.empty?
global_logger.info(
" - #{plugin_name} = [installed: " \
"#{installed_version} constraint: " \
"#{version_constraint}]"
)
end
if Vagrant.plugins_init?
begin
Vagrant::Bundler.instance.init!(plugins)
rescue Exception => e
global_logger.error("Plugin initialization error - #{e.class}: #{e}")
e.backtrace.each do |backtrace_line|
global_logger.debug(backtrace_line)
end
raise Vagrant::Errors::PluginInitError, message: e.to_s
end
end
# A lambda that knows how to load plugins from a single directory. # A lambda that knows how to load plugins from a single directory.
plugin_load_proc = lambda do |directory| plugin_load_proc = lambda do |directory|
# We only care about directories # We only care about directories
@ -318,3 +326,45 @@ Vagrant.source_root.join("plugins").children(true).each do |directory|
# Otherwise, attempt to load from sub-directories # Otherwise, attempt to load from sub-directories
directory.children(true).each(&plugin_load_proc) directory.children(true).each(&plugin_load_proc)
end end
# If we have plugins enabled, then load those
if Vagrant.plugins_enabled?
begin
global_logger.info("Loading plugins!")
plugins.each do |plugin_name, plugin_info|
if plugin_info["require"].to_s.empty?
begin
global_logger.debug("Loading plugin `#{plugin_name}` with default require: `#{plugin_name}`")
require plugin_name
rescue LoadError, Gem::LoadError => load_error
if plugin_name.include?("-")
begin
plugin_slash = plugin_name.gsub("-", "/")
global_logger.debug("Failed to load plugin `#{plugin_name}` with default require.")
global_logger.debug("Loading plugin `#{plugin_name}` with slash require: `#{plugin_slash}`")
require plugin_slash
rescue LoadError, Gem::LoadError
global_logger.warn("Failed to load plugin `#{plugin_name}`. Assuming library and moving on.")
end
end
end
else
global_logger.debug("Loading plugin `#{plugin_name}` with custom require: `#{plugin_info["require"]}`")
require plugin_info["require"]
end
global_logger.debug("Successfully loaded plugin `#{plugin_name}`.")
end
if defined?(::Bundler)
global_logger.debug("Bundler detected in use. Loading `:plugins` group.")
::Bundler.require(:plugins)
end
rescue Exception => e
global_logger.error("Plugin loading error: #{e.class} - #{e}")
e.backtrace.each do |backtrace_line|
global_logger.debug(backtrace_line)
end
raise Vagrant::Errors::PluginLoadError, message: e.to_s
end
else
global_logger.debug("Plugin loading is currently disabled.")
end

View File

@ -15,7 +15,6 @@ module Vagrant
autoload :Confirm, "vagrant/action/builtin/confirm" autoload :Confirm, "vagrant/action/builtin/confirm"
autoload :ConfigValidate, "vagrant/action/builtin/config_validate" autoload :ConfigValidate, "vagrant/action/builtin/config_validate"
autoload :DestroyConfirm, "vagrant/action/builtin/destroy_confirm" autoload :DestroyConfirm, "vagrant/action/builtin/destroy_confirm"
autoload :Disk, "vagrant/action/builtin/disk"
autoload :EnvSet, "vagrant/action/builtin/env_set" autoload :EnvSet, "vagrant/action/builtin/env_set"
autoload :GracefulHalt, "vagrant/action/builtin/graceful_halt" autoload :GracefulHalt, "vagrant/action/builtin/graceful_halt"
autoload :HandleBox, "vagrant/action/builtin/handle_box" autoload :HandleBox, "vagrant/action/builtin/handle_box"

View File

@ -1,31 +0,0 @@
module Vagrant
module Action
module Builtin
# This class is intended to be used by the Action::Warden class for executing
# action triggers before any given action.
#
# @param [Symbol] action_name - name to fire trigger on
# @param [Vagrant::Plugin::V2::Triger] triggers - trigger object
class AfterTriggerAction
# @param [Symbol] action_name - The action class name to fire trigger on
# @param [Vagrant::Plugin::V2::Triger] triggers - trigger object
def initialize(app, env, action_name, triggers)
@app = app
@env = env
@triggers = triggers
@action_name = action_name
end
def call(env)
machine = env[:machine]
machine_name = machine.name if machine
@triggers.fire_triggers(@action_name, :after, machine_name, :action) if Vagrant::Util::Experimental.feature_enabled?("typed_triggers");
# Carry on
@app.call(env)
end
end
end
end
end

View File

@ -1,28 +0,0 @@
module Vagrant
module Action
module Builtin
# This class is intended to be used by the Action::Warden class for executing
# action triggers before any given action.
class BeforeTriggerAction
# @param [Symbol] action_name - The action class name to fire trigger on
# @param [Vagrant::Plugin::V2::Triger] triggers - trigger object
def initialize(app, env, action_name, triggers)
@app = app
@env = env
@triggers = triggers
@action_name = action_name
end
def call(env)
machine = env[:machine]
machine_name = machine.name if machine
@triggers.fire_triggers(@action_name, :before, machine_name, :action) if Vagrant::Util::Experimental.feature_enabled?("typed_triggers");
# Carry on
@app.call(env)
end
end
end
end
end

View File

@ -348,16 +348,10 @@ module Vagrant
end end
if opts[:checksum] && opts[:checksum_type] if opts[:checksum] && opts[:checksum_type]
if opts[:checksum].to_s.strip.empty?
@logger.warn("Given checksum is empty, cannot validate checksum for box")
elsif opts[:checksum_type].to_s.strip.empty?
@logger.warn("Given checksum type is empty, cannot validate checksum for box")
else
env[:ui].detail(I18n.t("vagrant.actions.box.add.checksumming")) env[:ui].detail(I18n.t("vagrant.actions.box.add.checksumming"))
validate_checksum( validate_checksum(
opts[:checksum_type], opts[:checksum], box_url) opts[:checksum_type], opts[:checksum], box_url)
end end
end
# Add the box! # Add the box!
box = env[:box_collection].add( box = env[:box_collection].add(
@ -533,11 +527,22 @@ module Vagrant
end end
def validate_checksum(checksum_type, checksum, path) def validate_checksum(checksum_type, checksum, path)
@logger.info("Validating checksum with #{checksum_type}") checksum_klass = case checksum_type.to_sym
when :md5
Digest::MD5
when :sha1
Digest::SHA1
when :sha256
Digest::SHA2
else
raise Errors::BoxChecksumInvalidType,
type: checksum_type.to_s
end
@logger.info("Validating checksum with #{checksum_klass}")
@logger.info("Expected checksum: #{checksum}") @logger.info("Expected checksum: #{checksum}")
actual = FileChecksum.new(path, checksum_type).checksum actual = FileChecksum.new(path, checksum_klass).checksum
@logger.info("Actual checksum: #{actual}")
if actual.casecmp(checksum) != 0 if actual.casecmp(checksum) != 0
raise Errors::BoxChecksumMismatch, raise Errors::BoxChecksumMismatch,
actual: actual, actual: actual,

View File

@ -40,7 +40,6 @@ module Vagrant
# Have download options specified in the environment override # Have download options specified in the environment override
# options specified for the machine. # options specified for the machine.
download_options = { download_options = {
automatic_check: !env[:box_outdated_force],
ca_cert: env[:ca_cert] || machine.config.vm.box_download_ca_cert, ca_cert: env[:ca_cert] || machine.config.vm.box_download_ca_cert,
ca_path: env[:ca_path] || machine.config.vm.box_download_ca_path, ca_path: env[:ca_path] || machine.config.vm.box_download_ca_path,
client_cert: env[:client_cert] || client_cert: env[:client_cert] ||
@ -51,8 +50,7 @@ module Vagrant
env[:ui].output(I18n.t( env[:ui].output(I18n.t(
"vagrant.box_outdated_checking_with_refresh", "vagrant.box_outdated_checking_with_refresh",
name: box.name, name: box.name))
version: box.version))
update = nil update = nil
begin begin
update = box.has_update?(constraints, download_options: download_options) update = box.has_update?(constraints, download_options: download_options)
@ -60,9 +58,6 @@ module Vagrant
env[:ui].warn(I18n.t( env[:ui].warn(I18n.t(
"vagrant.box_outdated_metadata_download_error", "vagrant.box_outdated_metadata_download_error",
message: e.extra_data[:message])) message: e.extra_data[:message]))
rescue Errors::BoxMetadataMalformed => e
@logger.warn(e.to_s)
env[:ui].warn(I18n.t("vagrant.box_malformed_continue_on_update"))
rescue Errors::VagrantError => e rescue Errors::VagrantError => e
raise if !env[:box_outdated_ignore_errors] raise if !env[:box_outdated_ignore_errors]
env[:ui].detail(I18n.t( env[:ui].detail(I18n.t(
@ -70,23 +65,15 @@ module Vagrant
message: e.message)) message: e.message))
end end
env[:box_outdated] = update != nil env[:box_outdated] = update != nil
local_update = check_outdated_local(env) if update
if update && (local_update.nil? || (local_update.version < update[1].version))
env[:ui].warn(I18n.t( env[:ui].warn(I18n.t(
"vagrant.box_outdated_single", "vagrant.box_outdated_single",
name: update[0].name, name: update[0].name,
provider: box.provider, provider: box.provider,
current: box.version, current: box.version,
latest: update[1].version)) latest: update[1].version))
elsif local_update
env[:ui].warn(I18n.t(
"vagrant.box_outdated_local",
name: local_update.name,
old: box.version,
new: local_update.version))
env[:box_outdated] = true
else else
env[:box_outdated] = false check_outdated_local(env)
end end
@app.call(env) @app.call(env)
@ -101,8 +88,19 @@ module Vagrant
version ||= "" version ||= ""
version += "> #{machine.box.version}" version += "> #{machine.box.version}"
env[:box_collection].find( box = env[:box_collection].find(
machine.box.name, machine.box.provider, version) machine.box.name, machine.box.provider, version)
if box
env[:ui].warn(I18n.t(
"vagrant.box_outdated_local",
name: box.name,
old: machine.box.version,
new: box.version))
env[:box_outdated] = true
return
end
env[:box_outdated] = false
end end
end end
end end

View File

@ -99,11 +99,8 @@ module Vagrant
b.use Confirm, message, force_key b.use Confirm, message, force_key
end end
# Keep used boxes, even if "force" is applied
keep_used_boxes = env[:keep_used_boxes]
result = env[:action_runner].run(stack, env) result = env[:action_runner].run(stack, env)
if !result[:result] || keep_used_boxes if !result[:result]
# They said "no", so continue with the next box # They said "no", so continue with the next box
next next
end end

View File

@ -12,7 +12,7 @@ module Vagrant
def call(env) def call(env)
if !env.key?(:config_validate) || env[:config_validate] if !env.key?(:config_validate) || env[:config_validate]
errors = env[:machine].config.validate(env[:machine], env[:ignore_provider]) errors = env[:machine].config.validate(env[:machine])
if errors && !errors.empty? if errors && !errors.empty?
raise Errors::ConfigInvalid, raise Errors::ConfigInvalid,

View File

@ -1,39 +0,0 @@
module Vagrant
module Action
module Builtin
class Disk
def initialize(app, env)
@app = app
@logger = Log4r::Logger.new("vagrant::action::builtin::disk")
end
def call(env)
machine = env[:machine]
defined_disks = get_disks(machine, env)
# Call into providers machine implementation for disk management
if !defined_disks.empty?
if machine.provider.capability?(:configure_disks)
machine.provider.capability(:configure_disks, defined_disks)
else
env[:ui].warn(I18n.t("vagrant.actions.disk.provider_unsupported",
provider: machine.provider_name))
end
end
# Continue On
@app.call(env)
end
def get_disks(machine, env)
return @_disks if @_disks
@_disks = []
@_disks = machine.config.vm.disks
@_disks
end
end
end
end
end

View File

@ -25,114 +25,19 @@ module Vagrant
# Store in the type map so that --provision-with works properly # Store in the type map so that --provision-with works properly
@_provisioner_types[result] = provisioner.type @_provisioner_types[result] = provisioner.type
# Set top level provisioner name to provisioner configs name if top level name not set.
# This is mostly for handling the shell provisioner, if a user has set its name like:
#
# config.vm.provision "shell", name: "my_provisioner"
#
# Where `name` is a shell config option, not a top level provisioner class option
#
# Note: `name` is set to a symbol, since it is converted to one via #Config::VM.provision
provisioner_name = provisioner.name
if !provisioner_name
if provisioner.config.respond_to?(:name) &&
provisioner.config.name
provisioner_name = provisioner.config.name.to_sym
end
else
provisioner_name = provisioner_name.to_sym
end
# Build up the options # Build up the options
options = { options = {
name: provisioner_name, name: provisioner.name,
run: provisioner.run, run: provisioner.run,
before: provisioner.before,
after: provisioner.after,
} }
# Return the result # Return the result
[result, options] [result, options]
end end
@_provisioner_instances = sort_provisioner_instances(@_provisioner_instances)
return @_provisioner_instances.compact return @_provisioner_instances.compact
end end
private
# Sorts provisioners based on order specified with before/after options
#
# @return [Array<Provisioner, Hash>]
def sort_provisioner_instances(pvs)
final_provs = []
root_provs = []
# extract root provisioners
root_provs = pvs.find_all { |_, o| o[:before].nil? && o[:after].nil? }
if root_provs.size == pvs.size
# no dependencies found
return pvs
end
# ensure placeholder variables are Arrays
dep_provs = []
each_provs = []
all_provs = []
# extract dependency provisioners
dep_provs = pvs.find_all { |_, o| o[:before].is_a?(String) || o[:after].is_a?(String) }
# extract each provisioners
each_provs = pvs.find_all { |_,o| o[:before] == :each || o[:after] == :each }
# extract all provisioners
all_provs = pvs.find_all { |_,o| o[:before] == :all || o[:after] == :all }
# insert provisioners in order
final_provs = root_provs
dep_provs.each do |p,options|
idx = 0
if options[:before]
idx = final_provs.index { |_, o| o[:name].to_s == options[:before] }
final_provs.insert(idx, [p, options])
elsif options[:after]
idx = final_provs.index { |_, o| o[:name].to_s == options[:after] }
idx += 1
final_provs.insert(idx, [p, options])
end
end
# Add :each and :all provisioners in reverse to preserve order in Vagrantfile
tmp_final_provs = []
final_provs.each_with_index do |(prv,o), i|
tmp_before = []
tmp_after = []
each_provs.reverse_each do |p, options|
if options[:before]
tmp_before << [p,options]
elsif options[:after]
tmp_after << [p,options]
end
end
tmp_final_provs += tmp_before unless tmp_before.empty?
tmp_final_provs += [[prv,o]]
tmp_final_provs += tmp_after unless tmp_after.empty?
end
final_provs = tmp_final_provs
# Add all to final array
all_provs.reverse_each do |p,options|
if options[:before]
final_provs.insert(0, [p,options])
elsif options[:after]
final_provs.push([p,options])
end
end
return final_provs
end
# This will return a mapping of a provisioner instance to its # This will return a mapping of a provisioner instance to its
# type. # type.
def provisioner_type_map(env) def provisioner_type_map(env)
@ -142,13 +47,6 @@ module Vagrant
# Return the type map # Return the type map
@_provisioner_types @_provisioner_types
end end
# @private
# Reset the cached values for platform. This is not considered a public
# API and should only be used for testing.
def self.reset!
instance_variables.each(&method(:remove_instance_variable))
end
end end
end end
end end

View File

@ -97,14 +97,8 @@ module Vagrant
end end
end end
folder_data = JSON.dump(folders)
# Scrub any register credentials from the synced folders
# configuration data to prevent accidental leakage
folder_data = Util::CredentialScrubber.desensitize(folder_data)
machine.data_dir.join("synced_folders").open("w") do |f| machine.data_dir.join("synced_folders").open("w") do |f|
f.write(folder_data) f.write(JSON.dump(folders))
end end
end end

View File

@ -36,35 +36,17 @@ module Vagrant
# Get the command and wrap it in a login shell # Get the command and wrap it in a login shell
command = ShellQuote.escape(env[:ssh_run_command], "'") command = ShellQuote.escape(env[:ssh_run_command], "'")
command = "#{env[:machine].config.ssh.shell} -c '#{command}'"
if env[:machine].config.vm.communicator == :winssh
shell = env[:machine].config.winssh.shell
else
shell = env[:machine].config.ssh.shell
end
if shell == "cmd"
# Add an extra space to the command so cmd.exe quoting works
# properly
command = "#{shell} /C #{command} "
elsif shell == "powershell"
command = "$ProgressPreference = \"SilentlyContinue\"; #{command}"
command = Base64.strict_encode64(command.encode("UTF-16LE", "UTF-8"))
command = "#{shell} -encodedCommand #{command}"
else
command = "#{shell} -c '#{command}'"
end
# Execute! # Execute!
opts = env[:ssh_opts] || {} opts = env[:ssh_opts] || {}
opts[:extra_args] ||= [] opts[:extra_args] ||= []
# Allow the user to specify a tty or non-tty manually, but if they # Allow the user to specify a tty or non-tty manually, but if they
# don't then we default to a TTY unless they are using WinSSH # don't then we default to a TTY
if !opts[:extra_args].include?("-t") && if !opts[:extra_args].include?("-t") &&
!opts[:extra_args].include?("-T") && !opts[:extra_args].include?("-T") &&
env[:tty] && env[:tty]
env[:machine].config.vm.communicator != :winssh
opts[:extra_args] << "-t" opts[:extra_args] << "-t"
end end

View File

@ -80,10 +80,8 @@ module Vagrant
@app.call(env) @app.call(env)
@env[:ui].info I18n.t("vagrant.actions.general.package.compressing", fullpath: fullpath) @env[:ui].info I18n.t("vagrant.actions.general.package.compressing", fullpath: fullpath)
copy_include_files copy_include_files
setup_private_key setup_private_key
write_metadata_json
compress compress
end end
@ -153,22 +151,6 @@ module Vagrant
end end
end end
# Write the metadata file into the box so that the provider
# can be automatically detected when adding the box
def write_metadata_json
meta_path = File.join(@env["package.directory"], "metadata.json")
return if File.exist?(meta_path)
if @env[:machine] && @env[:machine].provider_name
provider_name = @env[:machine].provider_name
elsif @env[:env] && @env[:env].default_provider
provider_name = @env[:env].default_provider
else
return
end
File.write(meta_path, {provider: provider_name}.to_json)
end
# This will copy the generated private key into the box and use # 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 # it for SSH by default. We have to do this because we now generate
# random keypairs on boot, so packaged boxes would stop working # random keypairs on boot, so packaged boxes would stop working

View File

@ -2,7 +2,6 @@ require 'log4r'
require 'vagrant/action/hook' require 'vagrant/action/hook'
require 'vagrant/util/busy' require 'vagrant/util/busy'
require 'vagrant/util/experimental'
module Vagrant module Vagrant
module Action module Action
@ -34,19 +33,6 @@ module Vagrant
environment.merge!(@lazy_globals.call) if @lazy_globals environment.merge!(@lazy_globals.call) if @lazy_globals
environment.merge!(options || {}) environment.merge!(options || {})
if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
# NOTE: Triggers are initialized later in the Action::Runer because of
# how `@lazy_globals` are evaluated. Rather than trying to guess where
# the `env` is coming from, we can wait until they're merged into a single
# hash above.
env = environment[:env]
machine = environment[:machine]
machine_name = machine.name if machine
ui = Vagrant::UI::Prefixed.new(env.ui, "vagrant")
triggers = Vagrant::Plugin::V2::Trigger.new(env, env.vagrantfile.config.trigger, machine, ui)
end
# Setup the action hooks # Setup the action hooks
hooks = Vagrant.plugin("2").manager.action_hooks(environment[:action_name]) hooks = Vagrant.plugin("2").manager.action_hooks(environment[:action_name])
if !hooks.empty? if !hooks.empty?
@ -66,43 +52,19 @@ module Vagrant
ui = environment[:ui] if environment.key?(:ui) ui = environment[:ui] if environment.key?(:ui)
int_callback = lambda do int_callback = lambda do
if environment[:interrupted] if environment[:interrupted]
if ui ui.error I18n.t("vagrant.actions.runner.exit_immediately") if ui
begin
ui.error I18n.t("vagrant.actions.runner.exit_immediately")
rescue ThreadError
# We're being called in a trap-context. Wrap in a thread.
Thread.new {
ui.error I18n.t("vagrant.actions.runner.exit_immediately")
}.join(THREAD_MAX_JOIN_TIMEOUT)
end
end
abort abort
end end
if ui && !@@reported_interrupt ui.warn I18n.t("vagrant.actions.runner.waiting_cleanup") if ui && !@@reported_interrupt
begin
ui.warn I18n.t("vagrant.actions.runner.waiting_cleanup")
rescue ThreadError
# We're being called in a trap-context. Wrap in a thread.
Thread.new {
ui.warn I18n.t("vagrant.actions.runner.waiting_cleanup")
}.join(THREAD_MAX_JOIN_TIMEOUT)
end
end
environment[:interrupted] = true environment[:interrupted] = true
@@reported_interrupt = true @@reported_interrupt = true
end end
action_name = environment[:action_name]
triggers.fire_triggers(action_name, :before, machine_name, :hook) if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
# We place a process lock around every action that is called # We place a process lock around every action that is called
@logger.info("Running action: #{environment[:action_name]} #{callable_id}") @logger.info("Running action: #{environment[:action_name]} #{callable_id}")
Util::Busy.busy(int_callback) { callable.call(environment) } Util::Busy.busy(int_callback) { callable.call(environment) }
triggers.fire_triggers(action_name, :after, machine_name, :hook) if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
# Return the environment in case there are things in there that # Return the environment in case there are things in there that
# the caller wants to use. # the caller wants to use.
environment environment

View File

@ -1,7 +1,4 @@
require "log4r" require "log4r"
require 'vagrant/util/experimental'
require 'vagrant/action/builtin/before_trigger'
require 'vagrant/action/builtin/after_trigger'
module Vagrant module Vagrant
module Action module Action
@ -19,21 +16,8 @@ module Vagrant
attr_accessor :actions, :stack attr_accessor :actions, :stack
def initialize(actions, env) def initialize(actions, env)
if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
if env[:trigger_env]
@env = env[:trigger_env]
else
@env = env[:env]
end
machine = env[:machine]
machine_name = machine.name if machine
ui = Vagrant::UI::Prefixed.new(@env.ui, "vagrant")
@triggers = Vagrant::Plugin::V2::Trigger.new(@env, @env.vagrantfile.config.trigger, machine, ui)
end
@stack = [] @stack = []
@actions = actions.map { |m| finalize_action(m, env) }.flatten @actions = actions.map { |m| finalize_action(m, env) }
@logger = Log4r::Logger.new("vagrant::action::warden") @logger = Log4r::Logger.new("vagrant::action::warden")
@last_error = nil @last_error = nil
end end
@ -47,16 +31,7 @@ module Vagrant
raise Errors::VagrantInterrupt if env[:interrupted] raise Errors::VagrantInterrupt if env[:interrupted]
action = @actions.shift action = @actions.shift
@logger.info("Calling IN action: #{action}") @logger.info("Calling IN action: #{action}")
if !action.is_a?(Proc) && env[:hook]
hook_name = action.class.name.split("::").last.
gsub(/([a-z])([A-Z])/, '\1_\2').gsub('-', '_').downcase
end
env[:hook].call("before_#{hook_name}".to_sym) if hook_name
@stack.unshift(action).first.call(env) @stack.unshift(action).first.call(env)
env[:hook].call("after_#{hook_name}".to_sym) if hook_name
raise Errors::VagrantInterrupt if env[:interrupted] raise Errors::VagrantInterrupt if env[:interrupted]
@logger.info("Calling OUT action: #{action}") @logger.info("Calling OUT action: #{action}")
rescue SystemExit rescue SystemExit
@ -112,17 +87,7 @@ module Vagrant
if klass.is_a?(Class) if klass.is_a?(Class)
# A action klass which is to be instantiated with the # A action klass which is to be instantiated with the
# app, env, and any arguments given # app, env, and any arguments given
klass.new(self, env, *args, &block)
# We wrap the action class in two Trigger method calls so that
# action triggers can fire before and after each given action in the stack.
klass_name = klass.name
[Vagrant::Action::Builtin::BeforeTriggerAction.new(self, env,
klass_name,
@triggers),
klass.new(self, env, *args, &block),
Vagrant::Action::Builtin::AfterTriggerAction.new(self, env,
klass_name,
@triggers)]
elsif klass.respond_to?(:call) elsif klass.respond_to?(:call)
# Make it a lambda which calls the item then forwards # Make it a lambda which calls the item then forwards
# up the chain # up the chain

View File

@ -71,10 +71,6 @@ module Vagrant
thread = Thread.new do thread = Thread.new do
Thread.current[:error] = nil Thread.current[:error] = nil
# Note that this thread is being used for running
# a batch action
Thread.current[:batch_parallel_action] = par
# Record our pid when we started in order to figure out if # Record our pid when we started in order to figure out if
# we've forked... # we've forked...
start_pid = Process.pid start_pid = Process.pid
@ -164,16 +160,6 @@ module Vagrant
if !errors.empty? if !errors.empty?
raise Errors::BatchMultiError, message: errors.join("\n\n") raise Errors::BatchMultiError, message: errors.join("\n\n")
end end
# Check if any threads set an exit code and exit if found. If
# multiple threads have exit code values set, the first encountered
# will be the value used.
threads.each do |thread|
if thread[:exit_code]
@logger.debug("Found exit code set within batch action thread. Exiting")
Process.exit!(thread[:exit_code])
end
end
end end
end end
end end

View File

@ -16,9 +16,6 @@ module Vagrant
class Box class Box
include Comparable include Comparable
# Number of seconds to wait between checks for box updates
BOX_UPDATE_CHECK_INTERVAL = 3600
# The box name. This is the logical name used when adding the box. # The box name. This is the logical name used when adding the box.
# #
# @return [String] # @return [String]
@ -157,11 +154,6 @@ module Vagrant
raise Errors::BoxUpdateNoMetadata, name: @name raise Errors::BoxUpdateNoMetadata, name: @name
end end
if download_options.delete(:automatic_check) && !automatic_update_check_allowed?
@logger.info("Skipping box update check")
return
end
version += ", " if version version += ", " if version
version ||= "" version ||= ""
version += "> #{@version}" version += "> #{@version}"
@ -172,25 +164,6 @@ module Vagrant
[md, newer, newer.provider(@provider)] [md, newer, newer.provider(@provider)]
end end
# Check if a box update check is allowed. Uses a file
# in the box data directory to track when the last auto
# update check was performed and returns true if the
# BOX_UPDATE_CHECK_INTERVAL has passed.
#
# @return [Boolean]
def automatic_update_check_allowed?
check_path = directory.join("box_update_check")
if check_path.exist?
last_check_span = Time.now.to_i - check_path.mtime.to_i
if last_check_span < BOX_UPDATE_CHECK_INTERVAL
@logger.info("box update check is under the interval threshold")
return false
end
end
FileUtils.touch(check_path)
true
end
# This repackages this box and outputs it to the given path. # This repackages this box and outputs it to the given path.
# #
# @param [Pathname] path The full path (filename included) of where # @param [Pathname] path The full path (filename included) of where

View File

@ -116,7 +116,7 @@ module Vagrant
# Extract the box into a temporary directory. # Extract the box into a temporary directory.
@logger.debug("Unpacking box into temporary directory: #{temp_dir}") @logger.debug("Unpacking box into temporary directory: #{temp_dir}")
result = Util::Subprocess.execute( result = Util::Subprocess.execute(
"bsdtar", "--no-same-owner", "--no-same-permissions", "-v", "-x", "-m", "-s", "|\\\\\|/|", "-C", temp_dir.to_s, "-f", path.to_s) "bsdtar", "-v", "-x", "-m", "-C", temp_dir.to_s, "-f", path.to_s)
if result.exit_code != 0 if result.exit_code != 0
raise Errors::BoxUnpackageFailure, raise Errors::BoxUnpackageFailure,
output: result.stderr.to_s output: result.stderr.to_s
@ -233,23 +233,14 @@ module Vagrant
version = versiondir.basename.to_s version = versiondir.basename.to_s
versiondir.children(true).each do |provider| versiondir.children(true).each do |provider|
# Ensure version of box is correct before continuing
if !Gem::Version.correct?(version)
ui = Vagrant::UI::Prefixed.new(Vagrant::UI::Colored.new, "vagrant")
ui.warn(I18n.t("vagrant.box_version_malformed",
version: version, box_name: box_name))
@logger.debug("Invalid version #{version} for box #{box_name}")
next
end
# Verify this is a potentially valid box. If it looks # Verify this is a potentially valid box. If it looks
# correct enough then include it. # correct enough then include it.
if provider.directory? && provider.join("metadata.json").file? if provider.directory? && provider.join("metadata.json").file?
provider_name = provider.basename.to_s.to_sym provider_name = provider.basename.to_s.to_sym
@logger.debug("Box: #{box_name} (#{provider_name}, #{version})") @logger.debug("Box: #{box_name} (#{provider_name})")
results << [box_name, version, provider_name] results << [box_name, version, provider_name]
else else
@logger.debug("Invalid box #{box_name}, ignoring: #{provider}") @logger.debug("Invalid box, ignoring: #{provider}")
end end
end end
end end

View File

@ -68,26 +68,12 @@ module Vagrant
# Returns all the versions supported by this metadata. These # Returns all the versions supported by this metadata. These
# versions are sorted so the last element of the list is the # versions are sorted so the last element of the list is the
# latest version. Optionally filter versions by a matching # latest version.
# provider.
# #
# @return[Array<String>] # @return[Array<String>]
def versions(**opts) def versions
provider = nil
provider = opts[:provider].to_sym if opts[:provider]
if provider
@version_map.select do |version, raw|
if raw["providers"]
raw["providers"].detect do |p|
p["name"].to_sym == provider
end
end
end.keys.sort.map(&:to_s)
else
@version_map.keys.sort.map(&:to_s) @version_map.keys.sort.map(&:to_s)
end end
end
# Represents a single version within the metadata. # Represents a single version within the metadata.
class Version class Version

View File

@ -24,65 +24,30 @@ module Vagrant
# Default gem repositories # Default gem repositories
DEFAULT_GEM_SOURCES = [ DEFAULT_GEM_SOURCES = [
HASHICORP_GEMSTORE, "https://rubygems.org/".freeze,
"https://rubygems.org/".freeze HASHICORP_GEMSTORE
].freeze ].freeze
def self.instance def self.instance
@bundler ||= self.new @bundler ||= self.new
end end
# @return [Pathname] Global plugin path
attr_reader :plugin_gem_path attr_reader :plugin_gem_path
# @return [Pathname] Vagrant environment specific plugin path
attr_reader :env_plugin_gem_path
def initialize def initialize
@plugin_gem_path = Vagrant.user_data_path.join("gems", RUBY_VERSION).freeze @plugin_gem_path = Vagrant.user_data_path.join("gems", RUBY_VERSION).freeze
@logger = Log4r::Logger.new("vagrant::bundler") @logger = Log4r::Logger.new("vagrant::bundler")
# TODO: Remove fix when https://github.com/rubygems/rubygems/pull/2735
# gets merged and released
#
# Because of a rubygems bug, we need to set the gemrc file path
# through this method rather than relying on the environment varible
# GEMRC. On windows, that path gets split on `:`: and `;`, which means
# the drive letter gets treated as its own path. If that path exists locally,
# (like having a random folder called `c` where the library was invoked),
# it fails thinking the folder `c` is a gemrc file.
gemrc_val = ENV["GEMRC"]
ENV["GEMRC"] = ""
Gem.configuration = Gem::ConfigFile.new(["--config-file", gemrc_val])
ENV["GEMRC"] = gemrc_val
end
# Enable Vagrant environment specific plugins at given data path
#
# @param [Pathname] Path to Vagrant::Environment data directory
# @return [Pathname] Path to environment specific gem directory
def environment_path=(env_data_path)
@env_plugin_gem_path = env_data_path.join("plugins", "gems", RUBY_VERSION).freeze
end end
# Initializes Bundler and the various gem paths so that we can begin # Initializes Bundler and the various gem paths so that we can begin
# loading gems. # loading gems. This must only be called once.
def init!(plugins, repair=false) def init!(plugins, repair=false)
if !@initial_specifications
@initial_specifications = Gem::Specification.find_all{true}
else
Gem::Specification.all = @initial_specifications
Gem::Specification.reset
end
# Add HashiCorp RubyGems source # Add HashiCorp RubyGems source
if !Gem.sources.include?(HASHICORP_GEMSTORE) Gem.sources << HASHICORP_GEMSTORE
sources = [HASHICORP_GEMSTORE] + Gem.sources.sources
Gem.sources.replace(sources)
end
# Generate dependencies for all registered plugins # Generate dependencies for all registered plugins
plugin_deps = plugins.map do |name, info| plugin_deps = plugins.map do |name, info|
Gem::Dependency.new(name, info['installed_gem_version'].to_s.empty? ? '> 0' : info['installed_gem_version']) Gem::Dependency.new(name, info['gem_version'].to_s.empty? ? '> 0' : info['gem_version'])
end end
@logger.debug("Current generated plugin dependency list: #{plugin_deps}") @logger.debug("Current generated plugin dependency list: #{plugin_deps}")
@ -113,7 +78,7 @@ module Vagrant
# Activate the gems # Activate the gems
activate_solution(solution) activate_solution(solution)
full_vagrant_spec_list = @initial_specifications + full_vagrant_spec_list = Gem::Specification.find_all{true} +
solution.map(&:full_spec) solution.map(&:full_spec)
if(defined?(::Bundler)) if(defined?(::Bundler))
@ -126,7 +91,6 @@ module Vagrant
end end
Gem::Specification.reset Gem::Specification.reset
nil
end end
# Removes any temporary files created by init # Removes any temporary files created by init
@ -137,10 +101,9 @@ module Vagrant
# Installs the list of plugins. # Installs the list of plugins.
# #
# @param [Hash] plugins # @param [Hash] plugins
# @param [Boolean] env_local Environment local plugin install
# @return [Array<Gem::Specification>] # @return [Array<Gem::Specification>]
def install(plugins, env_local=false) def install(plugins, local=false)
internal_install(plugins, nil, env_local: env_local) internal_install(plugins, nil, local: local)
end end
# Installs a local '*.gem' file so that Bundler can find it. # Installs a local '*.gem' file so that Bundler can find it.
@ -157,7 +120,7 @@ module Vagrant
} }
} }
@logger.debug("Installing local plugin - #{plugin_info}") @logger.debug("Installing local plugin - #{plugin_info}")
internal_install(plugin_info, nil, env_local: opts[:env_local]) internal_install(plugin_info, {})
plugin_source.spec plugin_source.spec
end end
@ -166,14 +129,14 @@ module Vagrant
# @param [Hash] plugins # @param [Hash] plugins
# @param [Array<String>] specific Specific plugin names to update. If # @param [Array<String>] specific Specific plugin names to update. If
# empty or nil, all plugins will be updated. # empty or nil, all plugins will be updated.
def update(plugins, specific, **opts) def update(plugins, specific)
specific ||= [] specific ||= []
update = opts.merge({gems: specific.empty? ? true : specific}) update = {gems: specific.empty? ? true : specific}
internal_install(plugins, update) internal_install(plugins, update)
end end
# Clean removes any unused gems. # Clean removes any unused gems.
def clean(plugins, **opts) def clean(plugins)
@logger.debug("Cleaning Vagrant plugins of stale gems.") @logger.debug("Cleaning Vagrant plugins of stale gems.")
# Generate dependencies for all registered plugins # Generate dependencies for all registered plugins
plugin_deps = plugins.map do |name, info| plugin_deps = plugins.map do |name, info|
@ -200,13 +163,6 @@ module Vagrant
Gem::Specification.load(spec_path) Gem::Specification.load(spec_path)
end end
# Include environment specific specification if enabled
if env_plugin_gem_path
plugin_specs += Dir.glob(env_plugin_gem_path.join('specifications/*.gemspec').to_s).map do |spec_path|
Gem::Specification.load(spec_path)
end
end
@logger.debug("Generating current plugin state solution set.") @logger.debug("Generating current plugin state solution set.")
# Resolve the request set to ensure proper activation order # Resolve the request set to ensure proper activation order
@ -215,27 +171,11 @@ module Vagrant
solution_full_names = solution_specs.map(&:full_name) solution_full_names = solution_specs.map(&:full_name)
# Find all specs installed to plugins directory that are not # Find all specs installed to plugins directory that are not
# found within the solution set. # found within the solution set
plugin_specs.delete_if do |spec| plugin_specs.delete_if do |spec|
solution_full_names.include?(spec.full_name) solution_full_names.include?(spec.full_name)
end end
if env_plugin_gem_path
# If we are cleaning locally, remove any global specs. If
# not, remove any local specs
if opts[:env_local]
@logger.debug("Removing specifications that are not environment local")
plugin_specs.delete_if do |spec|
spec.full_gem_path.to_s.include?(plugin_gem_path.realpath.to_s)
end
else
@logger.debug("Removing specifications that are environment local")
plugin_specs.delete_if do |spec|
spec.full_gem_path.to_s.include?(env_plugin_gem_path.realpath.to_s)
end
end
end
@logger.debug("Specifications to be removed - #{plugin_specs.map(&:full_name)}") @logger.debug("Specifications to be removed - #{plugin_specs.map(&:full_name)}")
# Now delete all unused specs # Now delete all unused specs
@ -302,19 +242,13 @@ module Vagrant
end end
source_list[name] << source source_list[name] << source
end end
Gem::Dependency.new(name, *gem_version.split(",")) Gem::Dependency.new(name, gem_version)
end end
if Vagrant.strict_dependency_enforcement if Vagrant.strict_dependency_enforcement
@logger.debug("Enabling strict dependency enforcement") @logger.debug("Enabling strict dependency enforcement")
plugin_deps += vagrant_internal_specs.map do |spec| plugin_deps += vagrant_internal_specs.map do |spec|
next if system_plugins.include?(spec.name) next if system_plugins.include?(spec.name)
# If we are not running within the installer and
# we are not within a bundler environment then we
# only want activated specs
if !Vagrant.in_installer? && !Vagrant.in_bundler?
next if !spec.activated?
end
Gem::Dependency.new(spec.name, spec.version) Gem::Dependency.new(spec.name, spec.version)
end.compact end.compact
else else
@ -369,61 +303,28 @@ module Vagrant
solution = request_set.resolve(installer_set) solution = request_set.resolve(installer_set)
activate_solution(solution) activate_solution(solution)
# Remove gems which are already installed
request_set.sorted_requests.delete_if do |activation_req|
rs_spec = activation_req.spec
if vagrant_internal_specs.detect{|ispec| ispec.name == rs_spec.name && ispec.version == rs_spec.version }
@logger.debug("Removing activation request from install. Already installed. (#{rs_spec.spec.full_name})")
true
end
end
@logger.debug("Installing required gems.") @logger.debug("Installing required gems.")
# Install all remote gems into plugin path. Set the installer to ignore dependencies # Install all remote gems into plugin path. Set the installer to ignore dependencies
# as we know the dependencies are satisfied and it will attempt to validate a gem's # as we know the dependencies are satisfied and it will attempt to validate a gem's
# dependencies are satisfied by gems in the install directory (which will likely not # dependencies are satisfied by gems in the install directory (which will likely not
# be true) # be true)
install_path = extra[:env_local] ? env_plugin_gem_path : plugin_gem_path result = request_set.install_into(plugin_gem_path.to_s, true,
result = request_set.install_into(install_path.to_s, true,
ignore_dependencies: true, ignore_dependencies: true,
prerelease: Vagrant.prerelease?, prerelease: Vagrant.prerelease?,
wrappers: true wrappers: true
) )
result = result.map(&:full_spec) result = result.map(&:full_spec)
result.each do |spec|
existing_paths = $LOAD_PATH.find_all{|s| s.include?(spec.full_name) }
if !existing_paths.empty?
@logger.debug("Removing existing LOAD_PATHs for #{spec.full_name} - " +
existing_paths.join(", "))
existing_paths.each{|s| $LOAD_PATH.delete(s) }
end
spec.full_require_paths.each do |r_path|
if !$LOAD_PATH.include?(r_path)
@logger.debug("Adding path to LOAD_PATH - #{r_path}")
$LOAD_PATH.unshift(r_path)
end
end
end
result result
end end
# Generate the composite resolver set totally all of vagrant (builtin + plugin set) # Generate the composite resolver set totally all of vagrant (builtin + plugin set)
def generate_vagrant_set def generate_vagrant_set
sets = [generate_builtin_set, generate_plugin_set] Gem::Resolver.compose_sets(generate_builtin_set, generate_plugin_set)
if env_plugin_gem_path && env_plugin_gem_path.exist?
sets << generate_plugin_set(env_plugin_gem_path)
end
Gem::Resolver.compose_sets(*sets)
end end
# @return [Array<[Gem::Specification]>] spec list # @return [Array<[Gem::Specification, String]>] spec and directory pairs
def vagrant_internal_specs def vagrant_internal_specs
# activate any dependencies up front so we can always
# pin them when resolving
Gem::Specification.find { |s| s.name == "vagrant" && s.activated? }.
runtime_dependencies.each { |d| gem d.name, *d.requirement.as_list }
# discover all the gems we have available
list = {} list = {}
directories = [Gem::Specification.default_specifications_dir] directories = [Gem::Specification.default_specifications_dir]
Gem::Specification.find_all{true}.each do |spec| Gem::Specification.find_all{true}.each do |spec|
@ -477,16 +378,10 @@ module Vagrant
# Generate the plugin resolver set. Optionally provide specification names (short or # Generate the plugin resolver set. Optionally provide specification names (short or
# full) that should be ignored # full) that should be ignored
# def generate_plugin_set(skip=[])
# @param [Pathname] path to plugins
# @param [Array<String>] gems to skip
# @return [PluginSet]
def generate_plugin_set(*args)
plugin_path = args.detect{|i| i.is_a?(Pathname) } || plugin_gem_path
skip = args.detect{|i| i.is_a?(Array) } || []
plugin_set = PluginSet.new plugin_set = PluginSet.new
@logger.debug("Generating new plugin set instance. Skip gems - #{skip}") @logger.debug("Generating new plugin set instance. Skip gems - #{skip}")
Dir.glob(plugin_path.join('specifications/*.gemspec').to_s).each do |spec_path| Dir.glob(plugin_gem_path.join('specifications/*.gemspec').to_s).each do |spec_path|
spec = Gem::Specification.load(spec_path) spec = Gem::Specification.load(spec_path)
desired_spec_path = File.join(spec.gem_dir, "#{spec.name}.gemspec") desired_spec_path = File.join(spec.gem_dir, "#{spec.name}.gemspec")
# Vendor set requires the spec to be within the gem directory. Some gems will package their # Vendor set requires the spec to be within the gem directory. Some gems will package their

View File

@ -1,8 +1,6 @@
require 'log4r' require 'log4r'
require 'optparse' require 'optparse'
require 'vagrant/util/experimental'
module Vagrant module Vagrant
# Manages the command line interface to Vagrant. # Manages the command line interface to Vagrant.
class CLI < Vagrant.plugin("2", :command) class CLI < Vagrant.plugin("2", :command)
@ -13,11 +11,6 @@ module Vagrant
@logger = Log4r::Logger.new("vagrant::cli") @logger = Log4r::Logger.new("vagrant::cli")
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv) @main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
ui = Vagrant::UI::Prefixed.new(env.ui, "vagrant")
@triggers = Vagrant::Plugin::V2::Trigger.new(env, env.vagrantfile.config.trigger, nil, ui)
end
Util::CheckpointClient.instance.setup(env).check Util::CheckpointClient.instance.setup(env).check
@logger.info("CLI: #{@main_args.inspect} #{@sub_command.inspect} #{@sub_args.inspect}") @logger.info("CLI: #{@main_args.inspect} #{@sub_command.inspect} #{@sub_args.inspect}")
end end
@ -30,10 +23,6 @@ module Vagrant
return 0 return 0
end end
if @sub_command == "login"
$stderr.puts "WARNING: This command has been deprecated and aliased to `vagrant cloud auth login`"
end
# If we reached this far then we must have a subcommand. If not, # If we reached this far then we must have a subcommand. If not,
# then we also just print the help and exit. # then we also just print the help and exit.
command_plugin = nil command_plugin = nil
@ -62,9 +51,7 @@ module Vagrant
# Initialize and execute the command class, returning the exit status. # Initialize and execute the command class, returning the exit status.
result = 0 result = 0
begin begin
@triggers.fire_triggers(@sub_command.to_sym, :before, nil, :command) if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
result = command_class.new(@sub_args, @env).execute result = command_class.new(@sub_args, @env).execute
@triggers.fire_triggers(@sub_command.to_sym, :after, nil, :command) if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
rescue Interrupt rescue Interrupt
@env.ui.info(I18n.t("vagrant.cli_interrupt")) @env.ui.info(I18n.t("vagrant.cli_interrupt"))
result = 1 result = 1

View File

@ -129,7 +129,7 @@ module Vagrant
path = "(unknown)" path = "(unknown)"
if e.backtrace && e.backtrace[0] if e.backtrace && e.backtrace[0]
backtrace_tokens = e.backtrace[0].split(":") backtrace_tokens = e.backtrace[0].split(":")
path = e.backtrace.first.slice(0, e.backtrace.first.rindex(':')).rpartition(':').first path = backtrace_tokens[0]
backtrace_tokens.each do |part| backtrace_tokens.each do |part|
if part =~ /\d+/ if part =~ /\d+/
line = part.to_i line = part.to_i

View File

@ -60,18 +60,14 @@ module Vagrant
# #
# @param [Environment] env # @param [Environment] env
# @return [Hash] # @return [Hash]
def validate(machine, ignore_provider=nil) def validate(machine)
# Go through each of the configuration keys and validate # Go through each of the configuration keys and validate
errors = {} errors = {}
@keys.each do |_key, instance| @keys.each do |_key, instance|
if instance.respond_to?(:validate) if instance.respond_to?(:validate)
# Validate this single item, and if we have errors then # Validate this single item, and if we have errors then
# we merge them into our total errors list. # we merge them into our total errors list.
if _key == :vm
result = instance.validate(machine, ignore_provider)
else
result = instance.validate(machine) result = instance.validate(machine)
end
if result && !result.empty? if result && !result.empty?
errors = Util.merge_errors(errors, result) errors = Util.merge_errors(errors, result)
end end

View File

@ -8,7 +8,6 @@ require 'log4r'
require 'vagrant/util/file_mode' require 'vagrant/util/file_mode'
require 'vagrant/util/platform' require 'vagrant/util/platform'
require 'vagrant/util/hash_with_indifferent_access'
require "vagrant/util/silence_warnings" require "vagrant/util/silence_warnings"
require "vagrant/vagrantfile" require "vagrant/vagrantfile"
require "vagrant/version" require "vagrant/version"
@ -147,7 +146,6 @@ module Vagrant
if opts[:local_data_path] if opts[:local_data_path]
@local_data_path = Pathname.new(File.expand_path(opts[:local_data_path], @cwd)) @local_data_path = Pathname.new(File.expand_path(opts[:local_data_path], @cwd))
end end
@logger.debug("Effective local data path: #{@local_data_path}") @logger.debug("Effective local data path: #{@local_data_path}")
# If we have a root path, load the ".vagrantplugins" file. # If we have a root path, load the ".vagrantplugins" file.
@ -165,18 +163,6 @@ module Vagrant
@default_private_key_path = @home_path.join("insecure_private_key") @default_private_key_path = @home_path.join("insecure_private_key")
copy_insecure_private_key copy_insecure_private_key
# Initialize localized plugins
plugins = Vagrant::Plugin::Manager.instance.localize!(self)
# Load any environment local plugins
Vagrant::Plugin::Manager.instance.load_plugins(plugins)
# Initialize globalize plugins
plugins = Vagrant::Plugin::Manager.instance.globalize!
# Load any global plugins
Vagrant::Plugin::Manager.instance.load_plugins(plugins)
plugins = process_configured_plugins
# Call the hooks that does not require configurations to be loaded # Call the hooks that does not require configurations to be loaded
# by using a "clean" action runner # by using a "clean" action runner
hook(:environment_plugins_loaded, runner: Action::Runner.new(env: self)) hook(:environment_plugins_loaded, runner: Action::Runner.new(env: self))
@ -208,8 +194,7 @@ module Vagrant
home_path: home_path, home_path: home_path,
root_path: root_path, root_path: root_path,
tmp_path: tmp_path, tmp_path: tmp_path,
ui: @ui, ui: @ui
env: self
} }
end end
end end
@ -334,7 +319,7 @@ module Vagrant
# then look there. # then look there.
root_config = vagrantfile.config root_config = vagrantfile.config
if opts[:machine] if opts[:machine]
machine_info = vagrantfile.machine_config(opts[:machine], nil, nil, nil) machine_info = vagrantfile.machine_config(opts[:machine], nil, nil)
root_config = machine_info[:config] root_config = machine_info[:config]
end end
@ -905,13 +890,6 @@ module Vagrant
begin begin
@logger.debug("Creating: #{@local_data_path}") @logger.debug("Creating: #{@local_data_path}")
FileUtils.mkdir_p(@local_data_path) FileUtils.mkdir_p(@local_data_path)
# Create the rgloader/loader file so we can use encoded files.
loader_file = @local_data_path.join("rgloader", "loader.rb")
if !loader_file.file?
source_loader = Vagrant.source_root.join("templates/rgloader.rb")
FileUtils.mkdir_p(@local_data_path.join("rgloader").to_s)
FileUtils.cp(source_loader.to_s, loader_file.to_s)
end
rescue Errno::EACCES rescue Errno::EACCES
raise Errors::LocalDataDirectoryNotAccessible, raise Errors::LocalDataDirectoryNotAccessible,
local_data_path: @local_data_path.to_s local_data_path: @local_data_path.to_s
@ -920,114 +898,6 @@ module Vagrant
protected protected
# Attempt to guess the configured provider in use. Will fallback
# to the default provider if an explicit provider name is not
# provided. This can be pretty error prone, but is used during
# initial environment setup to allow loading plugins so it doesn't
# need to be perfect
#
# @return [String]
def guess_provider
gp = nil
ARGV.each_with_index do |val, idx|
if val.start_with?("--provider=")
gp = val.split("=", 2).last
break
elsif val == "--provider"
gp = ARGV[idx+1]
break
end
end
return gp.to_sym if gp
begin
default_provider
rescue Errors::NoDefaultProvider
# if a provider cannot be determined just return nil
nil
end
end
# Load any configuration provided by guests defined within
# the Vagrantfile to pull plugin information they may have
# defined.
def find_configured_plugins
plugins = []
provider = guess_provider
vagrantfile.machine_names.each do |mname|
ldp = @local_data_path.join("machines/#{mname}/#{provider}") if @local_data_path
plugins << vagrantfile.machine_config(mname, guess_provider, boxes, ldp, false)[:config]
end
result = plugins.reverse.inject(Vagrant::Util::HashWithIndifferentAccess.new) do |memo, val|
Vagrant::Util::DeepMerge.deep_merge(memo, val.vagrant.plugins)
end
Vagrant::Util::DeepMerge.deep_merge(result, vagrantfile.config.vagrant.plugins)
end
# Check for any local plugins defined within the Vagrantfile. If
# found, validate they are available. If they are not available,
# request to install them, or raise an exception
#
# @return [Hash] plugin list for loading
def process_configured_plugins
return if !Vagrant.plugins_enabled?
errors = vagrantfile.config.vagrant.validate(nil)
if !errors["vagrant"].empty?
raise Errors::ConfigInvalid,
errors: Util::TemplateRenderer.render(
"config/validation_failed",
errors: errors)
end
# Check if defined plugins are installed
installed = Plugin::Manager.instance.installed_plugins
needs_install = []
config_plugins = find_configured_plugins
config_plugins.each do |name, info|
if !installed[name]
needs_install << name
end
end
if !needs_install.empty?
ui.warn(I18n.t("vagrant.plugins.local.uninstalled_plugins",
plugins: needs_install.sort.join(", ")))
if !Vagrant.auto_install_local_plugins?
answer = nil
until ["y", "n"].include?(answer)
answer = ui.ask(I18n.t("vagrant.plugins.local.request_plugin_install") + " [N]: ")
answer = answer.strip.downcase
answer = "n" if answer.to_s.empty?
end
if answer == "n"
raise Errors::PluginMissingLocalError,
plugins: needs_install.sort.join(", ")
end
end
needs_install.each do |name|
pconfig = Util::HashWithIndifferentAccess.new(config_plugins[name])
ui.info(I18n.t("vagrant.commands.plugin.installing", name: name))
options = {sources: Vagrant::Bundler::DEFAULT_GEM_SOURCES.dup, env_local: true}
options[:sources] = pconfig[:sources] if pconfig[:sources]
options[:require] = pconfig[:entry_point] if pconfig[:entry_point]
options[:version] = pconfig[:version] if pconfig[:version]
spec = Plugin::Manager.instance.install_plugin(name, options)
ui.info(I18n.t("vagrant.commands.plugin.installed",
name: spec.name, version: spec.version.to_s))
end
ui.info("\n")
# Force halt after installation and require command to be run again. This
# will proper load any new locally installed plugins which are now available.
ui.warn(I18n.t("vagrant.plugins.local.install_rerun_command"))
exit(-1)
end
if Vagrant::Plugin::Manager.instance.local_file
Vagrant::Plugin::Manager.instance.local_file.installed_plugins
else
{}
end
end
# This method copies the private key into the home directory if it # This method copies the private key into the home directory if it
# doesn't already exist. # doesn't already exist.
# #

View File

@ -160,10 +160,6 @@ module Vagrant
error_key(:box_config_changing_box) error_key(:box_config_changing_box)
end end
class BoxFileNotExist < VagrantError
error_key(:box_file_not_exist)
end
class BoxMetadataCorrupted < VagrantError class BoxMetadataCorrupted < VagrantError
error_key(:box_metadata_corrupted) error_key(:box_metadata_corrupted)
end end
@ -304,10 +300,6 @@ module Vagrant
error_key(:command_deprecated) error_key(:command_deprecated)
end end
class CommandSuspendAllArgs < VagrantError
error_key(:command_suspend_all_arguments)
end
class CommandUnavailable < VagrantError class CommandUnavailable < VagrantError
error_key(:command_unavailable) error_key(:command_unavailable)
end end
@ -436,10 +428,6 @@ module Vagrant
error_key(:machine_action_locked) error_key(:machine_action_locked)
end end
class MachineFolderNotAccessible < VagrantError
error_key(:machine_folder_not_accessible)
end
class MachineGuestNotReady < VagrantError class MachineGuestNotReady < VagrantError
error_key(:machine_guest_not_ready) error_key(:machine_guest_not_ready)
end end
@ -592,10 +580,6 @@ module Vagrant
error_key(:provider_not_found) error_key(:provider_not_found)
end end
class ProviderNotFoundSuggestion < VagrantError
error_key(:provider_not_found_suggestion)
end
class ProviderNotUsable < VagrantError class ProviderNotUsable < VagrantError
error_key(:provider_not_usable) error_key(:provider_not_usable)
end end
@ -648,14 +632,6 @@ module Vagrant
error_key(:plugin_source_error) error_key(:plugin_source_error)
end end
class PluginNoLocalError < VagrantError
error_key(:plugin_no_local_error)
end
class PluginMissingLocalError < VagrantError
error_key(:plugin_missing_local_error)
end
class PushesNotDefined < VagrantError class PushesNotDefined < VagrantError
error_key(:pushes_not_defined) error_key(:pushes_not_defined)
end end
@ -672,10 +648,6 @@ module Vagrant
error_key(:push_strategy_not_provided) error_key(:push_strategy_not_provided)
end end
class RSyncPostCommandError < VagrantError
error_key(:rsync_post_command_error)
end
class RSyncError < VagrantError class RSyncError < VagrantError
error_key(:rsync_error) error_key(:rsync_error)
end end
@ -804,26 +776,6 @@ module Vagrant
error_key(:synced_folder_unusable) error_key(:synced_folder_unusable)
end end
class TriggersBadExitCodes < VagrantError
error_key(:triggers_bad_exit_codes)
end
class TriggersGuestNotExist < VagrantError
error_key(:triggers_guest_not_exist)
end
class TriggersGuestNotRunning < VagrantError
error_key(:triggers_guest_not_running)
end
class TriggersNoBlockGiven < VagrantError
error_key(:triggers_no_block_given)
end
class TriggersNoStageGiven < VagrantError
error_key(:triggers_no_stage_given)
end
class UIExpectsTTY < VagrantError class UIExpectsTTY < VagrantError
error_key(:ui_expects_tty) error_key(:ui_expects_tty)
end end
@ -832,30 +784,6 @@ module Vagrant
error_key(:unimplemented_provider_action) error_key(:unimplemented_provider_action)
end end
class UploadInvalidCompressionType < VagrantError
error_key(:upload_invalid_compression_type)
end
class UploadMissingExtractCapability < VagrantError
error_key(:upload_missing_extract_capability)
end
class UploadMissingTempCapability < VagrantError
error_key(:upload_missing_temp_capability)
end
class UploadSourceMissing < VagrantError
error_key(:upload_source_missing)
end
class UploaderError < VagrantError
error_key(:uploader_error)
end
class UploaderInterrupted < UploaderError
error_key(:uploader_interrupted)
end
class VagrantInterrupt < VagrantError class VagrantInterrupt < VagrantError
error_key(:interrupted) error_key(:interrupted)
end end
@ -900,6 +828,10 @@ module Vagrant
error_key(:vboxmanage_not_found_error) error_key(:vboxmanage_not_found_error)
end end
class VBoxManageNotFoundWSLError < VagrantError
error_key(:vboxmanage_not_found_wsl_error)
end
class VirtualBoxBrokenVersion040214 < VagrantError class VirtualBoxBrokenVersion040214 < VagrantError
error_key(:virtualbox_broken_version_040214) error_key(:virtualbox_broken_version_040214)
end end
@ -1008,10 +940,6 @@ module Vagrant
error_key(:power_off, "vagrant.actions.vm.export") error_key(:power_off, "vagrant.actions.vm.export")
end end
class WinRMInvalidCommunicator < VagrantError
error_key(:winrm_invalid_communicator)
end
class WSLVagrantVersionMismatch < VagrantError class WSLVagrantVersionMismatch < VagrantError
error_key(:wsl_vagrant_version_mismatch) error_key(:wsl_vagrant_version_mismatch)
end end

View File

@ -110,7 +110,6 @@ module Vagrant
@ui = Vagrant::UI::Prefixed.new(@env.ui, @name) @ui = Vagrant::UI::Prefixed.new(@env.ui, @name)
@ui_mutex = Mutex.new @ui_mutex = Mutex.new
@state_mutex = Mutex.new @state_mutex = Mutex.new
@triggers = Vagrant::Plugin::V2::Trigger.new(@env, @config.trigger, self, @ui)
# Read the ID, which is usually in local storage # Read the ID, which is usually in local storage
@id = nil @id = nil
@ -160,8 +159,6 @@ module Vagrant
# as extra data set on the environment hash for the middleware # as extra data set on the environment hash for the middleware
# runner. # runner.
def action(name, opts=nil) def action(name, opts=nil)
@triggers.fire_triggers(name, :before, @name.to_s, :action)
@logger.info("Calling action: #{name} on provider #{@provider}") @logger.info("Calling action: #{name} on provider #{@provider}")
opts ||= {} opts ||= {}
@ -172,10 +169,6 @@ module Vagrant
# Extra env keys are the remaining opts # Extra env keys are the remaining opts
extra_env = opts.dup extra_env = opts.dup
# An environment is required for triggers to function properly. This is
# passed in specifically for the `#Action::Warden` class triggers. We call it
# `:trigger_env` instead of `env` in case it collides with an existing environment
extra_env[:trigger_env] = @env
check_cwd # Warns the UI if the machine was last used on a different dir check_cwd # Warns the UI if the machine was last used on a different dir
@ -192,7 +185,7 @@ module Vagrant
locker = @env.method(:lock) if lock && !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 # Lock this machine for the duration of this action
return_env = locker.call("machine-action-#{id}") do locker.call("machine-action-#{id}") do
# Get the callable from the provider. # Get the callable from the provider.
callable = @provider.action(name) callable = @provider.action(name)
@ -210,10 +203,6 @@ module Vagrant
ui.machine("action", name.to_s, "end") ui.machine("action", name.to_s, "end")
action_result action_result
end end
@triggers.fire_triggers(name, :after, @name.to_s, :action)
# preserve returning environment after machine action runs
return return_env
rescue Errors::EnvironmentLockedError rescue Errors::EnvironmentLockedError
raise Errors::MachineActionLockedError, raise Errors::MachineActionLockedError,
action: name, action: name,
@ -399,10 +388,7 @@ module Vagrant
# Read the id file from the data directory if it exists as the # Read the id file from the data directory if it exists as the
# ID for the pre-existing physical representation of this machine. # ID for the pre-existing physical representation of this machine.
id_file = @data_dir.join("id") id_file = @data_dir.join("id")
id_content = id_file.read.strip if id_file.file? @id = id_file.read.chomp if id_file.file?
if !id_content.to_s.empty?
@id = id_content
end
end end
if @id != old_id && @provider if @id != old_id && @provider
@ -454,11 +440,9 @@ module Vagrant
info[:keys_only] ||= @config.ssh.default.keys_only info[:keys_only] ||= @config.ssh.default.keys_only
info[:verify_host_key] ||= @config.ssh.default.verify_host_key info[:verify_host_key] ||= @config.ssh.default.verify_host_key
info[:username] ||= @config.ssh.default.username info[:username] ||= @config.ssh.default.username
info[:remote_user] ||= @config.ssh.default.remote_user
info[:compression] ||= @config.ssh.default.compression info[:compression] ||= @config.ssh.default.compression
info[:dsa_authentication] ||= @config.ssh.default.dsa_authentication info[:dsa_authentication] ||= @config.ssh.default.dsa_authentication
info[:extra_args] ||= @config.ssh.default.extra_args info[:extra_args] ||= @config.ssh.default.extra_args
info[:config] ||= @config.ssh.default.config
# We set overrides if they are set. These take precedence over # We set overrides if they are set. These take precedence over
# provider-returned data. # provider-returned data.
@ -470,9 +454,7 @@ module Vagrant
info[:dsa_authentication] = @config.ssh.dsa_authentication info[:dsa_authentication] = @config.ssh.dsa_authentication
info[:username] = @config.ssh.username if @config.ssh.username info[:username] = @config.ssh.username if @config.ssh.username
info[:password] = @config.ssh.password if @config.ssh.password info[:password] = @config.ssh.password if @config.ssh.password
info[:remote_user] = @config.ssh.remote_user if @config.ssh.remote_user
info[:extra_args] = @config.ssh.extra_args if @config.ssh.extra_args info[:extra_args] = @config.ssh.extra_args if @config.ssh.extra_args
info[:config] = @config.ssh.config if @config.ssh.config
# We also set some fields that are purely controlled by Vagrant # We also set some fields that are purely controlled by Vagrant
info[:forward_agent] = @config.ssh.forward_agent info[:forward_agent] = @config.ssh.forward_agent
@ -599,7 +581,7 @@ module Vagrant
).chomp ).chomp
end end
if !File.identical?(vagrant_cwd.to_s, @env.root_path.to_s) if vagrant_cwd != @env.root_path.to_s
if vagrant_cwd if vagrant_cwd
ui.warn(I18n.t( ui.warn(I18n.t(
'vagrant.moved_cwd', 'vagrant.moved_cwd',

View File

@ -27,86 +27,13 @@ module Vagrant
@instance ||= self.new(user_plugins_file) @instance ||= self.new(user_plugins_file)
end end
attr_reader :user_file
attr_reader :system_file
attr_reader :local_file
# @param [Pathname] user_file # @param [Pathname] user_file
def initialize(user_file) def initialize(user_file)
@logger = Log4r::Logger.new("vagrant::plugin::manager")
@user_file = StateFile.new(user_file) @user_file = StateFile.new(user_file)
system_path = self.class.system_plugins_file system_path = self.class.system_plugins_file
@system_file = nil @system_file = nil
@system_file = StateFile.new(system_path) if system_path && system_path.file? @system_file = StateFile.new(system_path) if system_path && system_path.file?
@local_file = nil
@globalized = @localized = false
end
# Enable global plugins
#
# @return [Hash] list of plugins
def globalize!
@globalized = true
@logger.debug("Enabling globalized plugins")
plugins = installed_plugins
bundler_init(plugins)
plugins
end
# Enable environment local plugins
#
# @param [Environment] env Vagrant environment
# @return [Hash, nil] list of plugins
def localize!(env)
@localized = true
if env.local_data_path
@logger.debug("Enabling localized plugins")
@local_file = StateFile.new(env.local_data_path.join("plugins.json"))
Vagrant::Bundler.instance.environment_path = env.local_data_path
plugins = local_file.installed_plugins
bundler_init(plugins)
plugins
end
end
# @return [Boolean] local and global plugins are loaded
def ready?
@globalized && @localized
end
# Initialize bundler with given plugins
#
# @param [Hash] plugins List of plugins
# @return [nil]
def bundler_init(plugins)
if !Vagrant.plugins_init?
@logger.warn("Plugin initialization is disabled")
return nil
end
@logger.info("Plugins:")
plugins.each do |plugin_name, plugin_info|
installed_version = plugin_info["installed_gem_version"]
version_constraint = plugin_info["gem_version"]
installed_version = 'undefined' if installed_version.to_s.empty?
version_constraint = '> 0' if version_constraint.to_s.empty?
@logger.info(
" - #{plugin_name} = [installed: " \
"#{installed_version} constraint: " \
"#{version_constraint}]"
)
end
begin
Vagrant::Bundler.instance.init!(plugins)
rescue StandardError, ScriptError => err
@logger.error("Plugin initialization error - #{err.class}: #{err}")
err.backtrace.each do |backtrace_line|
@logger.debug(backtrace_line)
end
raise Vagrant::Errors::PluginInitError, message: err.to_s
end
end end
# Installs another plugin into our gem directory. # Installs another plugin into our gem directory.
@ -114,10 +41,7 @@ module Vagrant
# @param [String] name Name of the plugin (gem) # @param [String] name Name of the plugin (gem)
# @return [Gem::Specification] # @return [Gem::Specification]
def install_plugin(name, **opts) def install_plugin(name, **opts)
if opts[:env_local] && @local_file.nil? local = false
raise Errors::PluginNoLocalError
end
if name =~ /\.gem$/ if name =~ /\.gem$/
# If this is a gem file, then we install that gem locally. # If this is a gem file, then we install that gem locally.
local_spec = Vagrant::Bundler.instance.install_local(name, opts) local_spec = Vagrant::Bundler.instance.install_local(name, opts)
@ -135,7 +59,7 @@ module Vagrant
if local_spec.nil? if local_spec.nil?
result = nil result = nil
install_lambda = lambda do install_lambda = lambda do
Vagrant::Bundler.instance.install(plugins, opts[:env_local]).each do |spec| Vagrant::Bundler.instance.install(plugins, local).each do |spec|
next if spec.name != name next if spec.name != name
next if result && result.version >= spec.version next if result && result.version >= spec.version
result = spec result = spec
@ -151,20 +75,18 @@ module Vagrant
result = local_spec result = local_spec
end end
# Add the plugin to the state file # Add the plugin to the state file
plugin_file = opts[:env_local] ? @local_file : @user_file @user_file.add_plugin(
plugin_file.add_plugin(
result.name, result.name,
version: opts[:version], version: opts[:version],
require: opts[:require], require: opts[:require],
sources: opts[:sources], sources: opts[:sources],
env_local: !!opts[:env_local],
installed_gem_version: result.version.to_s installed_gem_version: result.version.to_s
) )
# After install clean plugin gems to remove any cruft. This is useful # After install clean plugin gems to remove any cruft. This is useful
# for removing outdated dependencies or other versions of an installed # for removing outdated dependencies or other versions of an installed
# plugin if the plugin is upgraded/downgraded # plugin if the plugin is upgraded/downgraded
Vagrant::Bundler.instance.clean(installed_plugins, local: !!opts[:local]) Vagrant::Bundler.instance.clean(installed_plugins)
result result
rescue Gem::GemNotFoundException rescue Gem::GemNotFoundException
raise Errors::PluginGemNotFound, name: name raise Errors::PluginGemNotFound, name: name
@ -175,7 +97,7 @@ module Vagrant
# Uninstalls the plugin with the given name. # Uninstalls the plugin with the given name.
# #
# @param [String] name # @param [String] name
def uninstall_plugin(name, **opts) def uninstall_plugin(name)
if @system_file if @system_file
if !@user_file.has_plugin?(name) && @system_file.has_plugin?(name) if !@user_file.has_plugin?(name) && @system_file.has_plugin?(name)
raise Errors::PluginUninstallSystem, raise Errors::PluginUninstallSystem,
@ -183,18 +105,7 @@ module Vagrant
end end
end end
if opts[:env_local] && @local_file.nil? @user_file.remove_plugin(name)
raise Errors::PluginNoLocalError
end
plugin_file = opts[:env_local] ? @local_file : @user_file
if !plugin_file.has_plugin?(name)
raise Errors::PluginNotInstalled,
name: name
end
plugin_file.remove_plugin(name)
# Clean the environment, removing any old plugins # Clean the environment, removing any old plugins
Vagrant::Bundler.instance.clean(installed_plugins) Vagrant::Bundler.instance.clean(installed_plugins)
@ -203,15 +114,9 @@ module Vagrant
end end
# Updates all or a specific set of plugins. # Updates all or a specific set of plugins.
def update_plugins(specific, **opts) def update_plugins(specific)
if opts[:env_local] && @local_file.nil? result = Vagrant::Bundler.instance.update(installed_plugins, specific)
raise Errors::PluginNoLocalError installed_plugins.each do |name, info|
end
plugin_file = opts[:env_local] ? @local_file : @user_file
result = Vagrant::Bundler.instance.update(plugin_file.installed_plugins, specific)
plugin_file.installed_plugins.each do |name, info|
matching_spec = result.detect{|s| s.name == name} matching_spec = result.detect{|s| s.name == name}
info = Hash[ info = Hash[
info.map do |key, value| info.map do |key, value|
@ -219,7 +124,7 @@ module Vagrant
end end
] ]
if matching_spec if matching_spec
plugin_file.add_plugin(name, **info.merge( @user_file.add_plugin(name, **info.merge(
version: "> 0", version: "> 0",
installed_gem_version: matching_spec.version.to_s installed_gem_version: matching_spec.version.to_s
)) ))
@ -243,11 +148,6 @@ module Vagrant
end end
plugin_list = Util::DeepMerge.deep_merge(system, @user_file.installed_plugins) plugin_list = Util::DeepMerge.deep_merge(system, @user_file.installed_plugins)
if @local_file
plugin_list = Util::DeepMerge.deep_merge(plugin_list,
@local_file.installed_plugins)
end
# Sort plugins by name # Sort plugins by name
Hash[ Hash[
plugin_list.map{|plugin_name, plugin_info| plugin_list.map{|plugin_name, plugin_info|
@ -291,84 +191,6 @@ module Vagrant
installed_map.values installed_map.values
end end
# Loads the requested plugins into the Vagrant runtime
#
# @param [Hash] plugins List of plugins to load
# @return [nil]
def load_plugins(plugins)
if !Vagrant.plugins_enabled?
@logger.warn("Plugin loading is disabled")
return
end
if plugins.nil?
@logger.debug("No plugins provided for loading")
return
end
begin
@logger.info("Loading plugins...")
plugins.each do |plugin_name, plugin_info|
if plugin_info["require"].to_s.empty?
begin
@logger.info("Loading plugin `#{plugin_name}` with default require: `#{plugin_name}`")
require plugin_name
rescue LoadError => err
if plugin_name.include?("-")
plugin_slash = plugin_name.gsub("-", "/")
@logger.error("Failed to load plugin `#{plugin_name}` with default require. - #{err.class}: #{err}")
@logger.info("Loading plugin `#{plugin_name}` with slash require: `#{plugin_slash}`")
require plugin_slash
else
raise
end
end
else
@logger.debug("Loading plugin `#{plugin_name}` with custom require: `#{plugin_info["require"]}`")
require plugin_info["require"]
end
@logger.debug("Successfully loaded plugin `#{plugin_name}`.")
end
if defined?(::Bundler)
@logger.debug("Bundler detected in use. Loading `:plugins` group.")
::Bundler.require(:plugins)
end
rescue ScriptError, StandardError => err
@logger.error("Plugin loading error: #{err.class} - #{err}")
err.backtrace.each do |backtrace_line|
@logger.debug(backtrace_line)
end
raise Vagrant::Errors::PluginLoadError, message: err.to_s
end
nil
end
# Check if the requested plugin is installed
#
# @param [String] name Name of plugin
# @param [String] version Specific version of the plugin
# @return [Boolean]
def plugin_installed?(name, version=nil)
# Make the requirement object
version = Gem::Requirement.new([version.to_s]) if version
# If plugins are loaded, check for match in loaded specs
if ready?
return installed_specs.any? do |s|
match = s.name == name
next match if !version
next match && version.satisfied_by?(s.version)
end
end
# Plugins are not loaded yet so check installed plugin data
plugin_info = installed_plugins[name]
return false if !plugin_info
return !!plugin_info if version.nil? || plugin_info["installed_gem_version"].nil?
installed_version = Gem::Version.new(plugin_info["installed_gem_version"])
version.satisfied_by?(installed_version)
end
end end
end end
end end

View File

@ -7,10 +7,6 @@ module Vagrant
# This is a helper to deal with the plugin state file that Vagrant # This is a helper to deal with the plugin state file that Vagrant
# uses to track what plugins are installed and activated and such. # uses to track what plugins are installed and activated and such.
class StateFile class StateFile
# @return [Pathname] path to file
attr_reader :path
def initialize(path) def initialize(path)
@path = path @path = path
@ -40,8 +36,7 @@ module Vagrant
"gem_version" => opts[:version] || "", "gem_version" => opts[:version] || "",
"require" => opts[:require] || "", "require" => opts[:require] || "",
"sources" => opts[:sources] || [], "sources" => opts[:sources] || [],
"installed_gem_version" => opts[:installed_gem_version], "installed_gem_version" => opts[:installed_gem_version]
"env_local" => !!opts[:env_local]
} }
save! save!

View File

@ -19,7 +19,6 @@ module Vagrant
autoload :Push, "vagrant/plugin/v2/push" autoload :Push, "vagrant/plugin/v2/push"
autoload :Provisioner, "vagrant/plugin/v2/provisioner" autoload :Provisioner, "vagrant/plugin/v2/provisioner"
autoload :SyncedFolder, "vagrant/plugin/v2/synced_folder" autoload :SyncedFolder, "vagrant/plugin/v2/synced_folder"
autoload :Trigger, "vagrant/plugin/v2/trigger"
end end
end end
end end

View File

@ -117,13 +117,6 @@ module Vagrant
# @see #execute # @see #execute
def test(command, opts=nil) def test(command, opts=nil)
end end
# Reset the communicator. For communicators which establish
# a persistent connection to the remote machine, this connection
# should be terminated and re-established. The communicator
# instance should be in a "fresh" state after calling this method.
def reset!
end
end end
end end
end end

View File

@ -1,325 +0,0 @@
require 'fileutils'
require 'log4r'
require 'shellwords'
require Vagrant.source_root.join("plugins/provisioners/shell/provisioner")
require "vagrant/util/subprocess"
require "vagrant/util/platform"
require "vagrant/util/powershell"
module Vagrant
module Plugin
module V2
class Trigger
# @return [Kernel_V2::Config::Trigger]
attr_reader :config
# This class is responsible for setting up basic triggers that were
# defined inside a Vagrantfile.
#
# @param [Vagrant::Environment] env Vagrant environment
# @param [Kernel_V2::TriggerConfig] config Trigger configuration
# @param [Vagrant::Machine] machine Active Machine
# @param [Vagrant::UI] ui Class for printing messages to user
def initialize(env, config, machine, ui)
@env = env
@config = config
@machine = machine
@ui = ui
@logger = Log4r::Logger.new("vagrant::trigger::#{self.class.to_s.downcase}")
end
# Fires all triggers, if any are defined for the action and guest. Returns early
# and logs a warning if the community plugin `vagrant-triggers` is installed
#
# @param [Symbol] action Vagrant command to fire trigger on
# @param [Symbol] stage :before or :after
# @param [String] guest_name The guest that invoked firing the triggers
def fire_triggers(action, stage, guest_name, type)
if community_plugin_detected?
@logger.warn("Community plugin `vagrant-triggers detected, so core triggers will not fire")
return
end
if !action
@logger.warn("Action given is nil, no triggers will fire")
return
else
action = action.to_sym
end
# get all triggers matching action
triggers = []
if stage == :before
triggers = config.before_triggers.select do |t|
t.command == action || (t.command == :all && !t.ignore.include?(action))
end
elsif stage == :after
triggers = config.after_triggers.select do |t|
t.command == action || (t.command == :all && !t.ignore.include?(action))
end
else
raise Errors::TriggersNoStageGiven,
action: action,
stage: stage,
guest_name: guest_name
end
triggers = filter_triggers(triggers, guest_name, type)
if !triggers.empty?
@logger.info("Firing trigger for action #{action} on guest #{guest_name}")
@ui.info(I18n.t("vagrant.trigger.start", type: type, stage: stage, action: action))
fire(triggers, guest_name)
end
end
protected
#-------------------------------------------------------------------
# Internal methods, don't call these.
#-------------------------------------------------------------------
# Looks up if the community plugin `vagrant-triggers` is installed
# and also caches the result
#
# @return [Boolean]
def community_plugin_detected?
if !defined?(@_triggers_enabled)
plugins = Vagrant::Plugin::Manager.instance.installed_plugins
@_triggers_enabled = plugins.keys.include?("vagrant-triggers")
end
@_triggers_enabled
end
# Filters triggers to be fired based on configured restraints
#
# @param [Array] triggers An array of triggers to be filtered
# @param [String] guest_name The name of the current guest
# @param [Symbol] type The type of trigger (:command or :type)
# @return [Array] The filtered array of triggers
def filter_triggers(triggers, guest_name, type)
# look for only_on trigger constraint and if it doesn't match guest
# name, throw it away also be sure to preserve order
filter = triggers.dup
filter.each do |trigger|
index = nil
match = false
if trigger.only_on
trigger.only_on.each do |o|
if o.match(guest_name.to_s)
# trigger matches on current guest, so we're fine to use it
match = true
break
end
end
# no matches found, so don't use trigger for guest
index = triggers.index(trigger) unless match == true
end
if trigger.type != type
index = triggers.index(trigger)
end
if index
@logger.debug("Trigger #{trigger.id} will be ignored for #{guest_name}")
triggers.delete_at(index)
end
end
return triggers
end
# Fires off all triggers in the given array
#
# @param [Array] triggers An array of triggers to be fired
def fire(triggers, guest_name)
# ensure on_error is respected by exiting or continuing
triggers.each do |trigger|
@logger.debug("Running trigger #{trigger.id}...")
if trigger.name
@ui.info(I18n.t("vagrant.trigger.fire_with_name",
name: trigger.name))
else
@ui.info(I18n.t("vagrant.trigger.fire"))
end
if trigger.info
info(trigger.info)
end
if trigger.warn
warn(trigger.warn)
end
if trigger.abort
trigger_abort(trigger.abort)
end
if trigger.run
run(trigger.run, trigger.on_error, trigger.exit_codes)
end
if trigger.run_remote
run_remote(trigger.run_remote, trigger.on_error, trigger.exit_codes)
end
if trigger.ruby_block
execute_ruby(trigger.ruby_block)
end
end
end
# Prints the given message at info level for a trigger
#
# @param [String] message The string to be printed
def info(message)
@ui.info(message)
end
# Prints the given message at warn level for a trigger
#
# @param [String] message The string to be printed
def warn(message)
@ui.warn(message)
end
# Runs a script on a guest
#
# @param [Provisioners::Shell::Config] config A Shell provisioner config
def run(config, on_error, exit_codes)
if config.inline
if Vagrant::Util::Platform.windows?
cmd = config.inline
else
cmd = Shellwords.split(config.inline)
end
@ui.detail(I18n.t("vagrant.trigger.run.inline", command: config.inline))
else
cmd = File.expand_path(config.path, @env.root_path).shellescape
args = Array(config.args)
cmd << " #{args.join(' ')}" if !args.empty?
cmd = Shellwords.split(cmd)
@ui.detail(I18n.t("vagrant.trigger.run.script", path: config.path))
end
# Pick an execution method to run the script or inline string with
# Default to Subprocess::Execute
exec_method = Vagrant::Util::Subprocess.method(:execute)
if Vagrant::Util::Platform.windows?
if config.inline
exec_method = Vagrant::Util::PowerShell.method(:execute_inline)
else
exec_method = Vagrant::Util::PowerShell.method(:execute)
end
end
begin
result = exec_method.call(*cmd, :notify => [:stdout, :stderr]) do |type,data|
options = {}
case type
when :stdout
options[:color] = :green if !config.keep_color
when :stderr
options[:color] = :red if !config.keep_color
end
@ui.detail(data, options)
end
if !exit_codes.include?(result.exit_code)
raise Errors::TriggersBadExitCodes,
code: result.exit_code
end
rescue => e
@ui.error(I18n.t("vagrant.errors.triggers_run_fail"))
@ui.error(e.message)
if on_error == :halt
@logger.debug("Trigger run encountered an error. Halting on error...")
raise e
else
@logger.debug("Trigger run encountered an error. Continuing on anyway...")
@ui.warn(I18n.t("vagrant.trigger.on_error_continue"))
end
end
end
# Runs a script on the guest
#
# @param [ShellProvisioner/Config] config A Shell provisioner config
def run_remote(config, on_error, exit_codes)
if !@machine
# machine doesn't even exist.
if on_error == :halt
raise Errors::TriggersGuestNotExist
else
@ui.warn(I18n.t("vagrant.errors.triggers_guest_not_exist"))
@ui.warn(I18n.t("vagrant.trigger.on_error_continue"))
return
end
elsif @machine.state.id != :running
if on_error == :halt
raise Errors::TriggersGuestNotRunning,
machine_name: @machine.name,
state: @machine.state.id
else
@machine.ui.error(I18n.t("vagrant.errors.triggers_guest_not_running",
machine_name: @machine.name,
state: @machine.state.id))
@machine.ui.warn(I18n.t("vagrant.trigger.on_error_continue"))
return
end
end
prov = VagrantPlugins::Shell::Provisioner.new(@machine, config)
begin
prov.provision
rescue => e
@machine.ui.error(I18n.t("vagrant.errors.triggers_run_fail"))
if on_error == :halt
@logger.debug("Trigger run encountered an error. Halting on error...")
raise e
else
@logger.debug("Trigger run encountered an error. Continuing on anyway...")
@machine.ui.error(e.message)
end
end
end
# Exits Vagrant immediately
#
# @param [Integer] code Code to exit Vagrant on
def trigger_abort(exit_code)
if Thread.current[:batch_parallel_action]
@ui.warn(I18n.t("vagrant.trigger.abort_threaded"))
@logger.debug("Trigger abort within parallel batch action. " \
"Setting exit code and terminating.")
Thread.current[:exit_code] = exit_code
Thread.current.terminate
else
@ui.warn(I18n.t("vagrant.trigger.abort"))
@logger.debug("Trigger abort within non-parallel action, exiting directly")
Process.exit!(exit_code)
end
end
# Calls the given ruby block for execution
#
# @param [Proc] ruby_block
def execute_ruby(ruby_block)
ruby_block.call(@env, @machine)
end
end
end
end
end

View File

@ -35,14 +35,6 @@ module Vagrant
!!ENV["VAGRANT_INSTALLER_ENV"] !!ENV["VAGRANT_INSTALLER_ENV"]
end end
# This returns a true/false if we are running within a bundler environment
#
# @return [Boolean]
def self.in_bundler?
!!ENV["BUNDLE_GEMFILE"] &&
!defined?(::Bundler).nil?
end
# Returns the path to the embedded directory of the Vagrant installer, # Returns the path to the embedded directory of the Vagrant installer,
# if there is one (if we're running in an installer). # if there is one (if we're running in an installer).
# #
@ -138,18 +130,6 @@ module Vagrant
end end
end end
# Automatically install locally defined plugins instead of
# waiting for user confirmation.
#
# @return [Boolean]
def self.auto_install_local_plugins?
if ENV["VAGRANT_INSTALL_LOCAL_PLUGINS"]
true
else
false
end
end
# Use Ruby Resolv in place of libc # Use Ruby Resolv in place of libc
# #
# @return [boolean] enabled or not # @return [boolean] enabled or not

View File

@ -329,15 +329,10 @@ module Vagrant
target = opts[:target] if opts.key?(:target) target = opts[:target] if opts.key?(:target)
target = "#{target}:" if target != "" target = "#{target}:" if target != ""
# Get the lines. The first default is because if the message
# is an empty string, then we want to still use the empty string.
lines = [message] lines = [message]
if message != "" lines = message.split("\n") if message != ""
lines = [].tap do |l|
message.scan(/(.*?)(\n|$)/).each do |m|
l << m.first if m.first != "" || (m.first == "" && m.last == "\n")
end
end
lines << "" if message.end_with?("\n")
end
# Otherwise, make sure to prefix every line properly # Otherwise, make sure to prefix every line properly
lines.map do |line| lines.map do |line|

View File

@ -162,8 +162,8 @@ module Vagrant
ui = Vagrant::UI::Prefixed.new(env.ui, "vagrant") ui = Vagrant::UI::Prefixed.new(env.ui, "vagrant")
if latest_version > installed_version if latest_version > installed_version
@logger.info("new version of Vagrant available - #{latest_version}") @logger.info("new version of Vagrant available - #{latest_version}")
ui.info(I18n.t("vagrant.version_upgrade_available", latest_version: latest_version, installed_version: installed_version), channel: :error) ui.info(I18n.t("vagrant.version_upgrade_available", latest_version: latest_version))
env.ui.info("", channel: :error) env.ui.info("")
else else
@logger.debug("vagrant is currently up to date") @logger.debug("vagrant is currently up to date")
end end

View File

@ -1,96 +0,0 @@
module Vagrant
module Util
class CurlHelper
# Hosts that do not require notification on redirect
SILENCED_HOSTS = [
"vagrantcloud.com".freeze,
"vagrantup.com".freeze
].freeze
def self.capture_output_proc(logger, ui, source=nil)
progress_data = ""
progress_regexp = /^\r\s*(\d.+?)\r/m
# Setup the proc that'll receive the real-time data from
# the downloader.
data_proc = Proc.new do |type, data|
# Type will always be "stderr" because that is the only
# type of data we're subscribed for notifications.
# Accumulate progress_data
progress_data << data
while true
# If the download has been redirected and we are no longer downloading
# from the original host, notify the user that the target host has
# changed from the source.
if progress_data.include?("Location")
location = progress_data.scan(/(^|[^\w-])Location: (.+?)$/m).flatten.compact.last.to_s.strip
if !location.empty?
location_uri = URI.parse(location)
unless location_uri.host.nil?
redirect_notify = false
logger.info("download redirected to #{location}")
source_uri = URI.parse(source)
source_host = source_uri.host.to_s.split(".", 2).last
location_host = location_uri.host.to_s.split(".", 2).last
if !redirect_notify && location_host != source_host && !SILENCED_HOSTS.include?(location_host)
ui.clear_line
ui.detail "Download redirected to host: #{location_uri.host}"
end
redirect_notify = true
end
end
progress_data.replace("")
break
end
# If we have a full amount of column data (two "\r") then
# we report new progress reports. Otherwise, just keep
# accumulating.
match = nil
check_match = true
while check_match
check_match = progress_regexp.match(progress_data)
if check_match
data = check_match[1].to_s
stop = progress_data.index(data) + data.length
progress_data.slice!(0, stop)
match = check_match
end
end
break if !match
# Ignore the first \r and split by whitespace to grab the columns
columns = data.strip.split(/\s+/)
# COLUMN DATA:
#
# 0 - % total
# 1 - Total size
# 2 - % received
# 3 - Received size
# 4 - % transferred
# 5 - Transferred size
# 6 - Average download speed
# 7 - Average upload speed
# 9 - Total time
# 9 - Time spent
# 10 - Time left
# 11 - Current speed
output = "Progress: #{columns[0]}% (Rate: #{columns[11]}/s, Estimated time remaining: #{columns[10]})"
ui.clear_line
ui.detail(output, new_line: false)
end
end
return data_proc
end
end
end
end

View File

@ -1,14 +1,11 @@
require "uri" require "uri"
require "log4r" require "log4r"
require "digest"
require "digest/md5" require "digest/md5"
require "digest/sha1" require "digest/sha1"
require "vagrant/util/busy" require "vagrant/util/busy"
require "vagrant/util/platform" require "vagrant/util/platform"
require "vagrant/util/subprocess" require "vagrant/util/subprocess"
require "vagrant/util/curl_helper"
require "vagrant/util/file_checksum"
module Vagrant module Vagrant
module Util module Util
@ -20,7 +17,13 @@ module Vagrant
# are properly tracked. # are properly tracked.
# #
# Vagrant/1.7.4 (+https://www.vagrantup.com; ruby2.1.0) # Vagrant/1.7.4 (+https://www.vagrantup.com; ruby2.1.0)
USER_AGENT = "Vagrant/#{VERSION} (+https://www.vagrantup.com; #{RUBY_ENGINE}#{RUBY_VERSION}) #{ENV['VAGRANT_USER_AGENT_PROVISIONAL_STRING']}".freeze USER_AGENT = "Vagrant/#{VERSION} (+https://www.vagrantup.com; #{RUBY_ENGINE}#{RUBY_VERSION})".freeze
# Supported file checksum
CHECKSUM_MAP = {
:md5 => Digest::MD5,
:sha1 => Digest::SHA1
}.freeze
# Hosts that do not require notification on redirect # Hosts that do not require notification on redirect
SILENCED_HOSTS = [ SILENCED_HOSTS = [
@ -64,11 +67,8 @@ module Vagrant
@location_trusted = options[:location_trusted] @location_trusted = options[:location_trusted]
@checksums = { @checksums = {
:md5 => options[:md5], :md5 => options[:md5],
:sha1 => options[:sha1], :sha1 => options[:sha1]
:sha256 => options[:sha256], }
:sha384 => options[:sha384],
:sha512 => options[:sha512]
}.compact
end end
# This executes the actual download, downloading the source file # This executes the actual download, downloading the source file
@ -88,7 +88,82 @@ module Vagrant
# tell us output so we can parse it out. # tell us output so we can parse it out.
extra_subprocess_opts[:notify] = :stderr extra_subprocess_opts[:notify] = :stderr
data_proc = Vagrant::Util::CurlHelper.capture_output_proc(@logger, @ui, @source) progress_data = ""
progress_regexp = /^\r\s*(\d.+?)\r/m
# Setup the proc that'll receive the real-time data from
# the downloader.
data_proc = Proc.new do |type, data|
# Type will always be "stderr" because that is the only
# type of data we're subscribed for notifications.
# Accumulate progress_data
progress_data << data
while true
# If the download has been redirected and we are no longer downloading
# from the original host, notify the user that the target host has
# changed from the source.
if progress_data.include?("Location")
location = progress_data.scan(/Location: (.+?)$/m).flatten.compact.first.to_s.strip
if !location.empty?
@logger.info("download redirected to #{location}")
location_uri = URI.parse(location)
source_uri = URI.parse(source)
source_host = source_uri.host.split(".", 2).last
location_host = location_uri.host.split(".", 2).last
if !@redirect_notify && location_host != source_host && !SILENCED_HOSTS.include?(location_host)
@ui.clear_line
@ui.detail "Download redirected to host: #{location_uri.host}"
end
@redirect_notify = true
end
progress_data.replace("")
break
end
# If we have a full amount of column data (two "\r") then
# we report new progress reports. Otherwise, just keep
# accumulating.
match = nil
check_match = true
while check_match
check_match = progress_regexp.match(progress_data)
if check_match
data = check_match[1].to_s
stop = progress_data.index(data) + data.length
progress_data.slice!(0, stop)
match = check_match
end
end
break if !match
# Ignore the first \r and split by whitespace to grab the columns
columns = data.strip.split(/\s+/)
# COLUMN DATA:
#
# 0 - % total
# 1 - Total size
# 2 - % received
# 3 - Received size
# 4 - % transferred
# 5 - Transferred size
# 6 - Average download speed
# 7 - Average upload speed
# 9 - Total time
# 9 - Time spent
# 10 - Time left
# 11 - Current speed
output = "Progress: #{columns[0]}% (Rate: #{columns[11]}/s, Estimated time remaining: #{columns[10]})"
@ui.clear_line
@ui.detail(output, new_line: false)
end
end
end end
@logger.info("Downloader starting download: ") @logger.info("Downloader starting download: ")
@ -164,23 +239,36 @@ module Vagrant
# @option checksums [String] :sha1 Compare SHA1 checksum # @option checksums [String] :sha1 Compare SHA1 checksum
# @return [Boolean] # @return [Boolean]
def validate_download!(source, path, checksums) def validate_download!(source, path, checksums)
checksums.each do |type, expected| CHECKSUM_MAP.each do |type, klass|
actual = FileChecksum.new(path, type).checksum if checksums[type]
result = checksum_file(klass, path)
@logger.debug("Validating checksum (#{type}) for #{source}. " \ @logger.debug("Validating checksum (#{type}) for #{source}. " \
"expected: #{expected} actual: #{actual}") "expected: #{checksums[type]} actual: #{result}")
if actual.casecmp(expected) != 0 if checksums[type] != result
raise Errors::DownloaderChecksumError.new( raise Errors::DownloaderChecksumError.new(
source: source, source: source,
path: path, path: path,
type: type, type: type,
expected_checksum: expected, expected_checksum: checksums[type],
actual_checksum: actual actual_checksum: result
) )
end end
end end
end
true true
end end
# Generate checksum on given file
#
# @param digest_class [Class] Digest class to use for generating checksum
# @param path [String, Pathname] Path to file
# @return [String] hexdigest result
def checksum_file(digest_class, path)
digester = digest_class.new
digester.file(path)
digester.hexdigest
end
def execute_curl(options, subprocess_options, &data_proc) def execute_curl(options, subprocess_options, &data_proc)
options = options.dup options = options.dup
options << subprocess_options options << subprocess_options
@ -204,21 +292,11 @@ module Vagrant
# show an error message. # show an error message.
if result.exit_code != 0 if result.exit_code != 0
@logger.warn("Downloader exit code: #{result.exit_code}") @logger.warn("Downloader exit code: #{result.exit_code}")
check = result.stderr.match(/\n*curl:\s+\((?<code>\d+)\)\s*(?<error>.*)$/) parts = result.stderr.split(/\n*curl:\s+\(\d+\)\s*/, 2)
if check && check[:code] == "416" parts[1] ||= ""
# All good actually. 416 means there is no more bytes to download
@logger.warn("Downloader got a 416, but is likely fine. Continuing on...")
else
if !check
err_msg = result.stderr
else
err_msg = check[:error]
end
raise Errors::DownloaderError, raise Errors::DownloaderError,
code: result.exit_code, code: result.exit_code,
message: err_msg message: parts[1].chomp
end
end end
result result

View File

@ -1,76 +0,0 @@
module Vagrant
module Util
class Experimental
class << self
# A method for determining if the experimental flag has been enabled with
# any features
#
# @return [Boolean]
def enabled?
if !defined?(@_experimental)
experimental = features_requested
if experimental.size >= 1 && experimental.first != "0"
@_experimental = true
else
@_experimental = false
end
end
@_experimental
end
# A method for determining if all experimental features have been enabled
# by either a global enabled value "1" or all features explicitly enabled.
#
# @return [Boolean]
def global_enabled?
if !defined?(@_global_enabled)
experimental = features_requested
if experimental.size == 1 && experimental.first == "1"
@_global_enabled = true
else
@_global_enabled = false
end
end
@_global_enabled
end
# A method for Vagrant internals to determine if a given feature
# has been abled by the user, is a valid feature flag and can be used.
#
# @param [String] feature
# @return [Boolean] - A hash containing the original array and if it is valid
def feature_enabled?(feature)
experimental = features_requested
feature = feature.to_s
return global_enabled? || experimental.include?(feature)
end
# Returns the features requested for the experimental flag
#
# @return [Array] - Returns an array of requested experimental features
def features_requested
if !defined?(@_requested_features)
@_requested_features = ENV["VAGRANT_EXPERIMENTAL"].to_s.downcase.split(',')
end
@_requested_features
end
# A function to guard experimental blocks of code from being executed
#
# @param [Array] features - Array of features to guard a method with
# @param [Block] block - Block of ruby code to be guarded against
def guard_with(*features, &block)
yield if block_given? && features.any? {|f| feature_enabled?(f)}
end
# @private
# Reset the cached values for platform. This is not considered a public
# API and should only be used for testing.
def reset!
instance_variables.each(&method(:remove_instance_variable))
end
end
end
end
end

View File

@ -2,9 +2,6 @@
# passed into FileChecksum. Note that this isn't strictly enforced at # passed into FileChecksum. Note that this isn't strictly enforced at
# the moment, and this class isn't directly used. It is merely here for # the moment, and this class isn't directly used. It is merely here for
# documentation of structure of the class. # documentation of structure of the class.
require "vagrant/errors"
class DigestClass class DigestClass
def update(string); end def update(string); end
def hexdigest; end def hexdigest; end
@ -13,26 +10,12 @@ end
class FileChecksum class FileChecksum
BUFFER_SIZE = 1024 * 8 BUFFER_SIZE = 1024 * 8
# Supported file checksum
CHECKSUM_MAP = {
:md5 => Digest::MD5,
:sha1 => Digest::SHA1,
:sha256 => Digest::SHA256,
:sha384 => Digest::SHA384,
:sha512 => Digest::SHA512
}.freeze
# Initializes an object to calculate the checksum of a file. The given # Initializes an object to calculate the checksum of a file. The given
# ``digest_klass`` should implement the ``DigestClass`` interface. Note # ``digest_klass`` should implement the ``DigestClass`` interface. Note
# that the built-in Ruby digest classes duck type this properly: # that the built-in Ruby digest classes duck type this properly:
# Digest::MD5, Digest::SHA1, etc. # Digest::MD5, Digest::SHA1, etc.
def initialize(path, digest_klass) def initialize(path, digest_klass)
if digest_klass.is_a?(Class)
@digest_klass = digest_klass @digest_klass = digest_klass
else
@digest_klass = load_digest(digest_klass)
end
@path = path @path = path
end end
@ -57,18 +40,6 @@ class FileChecksum
end end
end end
digest.hexdigest return digest.hexdigest
end
private
def load_digest(type)
digest = CHECKSUM_MAP[type.to_s.to_sym]
if digest.nil?
raise Vagrant::Errors::BoxChecksumInvalidType,
type: type.to_s,
types: CHECKSUM_MAP.keys.join(', ')
end
digest
end end
end end

View File

@ -17,80 +17,44 @@ module Vagrant
# systemd-networkd.service is in use # systemd-networkd.service is in use
# #
# @param [Vagrant::Plugin::V2::Communicator] comm Guest communicator
# @return [Boolean] # @return [Boolean]
def systemd_networkd?(comm) def systemd_networkd?(comm)
comm.test("systemctl -q is-active systemd-networkd.service", sudo: true) comm.test("sudo systemctl status systemd-networkd.service")
end
# Check if a unit file with the given name is defined. Name can
# be a pattern or explicit name.
#
# @param [Vagrant::Plugin::V2::Communicator] comm Guest communicator
# @param [String] name Name or pattern to search
# @return [Boolean]
def systemd_unit_file?(comm, name)
comm.test("systemctl -q list-unit-files | grep \"#{name}\"")
end
# Check if a unit is currently active within systemd
#
# @param [Vagrant::Plugin::V2::Communicator] comm Guest communicator
# @param [String] name Name or pattern to search
# @return [Boolean]
def systemd_unit?(comm, name)
comm.test("systemctl -q list-units | grep \"#{name}\"")
end
# Check if given service is controlled by systemd
#
# @param [Vagrant::Plugin::V2::Communicator] comm Guest communicator
# @param [String] service_name Name of the service to check
# @return [Boolean]
def systemd_controlled?(comm, service_name)
comm.test("systemctl -q is-active #{service_name}", sudo: true)
end end
# systemd hostname set is via hostnamectl # systemd hostname set is via hostnamectl
# #
# @param [Vagrant::Plugin::V2::Communicator] comm Guest communicator
# @return [Boolean] # @return [Boolean]
# NOTE: This test includes actually calling `hostnamectl` to verify
# that it is in working order. This prevents attempts to use the
# hostnamectl command when it is available, but dbus is not which
# renders the command useless
def hostnamectl?(comm) def hostnamectl?(comm)
comm.test("command -v hostnamectl && hostnamectl") comm.test("hostnamectl")
end end
## netplan helpers ## netplan helpers
# netplan is installed # netplan is installed
# #
# @param [Vagrant::Plugin::V2::Communicator] comm Guest communicator
# @return [Boolean] # @return [Boolean]
def netplan?(comm) def netplan?(comm)
comm.test("command -v netplan") comm.test("netplan -h")
end end
## nmcli helpers ## nmcli helpers
# nmcli is installed # nmcli is installed
# #
# @param [Vagrant::Plugin::V2::Communicator] comm Guest communicator
# @return [Boolean] # @return [Boolean]
def nmcli?(comm) def nmcli?(comm)
comm.test("command -v nmcli") comm.test("nmcli")
end end
# NetworkManager currently controls device # NetworkManager currently controls device
# #
# @param [Vagrant::Plugin::V2::Communicator] comm Guest communicator # @param comm [Communicator]
# @param device_name [String] # @param device_name [String]
# @return [Boolean] # @return [Boolean]
def nm_controlled?(comm, device_name) def nm_controlled?(comm, device_name)
comm.test("nmcli -t d show #{device_name}") && comm.test("nmcli d show #{device_name}") &&
!comm.test("nmcli -t d show #{device_name} | grep unmanaged") !comm.test("nmcli d show #{device_name} | grep unmanaged")
end end
end end

View File

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

View File

@ -5,9 +5,34 @@ module Vagrant
module NetworkIP module NetworkIP
# Returns the network address of the given IP and subnet. # Returns the network address of the given IP and subnet.
# #
# If the IP address is an IPv6 address, subnet should be a prefix
# length such as "64".
#
# @return [String] # @return [String]
def network_address(ip, subnet) def network_address(ip, subnet)
IPAddr.new(ip).mask(subnet).to_s # If this is an IPv6 address, then just mask it
if subnet.to_s =~ /^\d+$/
ip = IPAddr.new(ip)
return ip.mask(subnet.to_i).to_s
end
ip = ip_parts(ip)
netmask = ip_parts(subnet)
# Bitwise-AND each octet to get the network address
# in octets and join each part with a period to get
# the resulting network address.
ip.map { |part| part & netmask.shift }.join(".")
end
protected
# Splits an IP into the four octets and returns each as an
# integer in an array.
#
# @return [Array<Integer>]
def ip_parts(ip)
ip.split(".").map { |i| i.to_i }
end end
end end
end end

View File

@ -1,61 +0,0 @@
require "log4r"
module Vagrant
module Util
class Numeric
# Authors Note: This conversion has been borrowed from the ActiveSupport Numeric class
# Conversion helper constants
KILOBYTE = 1024
MEGABYTE = KILOBYTE * 1024
GIGABYTE = MEGABYTE * 1024
TERABYTE = GIGABYTE * 1024
PETABYTE = TERABYTE * 1024
EXABYTE = PETABYTE * 1024
BYTES_CONVERSION_MAP = {KB: KILOBYTE, MB: MEGABYTE, GB: GIGABYTE, TB: TERABYTE,
PB: PETABYTE, EB: EXABYTE}
# Regex borrowed from the vagrant-disksize config class
SHORTHAND_MATCH_REGEX = /^(?<number>[0-9]+)\s?(?<unit>KB|MB|GB|TB)?$/
class << self
LOGGER = Log4r::Logger.new("vagrant::util::numeric")
# A helper that converts a shortcut string to its bytes representation.
# The expected format of `str` is essentially: "<Number>XX"
# Where `XX` is shorthand for KB, MB, GB, TB, PB, or EB. For example, 50 megabytes:
#
# str = "50MB"
#
# @param [String] - str
# @return [Integer,nil] - bytes - returns nil if method fails to convert to bytes
def string_to_bytes(str)
bytes = nil
str = str.to_s.strip
matches = SHORTHAND_MATCH_REGEX.match(str)
if matches
number = matches[:number].to_i
unit = matches[:unit].to_sym
if BYTES_CONVERSION_MAP.key?(unit)
bytes = number * BYTES_CONVERSION_MAP[unit]
else
LOGGER.error("An invalid unit or format was given, string_to_bytes cannot convert #{str}")
end
end
bytes
end
# @private
# Reset the cached values for platform. This is not considered a public
# API and should only be used for testing.
def reset!
instance_variables.each(&method(:remove_instance_variable))
end
end
end
end
end

View File

@ -13,14 +13,6 @@ module Vagrant
# This class just contains some platform checking code. # This class just contains some platform checking code.
class Platform class Platform
class << self class << self
def logger
if !defined?(@_logger)
@_logger = Log4r::Logger.new("vagrant::util::platform")
end
@_logger
end
def cygwin? def cygwin?
if !defined?(@_cygwin) if !defined?(@_cygwin)
@_cygwin = ENV["VAGRANT_DETECTED_OS"].to_s.downcase.include?("cygwin") || @_cygwin = ENV["VAGRANT_DETECTED_OS"].to_s.downcase.include?("cygwin") ||
@ -43,10 +35,11 @@ module Vagrant
if !defined?(@_wsl) if !defined?(@_wsl)
@_wsl = false @_wsl = false
SilenceWarnings.silence! do SilenceWarnings.silence! do
# Find 'microsoft' in /proc/version indicative of WSL # Use PATH values to check for `/mnt/c` path indicative of WSL
if File.file?('/proc/version') if ENV.fetch("PATH", "").downcase.include?("/mnt/c")
osversion = File.open('/proc/version', &:gets) # Validate WSL via uname output
if osversion.downcase.include?("microsoft") uname = Subprocess.execute("uname", "-r")
if uname.exit_code == 0 && uname.stdout.downcase.include?("microsoft")
@_wsl = true @_wsl = true
end end
end end
@ -86,49 +79,24 @@ module Vagrant
return @_windows_admin return @_windows_admin
end end
# Checks if Hyper-V is accessible to the local user. It will check # Checks if the user running Vagrant on Windows is a member of the
# if user is in the "Hyper-V Administrators" group, is a Domain # "Hyper-V Administrators" group.
# administrator, and finally will run a manual interaction with
# Hyper-V to determine if Hyper-V is usable for the current user.
# #
# From: https://support.microsoft.com/en-us/kb/243330 # From: https://support.microsoft.com/en-us/kb/243330
# SID: S-1-5-32-578 # SID: S-1-5-32-578
# Name: BUILTIN\Hyper-V Administrators # Name: BUILTIN\Hyper-V Administrators
# SID: S-1-5-21DOMAIN-512
# Name: Domain Admins
# #
# @return [Boolean] # @return [Boolean]
def windows_hyperv_admin? def windows_hyperv_admin?
return @_windows_hyperv_admin if defined?(@_windows_hyperv_admin) return @_windows_hyperv_admin if defined?(@_windows_hyperv_admin)
if ENV["VAGRANT_IS_HYPERV_ADMIN"] @_windows_hyperv_admin = -> {
return @_windows_hyperv_admin = true ps_cmd = "[System.Security.Principal.WindowsIdentity]::GetCurrent().Groups | ForEach-Object { if ($_.Value -eq 'S-1-5-32-578'){ Write-Host 'true'; break }}"
end
ps_cmd = "Write-Output ([System.Security.Principal.WindowsIdentity]::GetCurrent().Groups | " \
"Select-Object Value | ConvertTo-JSON)"
output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd) output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd)
if output return output == 'true'
groups = begin }.call
JSON.load(output)
rescue JSON::ParserError
[]
end
admin_group = groups.detect do |g|
g["Value"].to_s == "S-1-5-32-578" ||
(g["Value"].start_with?("S-1-5-21") && g["Value"].to_s.end_with?("-512"))
end
if admin_group return @_windows_hyperv_admin
return @_windows_hyperv_admin = true
end
end
ps_cmd = "$x = (Get-VMHost).Name; if($x -eq [System.Net.Dns]::GetHostName()){ Write-Output 'true'}"
output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd)
result = output == "true"
return @_windows_hyperv_admin = result
end end
# Checks if Hyper-V is enabled on the host system and returns true # Checks if Hyper-V is enabled on the host system and returns true
@ -139,17 +107,9 @@ module Vagrant
return @_windows_hyperv_enabled if defined?(@_windows_hyperv_enabled) return @_windows_hyperv_enabled if defined?(@_windows_hyperv_enabled)
@_windows_hyperv_enabled = -> { @_windows_hyperv_enabled = -> {
["Get-WindowsOptionalFeature", "Get-WindowsFeature"].each do |cmd_name| ps_cmd = "$(Get-WindowsOptionalFeature -FeatureName Microsoft-Hyper-V-All -Online).State"
ps_cmd = "$(#{cmd_name} -FeatureName Microsoft-Hyper-V-Hypervisor).State"
begin
output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd) output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd)
return true if output == "Enabled" return output == 'Enabled'
rescue Errors::PowerShellInvalidVersion
logger.warn("Invalid PowerShell version detected during Hyper-V enable check")
return false
end
end
return false
}.call }.call
return @_windows_hyperv_enabled return @_windows_hyperv_enabled
@ -366,12 +326,6 @@ module Vagrant
# Lowercase the drive letter, skip the next symbol (which is a # Lowercase the drive letter, skip the next symbol (which is a
# colon from a Windows path) and convert path to UNIX style. # colon from a Windows path) and convert path to UNIX style.
check_path = "/mnt/#{path[0, 1].downcase}#{path[2..-1].tr('\\', '/')}/rootfs" check_path = "/mnt/#{path[0, 1].downcase}#{path[2..-1].tr('\\', '/')}/rootfs"
begin
process = Subprocess.execute("wslpath", "-u", "-a", path)
check_path = "#{process.stdout.chomp}/rootfs" if process.exit_code == 0
rescue Errors::CommandUnavailable => e
# pass
end
logger.debug("checking `#{path}` for current WSL instance") logger.debug("checking `#{path}` for current WSL instance")
begin begin
@ -435,34 +389,28 @@ module Vagrant
# @param [String, Pathname] path Path to convert # @param [String, Pathname] path Path to convert
# @return [String] # @return [String]
def wsl_to_windows_path(path) def wsl_to_windows_path(path)
path = path.to_s
if wsl? && wsl_windows_access? && !path.match(/^[a-zA-Z]:/) if wsl? && wsl_windows_access? && !path.match(/^[a-zA-Z]:/)
path = File.expand_path(path)
begin
process = Subprocess.execute("wslpath", "-w", "-a", path)
return process.stdout.chomp if process.exit_code == 0
rescue Errors::CommandUnavailable => e
# pass
end
if wsl_path?(path) if wsl_path?(path)
parts = path.split("/") parts = path.split("/")
parts.delete_if(&:empty?) parts.delete_if(&:empty?)
root_path = wsl_rootfs root_path = wsl_rootfs
# lxrun splits home separate so we need to account # lxrun splits home separate so we need to account
# for it's specialness here when we build the path # for it's specialness here when we build the path
if root_path.end_with?("lxss") && !(["root", "home"].include?(parts.first)) if root_path.end_with?("lxss") && parts.first != "home"
root_path = "#{root_path}\\rootfs" root_path = "#{root_path}\\rootfs"
end end
path = [root_path, *parts].join("\\") [root_path, *parts].join("\\")
else else
path = path.sub("/mnt/", "") path = path.to_s.sub("/mnt/", "")
parts = path.split("/") parts = path.split("/")
parts.first << ":" parts.first << ":"
path = parts.join("\\") path = parts.join("\\")
end
end
path path
end end
else
path
end
end
# Takes a windows path and formats it to the # Takes a windows path and formats it to the
# 'unix' style (i.e. `/cygdrive/c` or `/c/`) # 'unix' style (i.e. `/cygdrive/c` or `/c/`)
@ -514,14 +462,6 @@ module Vagrant
def wsl_windows_accessible_path def wsl_windows_accessible_path
if !defined?(@_wsl_windows_accessible_path) if !defined?(@_wsl_windows_accessible_path)
access_path = ENV["VAGRANT_WSL_WINDOWS_ACCESS_USER_HOME_PATH"] access_path = ENV["VAGRANT_WSL_WINDOWS_ACCESS_USER_HOME_PATH"]
if access_path.to_s.empty?
begin
process = Subprocess.execute("wslpath", "-u", "-a", wsl_windows_home)
access_path = process.stdout.chomp if process.exit_code == 0
rescue Errors::CommandUnavailable => e
# pass
end
end
if access_path.to_s.empty? if access_path.to_s.empty?
access_path = wsl_windows_home.gsub("\\", "/").sub(":", "") access_path = wsl_windows_home.gsub("\\", "/").sub(":", "")
access_path[0] = access_path[0].downcase access_path[0] = access_path[0].downcase
@ -541,41 +481,6 @@ module Vagrant
path.to_s.start_with?(wsl_windows_accessible_path.to_s) path.to_s.start_with?(wsl_windows_accessible_path.to_s)
end end
# Mount pattern for extracting local mount information
MOUNT_PATTERN = /^(?<device>.+?) on (?<mount>.+?) type (?<type>.+?) \((?<options>.+)\)/.freeze
# Get list of local mount paths that are DrvFs file systems
#
# @return [Array<String>]
def wsl_drvfs_mounts
if !defined?(@_wsl_drvfs_mounts)
@_wsl_drvfs_mounts = []
if wsl?
result = Util::Subprocess.execute("mount")
result.stdout.each_line do |line|
info = line.match(MOUNT_PATTERN)
if info && info[:type] == "drvfs"
@_wsl_drvfs_mounts << info[:mount]
end
end
end
end
@_wsl_drvfs_mounts
end
# Check if given path is located on DrvFs file system
#
# @param [String, Pathname] path Path to check
# @return [Boolean]
def wsl_drvfs_path?(path)
if wsl?
wsl_drvfs_mounts.each do |mount_path|
return true if path.to_s.start_with?(mount_path)
end
end
false
end
# If running within the Windows Subsystem for Linux, this will provide # If running within the Windows Subsystem for Linux, this will provide
# simple setup to allow sharing of the user's VAGRANT_HOME directory # simple setup to allow sharing of the user's VAGRANT_HOME directory
# within the subsystem # within the subsystem
@ -656,9 +561,9 @@ module Vagrant
def wsl_validate_matching_vagrant_versions! def wsl_validate_matching_vagrant_versions!
valid = false valid = false
if Util::Which.which("vagrant.exe") if Util::Which.which("vagrant.exe")
result = Util::Subprocess.execute("vagrant.exe", "--version") result = Util::Subprocess.execute("vagrant.exe", "version")
if result.exit_code == 0 if result.exit_code == 0
windows_version = result.stdout.match(/Vagrant (?<version>[\w.-]+)/) windows_version = result.stdout.match(/Installed Version: (?<version>[\w.-]+)/)
if windows_version if windows_version
windows_version = windows_version[:version].strip windows_version = windows_version[:version].strip
valid = windows_version == Vagrant::VERSION valid = windows_version == Vagrant::VERSION

View File

@ -1,4 +1,3 @@
require "base64"
require "tmpdir" require "tmpdir"
require_relative "subprocess" require_relative "subprocess"
require_relative "which" require_relative "which"
@ -21,20 +20,12 @@ module Vagrant
if !defined?(@_powershell_executable) if !defined?(@_powershell_executable)
@_powershell_executable = "powershell" @_powershell_executable = "powershell"
if Which.which(@_powershell_executable).nil?
# Try to use WSL interoperability if PowerShell is not symlinked to # Try to use WSL interoperability if PowerShell is not symlinked to
# the container. # the container.
if Platform.wsl? if Which.which(@_powershell_executable).nil? && Platform.wsl?
@_powershell_executable += ".exe" @_powershell_executable += ".exe"
if Which.which(@_powershell_executable).nil? if Which.which(@_powershell_executable).nil?
@_powershell_executable = "/mnt/c/Windows/System32/WindowsPowerShell/v1.0/powershell.exe"
if Which.which(@_powershell_executable).nil?
@_powershell_executable = nil
end
end
else
@_powershell_executable = nil @_powershell_executable = nil
end end
end end
@ -50,30 +41,19 @@ module Vagrant
# Execute a powershell script. # Execute a powershell script.
# #
# @param [String] path Path to the PowerShell script to execute. # @param [String] path Path to the PowerShell script to execute.
# @param [Array<String>] args Command arguments
# @param [Hash] opts Options passed to execute
# @option opts [Hash] :env Custom environment variables
# @return [Subprocess::Result] # @return [Subprocess::Result]
def self.execute(path, *args, **opts, &block) def self.execute(path, *args, **opts, &block)
validate_install! validate_install!
if opts.delete(:sudo) || opts.delete(:runas) if opts.delete(:sudo) || opts.delete(:runas)
powerup_command(path, args, opts) powerup_command(path, args, opts)
else else
if mpath = opts.delete(:module_path)
m_env = opts.fetch(:env, {})
m_env["PSModulePath"] = "$env:PSModulePath+';#{mpath}'"
opts[:env] = m_env
end
if env = opts.delete(:env)
env = env.map{|k,v| "$env:#{k}=#{v}"}.join(";") + "; "
end
command = [ command = [
executable, executable,
"-NoLogo", "-NoLogo",
"-NoProfile", "-NoProfile",
"-NonInteractive", "-NonInteractive",
"-ExecutionPolicy", "Bypass", "-ExecutionPolicy", "Bypass",
"#{env}&('#{path}')", "&('#{path}')",
args args
].flatten ].flatten
@ -88,20 +68,10 @@ module Vagrant
# Execute a powershell command. # Execute a powershell command.
# #
# @param [String] command PowerShell command to execute. # @param [String] command PowerShell command to execute.
# @param [Hash] opts Extra options
# @option opts [Hash] :env Custom environment variables
# @return [nil, String] Returns nil if exit code is non-zero. # @return [nil, String] Returns nil if exit code is non-zero.
# Returns stdout string if exit code is zero. # Returns stdout string if exit code is zero.
def self.execute_cmd(command, **opts) def self.execute_cmd(command)
validate_install! validate_install!
if mpath = opts.delete(:module_path)
m_env = opts.fetch(:env, {})
m_env["PSModulePath"] = "$env:PSModulePath+';#{mpath}'"
opts[:env] = m_env
end
if env = opts.delete(:env)
env = env.map{|k,v| "$env:#{k}=#{v}"}.join(";") + "; "
end
c = [ c = [
executable, executable,
"-NoLogo", "-NoLogo",
@ -109,7 +79,7 @@ module Vagrant
"-NonInteractive", "-NonInteractive",
"-ExecutionPolicy", "Bypass", "-ExecutionPolicy", "Bypass",
"-Command", "-Command",
"#{env}#{command}" command
].flatten.compact ].flatten.compact
r = Subprocess.execute(*c) r = Subprocess.execute(*c)
@ -117,39 +87,6 @@ module Vagrant
return r.stdout.chomp return r.stdout.chomp
end end
# Execute a powershell command and return a result
#
# @param [String] command PowerShell command to execute.
# @param [Hash] opts A collection of options for subprocess::execute
# @option opts [Hash] :env Custom environment variables
# @param [Block] block Ruby block
def self.execute_inline(*command, **opts, &block)
validate_install!
if mpath = opts.delete(:module_path)
m_env = opts.fetch(:env, {})
m_env["PSModulePath"] = "$env:PSModulePath+';#{mpath}'"
opts[:env] = m_env
end
if env = opts.delete(:env)
env = env.map{|k,v| "$env:#{k}=#{v}"}.join(";") + "; "
end
command = command.join(' ')
c = [
executable,
"-NoLogo",
"-NoProfile",
"-NonInteractive",
"-ExecutionPolicy", "Bypass",
"-Command",
"#{env}#{command}"
].flatten.compact
c << opts
Subprocess.execute(*c, &block)
end
# Returns the version of PowerShell that is installed. # Returns the version of PowerShell that is installed.
# #
# @return [String] # @return [String]
@ -209,26 +146,18 @@ module Vagrant
# @return [Array<String>] # @return [Array<String>]
def self.powerup_command(path, args, opts) def self.powerup_command(path, args, opts)
Dir.mktmpdir("vagrant") do |dpath| Dir.mktmpdir("vagrant") do |dpath|
all_args = [path] + args.flatten.map{ |a| all_args = ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", path] + args
a.gsub(/^['"](.+)['"]$/, "\\1") arg_list = "@('" + all_args.join("', '") + "')"
}
arg_list = "\"" + all_args.join("\" \"") + "\""
stdout = File.join(dpath, "stdout.txt") stdout = File.join(dpath, "stdout.txt")
stderr = File.join(dpath, "stderr.txt") stderr = File.join(dpath, "stderr.txt")
exitcode = File.join(dpath, "exitcode.txt")
script = "& #{arg_list} ; exit $LASTEXITCODE;" script = "$sp = Start-Process -FilePath powershell -ArgumentList #{arg_list} " \
script_content = Base64.strict_encode64(script.encode("UTF-16LE", "UTF-8")) "-PassThru -Wait -RedirectStandardOutput '#{stdout}' -RedirectStandardError '#{stderr}' -WindowStyle Hidden; " \
"if($sp){ Set-Content -Path '#{exitcode}' -Value $sp.ExitCode;exit $sp.ExitCode; }else{ exit 1 }"
# Wrap so we can redirect output to read later # escape quotes so we can nest our script within a start-process
wrapper = "$p = Start-Process -FilePath powershell -ArgumentList @('-NoLogo', '-NoProfile', " \ script.gsub!("'", "''")
"'-NonInteractive', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', '#{script_content}') " \
"-PassThru -WindowStyle Hidden -Wait -RedirectStandardOutput '#{stdout}' -RedirectStandardError '#{stderr}'; " \
"if($p){ exit $p.ExitCode; }else{ exit 1 }"
wrapper_content = Base64.strict_encode64(wrapper.encode("UTF-16LE", "UTF-8"))
powerup = "$p = Start-Process -FilePath powershell -ArgumentList @('-NoLogo', '-NoProfile', " \
"'-NonInteractive', '-ExecutionPolicy', 'Bypass', '-EncodedCommand', '#{wrapper_content}') " \
"-PassThru -WindowStyle Hidden -Wait -Verb RunAs; if($p){ exit $p.ExitCode; }else{ exit 1 }"
cmd = [ cmd = [
"powershell", "powershell",
@ -236,20 +165,31 @@ module Vagrant
"-NoProfile", "-NoProfile",
"-NonInteractive", "-NonInteractive",
"-ExecutionPolicy", "Bypass", "-ExecutionPolicy", "Bypass",
"-Command", powerup "-Command", "$p = Start-Process -FilePath powershell -ArgumentList " \
"@('-NoLogo', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Bypass', '-Command', '#{script}') " \
"-PassThru -Wait -WindowStyle Hidden -Verb RunAs; if($p){ exit $p.ExitCode; }else{ exit 1 }"
] ]
result = Subprocess.execute(*cmd.push(opts)) result = Subprocess.execute(*cmd.push(opts))
r_stdout = result.stdout
if File.exist?(stdout) if File.exist?(stdout)
r_stdout += File.read(stdout) r_stdout = File.read(stdout)
else
r_stdout = result.stdout
end end
r_stderr = result.stderr
if File.exist?(stderr) if File.exist?(stderr)
r_stderr += File.read(stderr) r_stderr = File.read(stderr)
else
r_stderr = result.stderr
end end
Subprocess::Result.new(result.exit_code, r_stdout, r_stderr) code = 1
if File.exist?(exitcode)
code_txt = File.read(exitcode).strip
if code_txt.match(/^\d+$/)
code = code_txt.to_i
end
end
Subprocess::Result.new(code, r_stdout, r_stderr)
end end
end end

View File

@ -140,7 +140,7 @@ module Vagrant
end end
# no strict hostkey checking unless paranoid # no strict hostkey checking unless paranoid
if ssh_info[:verify_host_key] == :never || !ssh_info[:verify_host_key] if ! ssh_info[:verify_host_key]
command_options += [ command_options += [
"-o", "StrictHostKeyChecking=no", "-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null"] "-o", "UserKnownHostsFile=/dev/null"]
@ -150,24 +150,11 @@ module Vagrant
if !plain_mode && options[:private_key_path] if !plain_mode && options[:private_key_path]
options[:private_key_path].each do |path| options[:private_key_path].each do |path|
private_key_arr = []
if path.include?('%')
if path.include?(' ') && Platform.windows?
LOGGER.warn("Paths with spaces and % on windows is not supported and will fail to read the file")
end
# Use '-o' instead of '-i' because '-i' does not call # Use '-o' instead of '-i' because '-i' does not call
# percent_expand in misc.c, but '-o' does. when passing the path, # percent_expand in misc.c, but '-o' does. when passing the path,
# replace '%' in the path with '%%' to escape the '%' # replace '%' in the path with '%%' to escape the '%'
path = path.to_s.gsub('%', '%%') path = path.to_s.gsub('%', '%%')
private_key_arr = ["-o", "IdentityFile=\"#{path}\""] command_options += ["-o", "IdentityFile=\"#{path}\""]
else
# Pass private key file directly with '-i', which properly supports
# paths with spaces on Windows guests
private_key_arr = ["-i", path]
end
command_options += private_key_arr
end end
end end
@ -178,10 +165,6 @@ module Vagrant
"-o", "ForwardX11Trusted=yes"] "-o", "ForwardX11Trusted=yes"]
end end
if ssh_info[:config]
command_options += ["-F", ssh_info[:config]]
end
if ssh_info[:proxy_command] if ssh_info[:proxy_command]
command_options += ["-o", "ProxyCommand=#{ssh_info[:proxy_command]}"] command_options += ["-o", "ProxyCommand=#{ssh_info[:proxy_command]}"]
end end

View File

@ -93,7 +93,7 @@ module Vagrant
# Special installer-related things # Special installer-related things
if Vagrant.in_installer? if Vagrant.in_installer?
installer_dir = Vagrant.installer_embedded_dir.to_s.downcase installer_dir = ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"].to_s.downcase
# If we're in an installer on Mac and we're executing a command # If we're in an installer on Mac and we're executing a command
# in the installer context, then force DYLD_LIBRARY_PATH to look # in the installer context, then force DYLD_LIBRARY_PATH to look
@ -123,18 +123,6 @@ module Vagrant
@logger.info("Command not in installer, restoring original environment...") @logger.info("Command not in installer, restoring original environment...")
jailbreak(process.environment) jailbreak(process.environment)
end end
# If running within an AppImage and calling external executable. When
# executable is external set the LD_LIBRARY_PATH to host values.
if ENV["VAGRANT_APPIMAGE"]
embed_path = Pathname.new(Vagrant.installer_embedded_dir).expand_path.to_s
exec_path = Pathname.new(@command[0]).expand_path.to_s
if !exec_path.start_with?(embed_path) && ENV["VAGRANT_APPIMAGE_HOST_LD_LIBRARY_PATH"]
@logger.info("Detected AppImage environment and request to external binary. Updating library path.")
@logger.debug("Setting LD_LIBRARY_PATH to #{ENV["VAGRANT_APPIMAGE_HOST_LD_LIBRARY_PATH"]}")
process.environment["LD_LIBRARY_PATH"] = ENV["VAGRANT_APPIMAGE_HOST_LD_LIBRARY_PATH"].to_s
end
end
else else
@logger.info("Vagrant not running in installer, restoring original environment...") @logger.info("Vagrant not running in installer, restoring original environment...")
jailbreak(process.environment) jailbreak(process.environment)

View File

@ -1,103 +0,0 @@
require "uri"
require "log4r"
require "vagrant/util/busy"
require "vagrant/util/platform"
require "vagrant/util/subprocess"
require "vagrant/util/curl_helper"
module Vagrant
module Util
# This class uploads files using various protocols by subprocessing
# to cURL. cURL is a much more capable and complete download tool than
# a hand-rolled Ruby library, so we defer to its expertise.
class Uploader
# @param [String] destination - valid URL to upload file to
# @param [String] file - location of file to upload on disk
# @param [Hash] options
def initialize(destination, file, options=nil)
options ||= {}
@logger = Log4r::Logger.new("vagrant::util::uploader")
@destination = destination.to_s
@file = file.to_s
@ui = options[:ui]
@request_method = options[:method]
if !@request_method
@request_method = "PUT"
end
end
def upload!
data_proc = Vagrant::Util::CurlHelper.capture_output_proc(@logger, @ui)
@logger.info("Uploader starting upload: ")
@logger.info(" -- Source: #{@file}")
@logger.info(" -- Destination: #{@destination}")
options = build_options
subprocess_options = {notify: :stderr}
begin
execute_curl(options, subprocess_options, &data_proc)
rescue Errors::UploaderError => e
raise
ensure
@ui.clear_line if @ui
end
end
protected
def build_options
options = [@destination, "--request", @request_method, "--upload-file", @file]
return options
end
def execute_curl(options, subprocess_options, &data_proc)
options = options.dup
options << subprocess_options
# Create the callback that is called if we are interrupted
interrupted = false
int_callback = Proc.new do
@logger.info("Uploader interrupted!")
interrupted = true
end
# Execute!
result = Busy.busy(int_callback) do
Subprocess.execute("curl", *options, &data_proc)
end
# If the upload was interrupted, then raise a specific error
raise Errors::UploaderInterrupted if interrupted
# If it didn't exit successfully, we need to parse the data and
# show an error message.
if result.exit_code != 0
@logger.warn("Uploader exit code: #{result.exit_code}")
check = result.stderr.match(/\n*curl:\s+\((?<code>\d+)\)\s*(?<error>.*)$/)
if !check
err_msg = result.stderr
else
err_msg = check[:error]
end
raise Errors::UploaderError,
exit_code: result.exit_code,
message: err_msg
end
if @ui
@ui.clear_line
# Windows doesn't clear properly for some reason, so we just
# output one more newline.
@ui.detail("") if Platform.windows?
end
result
end
end
end
end

View File

@ -1,5 +1,4 @@
require "vagrant/util/template_renderer" require "vagrant/util/template_renderer"
require "log4r"
module Vagrant module Vagrant
# This class provides a way to load and access the contents # This class provides a way to load and access the contents
@ -27,7 +26,6 @@ module Vagrant
@keys = keys @keys = keys
@loader = loader @loader = loader
@config, _ = loader.load(keys) @config, _ = loader.load(keys)
@logger = Log4r::Logger.new("vagrant::vagrantfile")
end end
# Returns a {Machine} for the given name and provider that # Returns a {Machine} for the given name and provider that
@ -44,7 +42,7 @@ module Vagrant
# @return [Machine] # @return [Machine]
def machine(name, provider, boxes, data_path, env) def machine(name, provider, boxes, data_path, env)
# Load the configuration for the machine # Load the configuration for the machine
results = machine_config(name, provider, boxes, data_path) results = machine_config(name, provider, boxes)
box = results[:box] box = results[:box]
config = results[:config] config = results[:config]
config_errors = results[:config_errors] config_errors = results[:config_errors]
@ -109,10 +107,9 @@ module Vagrant
# be backed by (required for provider overrides). # be backed by (required for provider overrides).
# @param [BoxCollection] boxes BoxCollection to look up the # @param [BoxCollection] boxes BoxCollection to look up the
# box Vagrantfile. # box Vagrantfile.
# @param [Pathname] data_path Machine data path
# @return [Hash<Symbol, Object>] Various configuration parameters for a # @return [Hash<Symbol, Object>] Various configuration parameters for a
# machine. See the main documentation body for more info. # machine. See the main documentation body for more info.
def machine_config(name, provider, boxes, data_path=nil, validate_provider=true) def machine_config(name, provider, boxes)
keys = @keys.dup keys = @keys.dup
sub_machine = @config.vm.defined_vms[name] sub_machine = @config.vm.defined_vms[name]
@ -127,25 +124,11 @@ module Vagrant
box_formats = nil box_formats = nil
if provider != nil if provider != nil
provider_plugin = Vagrant.plugin("2").manager.providers[provider] provider_plugin = Vagrant.plugin("2").manager.providers[provider]
if !provider_plugin && validate_provider if !provider_plugin
providers = Vagrant.plugin("2").manager.providers.to_hash.keys
if providers
providers_str = providers.join(', ')
else
providers_str = "N/A"
end
if providers.include? provider.downcase
raise Errors::ProviderNotFoundSuggestion,
machine: name, provider: provider,
suggestion: provider.downcase, providers: providers_str
end
raise Errors::ProviderNotFound, raise Errors::ProviderNotFound,
machine: name, provider: provider, providers: providers_str machine: name, provider: provider
end end
if validate_provider
provider_cls = provider_plugin[0] provider_cls = provider_plugin[0]
provider_options = provider_plugin[1] provider_options = provider_plugin[1]
box_formats = provider_options[:box_format] || provider box_formats = provider_options[:box_format] || provider
@ -159,9 +142,6 @@ module Vagrant
provider: provider.to_s, provider: provider.to_s,
message: e.to_s message: e.to_s
end end
else
box_formats = provider
end
end end
# Add the sub-machine configuration to the loader and keys # Add the sub-machine configuration to the loader and keys
@ -174,21 +154,8 @@ module Vagrant
# Track the original box so we know if we changed # Track the original box so we know if we changed
box = nil box = nil
initial_box = original_box = config.vm.box original_box = config.vm.box
initial_version = original_version = config.vm.box_version original_version = config.vm.box_version
# Check if this machine has a local box metadata file
# describing the existing guest. If so, load it and
# set the box name and version to allow the actual
# box in use to be discovered.
if data_path
meta_file = data_path.join("box_meta")
if meta_file.file?
box_meta = JSON.parse(meta_file.read)
config.vm.box = box_meta["name"]
config.vm.box_version = box_meta["version"]
end
end
# The proc below loads the box and provider overrides. This is # The proc below loads the box and provider overrides. This is
# in a proc because it may have to recurse if the provider override # in a proc because it may have to recurse if the provider override
@ -201,14 +168,12 @@ module Vagrant
box = boxes.find(config.vm.box, box_formats, config.vm.box_version) box = boxes.find(config.vm.box, box_formats, config.vm.box_version)
if box if box
box_vagrantfile = find_vagrantfile(box.directory) box_vagrantfile = find_vagrantfile(box.directory)
if box_vagrantfile && !config.vm.ignore_box_vagrantfile if box_vagrantfile
box_config_key = box_config_key =
"#{boxes.object_id}_#{box.name}_#{box.provider}".to_sym "#{boxes.object_id}_#{box.name}_#{box.provider}".to_sym
@loader.set(box_config_key, box_vagrantfile) @loader.set(box_config_key, box_vagrantfile)
local_keys.unshift(box_config_key) local_keys.unshift(box_config_key)
config, config_warnings, config_errors = @loader.load(local_keys) config, config_warnings, config_errors = @loader.load(local_keys)
elsif box_vagrantfile && config.vm.ignore_box_vagrantfile
@logger.warn("Ignoring #{box.name} provided Vagrantfile inside box")
end end
end end
end end
@ -236,22 +201,6 @@ module Vagrant
# Load the box and provider overrides # Load the box and provider overrides
load_box_proc.call load_box_proc.call
# NOTE: In cases where the box_meta file contains stale information
# and the reference box no longer exists, fall back to initial
# configuration and attempt to load that
if box.nil?
@logger.warn("Failed to locate #{config.vm.box} with version #{config.vm.box_version}")
@logger.warn("Performing lookup with inital values #{initial_box} with version #{initial_version}")
config.vm.box = original_box = initial_box
config.vm.box_version = original_box = initial_version
load_box_proc.call
end
# Ensure box attributes are set to original values in
# case they were modified by the local box metadata
config.vm.box = original_box
config.vm.box_version = original_version
return { return {
box: box, box: box,
provider_cls: provider_cls, provider_cls: provider_cls,

View File

@ -86,7 +86,7 @@ module VagrantPlugins
box_force: options[:force], box_force: options[:force],
box_download_ca_cert: options[:ca_cert], box_download_ca_cert: options[:ca_cert],
box_download_ca_path: options[:ca_path], box_download_ca_path: options[:ca_path],
box_download_client_cert: options[:client_cert], box_client_cert: options[:client_cert],
box_download_insecure: options[:insecure], box_download_insecure: options[:insecure],
box_download_location_trusted: options[:location_trusted], box_download_location_trusted: options[:location_trusted],
ui: Vagrant::UI::Prefixed.new(@env.ui, "box"), ui: Vagrant::UI::Prefixed.new(@env.ui, "box"),

View File

@ -26,10 +26,6 @@ module VagrantPlugins
options[:global] = g options[:global] = g
end end
o.on("-f", "--force", "Force checks for latest box updates") do |f|
options[:force] = f
end
build_download_options(o, download_options) build_download_options(o, download_options)
end end
@ -44,7 +40,7 @@ module VagrantPlugins
with_target_vms(argv) do |machine| with_target_vms(argv) do |machine|
@env.action_runner.run(Vagrant::Action.action_box_outdated, { @env.action_runner.run(Vagrant::Action.action_box_outdated, {
box_outdated_force: options[:force], box_outdated_force: true,
box_outdated_refresh: true, box_outdated_refresh: true,
box_outdated_success_ui: true, box_outdated_success_ui: true,
machine: machine, machine: machine,
@ -77,15 +73,7 @@ module VagrantPlugins
end end
current = Gem::Version.new(box.version) current = Gem::Version.new(box.version)
box_versions = md.versions(provider: box.provider) latest = Gem::Version.new(md.versions.last)
if box_versions.empty?
latest_box_version = box_versions.last.to_i
else
latest_box_version = box_versions.last
end
latest = Gem::Version.new(latest_box_version)
if latest <= current if latest <= current
@env.ui.success(I18n.t( @env.ui.success(I18n.t(
"vagrant.box_up_to_date", "vagrant.box_up_to_date",

View File

@ -30,10 +30,6 @@ module VagrantPlugins
o.on("-f", "--force", "Destroy without confirmation even when box is in use.") do |f| o.on("-f", "--force", "Destroy without confirmation even when box is in use.") do |f|
options[:force] = f options[:force] = f
end end
o.on("-k", "--keep-active-boxes", "When combined with `--force`, will keep boxes still actively in use.") do |k|
options[:keep] = k
end
end end
# Parse the options # Parse the options
@ -45,7 +41,7 @@ module VagrantPlugins
return @env.ui.warn(I18n.t("vagrant.commands.box.no_installed_boxes"), prefix: false) return @env.ui.warn(I18n.t("vagrant.commands.box.no_installed_boxes"), prefix: false)
end end
delete_oldest_boxes(boxes, options[:provider], options[:force], options[:name], options[:dry_run], options[:keep]) delete_oldest_boxes(boxes, options[:provider], options[:force], options[:name], options[:dry_run])
# Success, exit status 0 # Success, exit status 0
0 0
@ -53,7 +49,7 @@ module VagrantPlugins
private private
def delete_oldest_boxes(boxes, only_provider, skip_confirm, only_name, dry_run, keep_used_boxes) def delete_oldest_boxes(boxes, only_provider, skip_confirm, only_name, dry_run)
# Find the longest box name # Find the longest box name
longest_box = boxes.max_by { |x| x[0].length } longest_box = boxes.max_by { |x| x[0].length }
longest_box_length = longest_box[0].length longest_box_length = longest_box[0].length
@ -116,7 +112,6 @@ module VagrantPlugins
box_provider: provider, box_provider: provider,
box_version: version, box_version: version,
force_confirm_box_remove: skip_confirm, force_confirm_box_remove: skip_confirm,
keep_used_boxes: keep_used_boxes,
box_remove_all_versions: false, box_remove_all_versions: false,
}) })
end end

View File

@ -80,7 +80,7 @@ module VagrantPlugins
end end
to_update = [ to_update = [
[name, provider, boxes[name][provider].sort_by{|n| Gem::Version.new(n)}.last], [name, provider, boxes[name][provider].sort.last],
] ]
to_update.each do |n, p, v| to_update.each do |n, p, v|
@ -99,16 +99,12 @@ module VagrantPlugins
next next
end end
if !machine.box
collection = Vagrant::BoxCollection.new(@env.boxes_path)
machine.box = collection.find(machine.config.vm.box, provider || machine.provider_name || @env.default_provider, "> 0")
if !machine.box if !machine.box
machine.ui.output(I18n.t( machine.ui.output(I18n.t(
"vagrant.errors.box_update_no_box", "vagrant.errors.box_update_no_box",
name: machine.config.vm.box)) name: machine.config.vm.box))
next next
end end
end
name = machine.box.name name = machine.box.name
provider = machine.box.provider provider = machine.box.provider
@ -128,13 +124,7 @@ module VagrantPlugins
if download_options[:insecure].nil? if download_options[:insecure].nil?
download_options[:insecure] = machine.config.vm.box_download_insecure download_options[:insecure] = machine.config.vm.box_download_insecure
end end
begin
box_update(box, version, machine.ui, download_options, force) box_update(box, version, machine.ui, download_options, force)
rescue Vagrant::Errors::BoxUpdateNoMetadata => e
machine.ui.warn(e)
next
end
end end
end end
@ -164,7 +154,7 @@ module VagrantPlugins
box_version: update[1].version, box_version: update[1].version,
ui: ui, ui: ui,
box_force: force, box_force: force,
box_download_client_cert: download_options[:client_cert], box_client_cert: download_options[:client_cert],
box_download_ca_cert: download_options[:ca_cert], box_download_ca_cert: download_options[:ca_cert],
box_download_ca_path: download_options[:ca_path], box_download_ca_path: download_options[:ca_path],
box_download_insecure: download_options[:insecure] box_download_insecure: download_options[:insecure]

View File

@ -1,90 +0,0 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module AuthCommand
module Command
class Login < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud auth login [options]"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-c", "--check", "Checks if currently logged in") do |c|
options[:check] = c
end
o.on("-d", "--description DESCRIPTION", String, "Set description for the Vagrant Cloud token") do |d|
options[:description] = d
end
o.on("-k", "--logout", "Logout from Vagrant Cloud") do |k|
options[:logout] = k
end
o.on("-t", "--token TOKEN", String, "Set the Vagrant Cloud token") do |t|
options[:token] = t
end
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |l|
options[:login] = l
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
@client = Client.new(@env)
@client.username_or_email = options[:login]
# 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])
else
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options)
end
0
end
def execute_check
if @client.logged_in?
@env.ui.success(I18n.t("cloud_command.check_logged_in"))
return 0
else
@env.ui.error(I18n.t("cloud_command.check_not_logged_in"))
return 1
end
end
def execute_logout
@client.clear_token
@env.ui.success(I18n.t("cloud_command.logged_out"))
return 0
end
def execute_token(token)
@client.store_token(token)
@env.ui.success(I18n.t("cloud_command.token_saved"))
if @client.logged_in?
@env.ui.success(I18n.t("cloud_command.check_logged_in"))
return 0
else
@env.ui.error(I18n.t("cloud_command.invalid_token"))
return 1
end
end
end
end
end
end
end

View File

@ -1,42 +0,0 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module AuthCommand
module Command
class Logout < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud auth logout [options]"
o.separator ""
o.separator "Log out of Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |l|
options[:login] = l
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if !argv.empty?
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
# Initializes client and deletes token on disk
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
@client.clear_token
@env.ui.success(I18n.t("cloud_command.logged_out"))
return 0
end
end
end
end
end
end

View File

@ -1,20 +0,0 @@
require "vagrant"
module VagrantPlugins
module CloudCommand
module AuthCommand
class Plugin < Vagrant.plugin("2")
name "vagrant cloud auth"
description <<-DESC
Authorization commands for Vagrant Cloud
DESC
command(:auth) do
require_relative "root"
Command::Root
end
end
end
end
end

View File

@ -1,73 +0,0 @@
module VagrantPlugins
module CloudCommand
module AuthCommand
module Command
class Root < Vagrant.plugin("2", :command)
def self.synopsis
"Manages Vagrant Cloud authorization related to Vagrant Cloud"
end
def initialize(argv, env)
super
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
@subcommands = Vagrant::Registry.new
@subcommands.register(:login) do
require File.expand_path("../login", __FILE__)
Command::Login
end
@subcommands.register(:logout) do
require File.expand_path("../logout", __FILE__)
Command::Logout
end
@subcommands.register(:whoami) do
require File.expand_path("../whoami", __FILE__)
Command::Whoami
end
end
def execute
if @main_args.include?("-h") || @main_args.include?("--help")
# Print the help for all the box commands.
return help
end
# If we reached this far then we must have a subcommand. If not,
# then we also just print the help and exit.
command_class = @subcommands.get(@sub_command.to_sym) if @sub_command
return help if !command_class || !@sub_command
@logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}")
# Initialize and execute the command class
command_class.new(@sub_args, @env).execute
end
# Prints the help out for this command
def help
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant cloud auth <subcommand> [<args>]"
opts.separator ""
opts.separator "Authorization with Vagrant Cloud"
opts.separator ""
opts.separator "Available subcommands:"
# Add the available subcommands as separators in order to print them
# out as well.
keys = []
@subcommands.each { |key, value| keys << key.to_s }
keys.sort.each do |key|
opts.separator " #{key}"
end
opts.separator ""
opts.separator "For help on any individual subcommand run `vagrant cloud auth <subcommand> -h`"
end
@env.ui.info(opts.help, prefix: false)
end
end
end
end
end
end

View File

@ -1,62 +0,0 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module AuthCommand
module Command
class Whoami < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud auth whoami [options] [token]"
o.separator ""
o.separator "Display currently logged in user"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |l|
options[:login] = l
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.size > 1
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:login])
if argv.first
token = argv.first
else
token = @client.token
end
whoami(token, options[:username])
end
def whoami(access_token, username)
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account(username, access_token, server_url)
begin
success = account.validate_token
user = success["user"]["username"]
@env.ui.success("Currently logged in as #{user}")
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.whoami.read_error", org: username))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end
end

View File

@ -1,75 +0,0 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module BoxCommand
module Command
class Create < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud box create [options] organization/box-name"
o.separator ""
o.separator "Creates an empty box entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-d", "--description DESCRIPTION", String, "Full description of the box") do |d|
options[:description] = d
end
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u|
options[:username] = u
end
o.on("-s", "--short-description DESCRIPTION", String, "Short description of the box") do |s|
options[:short] = s
end
o.on("-p", "--private", "Makes box private") do |p|
options[:private] = p
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 1
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/', 2)
org = box[0]
box_name = box[1]
create_box(org, box_name, options, @client.token)
end
# @param [String] - org
# @param [String] - box_name
# @param [Hash] - options
def create_box(org, box_name, options, access_token)
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, options[:short], options[:description], access_token)
begin
success = box.create
@env.ui.success(I18n.t("cloud_command.box.create_success", org: org, box_name: box_name))
success = success.delete_if { |_, v| v.nil? }
VagrantPlugins::CloudCommand::Util.format_box_results(success, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.box.create_fail", org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end
end

View File

@ -1,65 +0,0 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module BoxCommand
module Command
class Delete < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud box delete [options] organization/box-name"
o.separator ""
o.separator "Deletes box entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 1
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@env.ui.warn(I18n.t("cloud_command.box.delete_warn", box: argv.first))
cont = @env.ui.ask(I18n.t("cloud_command.continue"))
return 1 if cont.strip.downcase != "y"
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/', 2)
org = box[0]
box_name = box[1]
delete_box(org, box_name, options[:username], @client.token)
end
def delete_box(org, box_name, username, access_token)
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account(username, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
begin
success = box.delete(org, box_name)
@env.ui.success(I18n.t("cloud_command.box.delete_success", org: org, box_name: box_name))
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.box.delete_fail", org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end
end

View File

@ -1,19 +0,0 @@
require "vagrant"
module VagrantPlugins
module CloudCommand
module BoxCommand
class Plugin < Vagrant.plugin("2")
name "vagrant cloud box"
description <<-DESC
Box life cycle commands for Vagrant Cloud
DESC
command(:box) do
require_relative "root"
Command::Root
end
end
end
end
end

View File

@ -1,77 +0,0 @@
module VagrantPlugins
module CloudCommand
module BoxCommand
module Command
class Root < Vagrant.plugin("2", :command)
def self.synopsis
"Commands to manage boxes on Vagrant Cloud"
end
def initialize(argv, env)
super
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
@subcommands = Vagrant::Registry.new
@subcommands.register(:create) do
require File.expand_path("../create", __FILE__)
Command::Create
end
@subcommands.register(:delete) do
require File.expand_path("../delete", __FILE__)
Command::Delete
end
@subcommands.register(:show) do
require File.expand_path("../show", __FILE__)
Command::Show
end
@subcommands.register(:update) do
require File.expand_path("../update", __FILE__)
Command::Update
end
end
def execute
if @main_args.include?("-h") || @main_args.include?("--help")
# Print the help for all the box commands.
return help
end
# If we reached this far then we must have a subcommand. If not,
# then we also just print the help and exit.
command_class = @subcommands.get(@sub_command.to_sym) if @sub_command
return help if !command_class || !@sub_command
@logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}")
# Initialize and execute the command class
command_class.new(@sub_args, @env).execute
end
# Prints the help out for this command
def help
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant cloud box <subcommand> [<args>]"
opts.separator ""
opts.separator "Commands to manage boxes on Vagrant Cloud"
opts.separator ""
opts.separator "Available subcommands:"
# Add the available subcommands as separators in order to print them
# out as well.
keys = []
@subcommands.each { |key, value| keys << key.to_s }
keys.sort.each do |key|
opts.separator " #{key}"
end
opts.separator ""
opts.separator "For help on any individual subcommand run `vagrant cloud box <subcommand> -h`"
end
@env.ui.info(opts.help, prefix: false)
end
end
end
end
end
end

View File

@ -1,74 +0,0 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module BoxCommand
module Command
class Show < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud box show [options] organization/box-name"
o.separator ""
o.separator "Displays a boxes attributes on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u|
options[:username] = u
end
o.on("--versions VERSION", String, "Display box information for a specific version") do |v|
options[:version] = v
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 1
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/', 2)
show_box(box[0], box[1], options, @client.token)
end
def show_box(org, box_name, options, access_token)
username = options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account(username, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
begin
success = box.read(org, box_name)
if options[:version]
# show *this* version only
results = success["versions"].select{ |v| v if v["version"] == options[:version] }.first
if !results
@env.ui.warn(I18n.t("cloud_command.box.show_filter_empty", version: options[:version], org: org, box_name: box_name))
return 0
end
else
results = success
end
results = results.delete_if { |_, v| v.nil? }
VagrantPlugins::CloudCommand::Util.format_box_results(results, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.box.show_fail", org: org,box_name:box_name))
@env.ui.error(e)
return 1
end
end
end
end
end
end
end

View File

@ -1,71 +0,0 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module BoxCommand
module Command
class Update < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud box update [options] organization/box-name"
o.separator ""
o.separator "Updates a box entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-d", "--description DESCRIPTION", "Full description of the box") do |d|
options[:description] = d
end
o.on("-u", "--username", "The username of the organization that will own the box") do |u|
options[:username] = u
end
o.on("-s", "--short-description DESCRIPTION", "Short description of the box") do |s|
options[:short_description] = s
end
o.on("-p", "--private", "Makes box private") do |p|
options[:private] = p
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 1 || options.length == 0
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/', 2)
update_box(box[0], box[1], options, @client.token)
end
def update_box(org, box_name, options, access_token)
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account(options[:username], access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
options[:organization] = org
options[:name] = box_name
begin
success = box.update(options)
@env.ui.success(I18n.t("cloud_command.box.update_success", org: org, box_name: box_name))
success = success.delete_if{|_, v|v.nil?}
VagrantPlugins::CloudCommand::Util.format_box_results(success, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.box.update_fail", org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end
end

View File

@ -1,264 +0,0 @@
require "rest_client"
require "vagrant_cloud"
require "vagrant/util/downloader"
require "vagrant/util/presence"
require Vagrant.source_root.join("plugins/commands/cloud/errors")
module VagrantPlugins
module CloudCommand
class Client
######################################################################
# Class that deals with managing users 'local' token for Vagrant Cloud
######################################################################
APP = "app".freeze
include Vagrant::Util::Presence
attr_accessor :username_or_email
attr_accessor :password
attr_reader :two_factor_default_delivery_method
attr_reader :two_factor_delivery_methods
# Initializes a login client with the given Vagrant::Environment.
#
# @param [Vagrant::Environment] env
def initialize(env)
@logger = Log4r::Logger.new("vagrant::cloud::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
Vagrant::Util::CredentialScrubber.sensitive(token)
with_error_handling do
url = "#{Vagrant.server_url}/api/v1/authenticate" +
"?access_token=#{token}"
RestClient.get(url, content_type: :json)
true
end
rescue Errors::Unauthorized
false
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] description
# @param [String] code
# @return [String] token The access token, or nil if auth failed.
def login(description: nil, code: nil)
@logger.info("Logging in '#{username_or_email}'")
Vagrant::Util::CredentialScrubber.sensitive(password)
response = post(
"/api/v1/authenticate", {
user: {
login: username_or_email,
password: password
},
token: {
description: description
},
two_factor: {
code: code
}
}
)
Vagrant::Util::CredentialScrubber.sensitive(response["token"])
response["token"]
end
# Requests a 2FA code
# @param [String] delivery_method
def request_code(delivery_method)
@env.ui.warn("Requesting 2FA code via #{delivery_method.upcase}...")
Vagrant::Util::CredentialScrubber.sensitive(password)
response = post(
"/api/v1/two-factor/request-code", {
user: {
login: username_or_email,
password: password
},
two_factor: {
delivery_method: delivery_method.downcase
}
}
)
two_factor = response['two_factor']
obfuscated_destination = two_factor['obfuscated_destination']
@env.ui.success("2FA code sent to #{obfuscated_destination}.")
end
# Issues a post to a Vagrant Cloud path with the given payload.
# @param [String] path
# @param [Hash] payload
# @return [Hash] response data
def post(path, payload)
with_error_handling do
url = File.join(Vagrant.server_url, path)
proxy = nil
proxy ||= ENV["HTTPS_PROXY"] || ENV["https_proxy"]
proxy ||= ENV["HTTP_PROXY"] || ENV["http_proxy"]
RestClient.proxy = proxy
response = RestClient::Request.execute(
method: :post,
url: url,
payload: JSON.dump(payload),
proxy: proxy,
headers: {
accept: :json,
content_type: :json,
user_agent: Vagrant::Util::Downloader::USER_AGENT,
},
)
JSON.load(response.to_s)
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
# `VAGRANT_CLOUD_TOKEN` environment variable and then fallback to the stored
# access token on disk.
#
# @return [String]
def token
if present?(ENV["VAGRANT_CLOUD_TOKEN"]) && token_path.exist?
@env.ui.warn <<-EOH.strip
Vagrant detected both the VAGRANT_CLOUD_TOKEN environment variable and a Vagrant login
token are present on this system. The VAGRANT_CLOUD_TOKEN environment variable takes
precedence over the locally stored token. To remove this error, either unset
the VAGRANT_CLOUD_TOKEN environment variable or remove the login token stored on disk:
~/.vagrant.d/data/vagrant_login_token
EOH
end
if present?(ENV["VAGRANT_CLOUD_TOKEN"])
@logger.debug("Using authentication token from environment variable")
return ENV["VAGRANT_CLOUD_TOKEN"]
end
if token_path.exist?
@logger.debug("Using authentication token from disk at #{token_path}")
return token_path.read.strip
end
if present?(ENV["ATLAS_TOKEN"])
@logger.warn("ATLAS_TOKEN detected within environment. Using ATLAS_TOKEN in place of VAGRANT_CLOUD_TOKEN.")
return ENV["ATLAS_TOKEN"]
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!")
raise Errors::Unauthorized
rescue RestClient::BadRequest => e
@logger.debug("Bad request:")
@logger.debug(e.message)
@logger.debug(e.backtrace.join("\n"))
parsed_response = JSON.parse(e.response)
errors = parsed_response["errors"].join("\n")
raise Errors::ServerError, errors: errors
rescue RestClient::NotAcceptable => e
@logger.debug("Got unacceptable response:")
@logger.debug(e.message)
@logger.debug(e.backtrace.join("\n"))
parsed_response = JSON.parse(e.response)
if two_factor = parsed_response['two_factor']
store_two_factor_information two_factor
if two_factor_default_delivery_method != APP
request_code two_factor_default_delivery_method
end
raise Errors::TwoFactorRequired
end
begin
errors = parsed_response["errors"].join("\n")
raise Errors::ServerError, errors: errors
rescue JSON::ParserError; end
@logger.debug("Got an unexpected error:")
@logger.debug(e.inspect)
raise Errors::Unexpected, error: 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
def store_two_factor_information(two_factor)
@two_factor_default_delivery_method =
two_factor['default_delivery_method']
@two_factor_delivery_methods =
two_factor['delivery_methods']
@env.ui.warn "2FA is enabled for your account."
if two_factor_default_delivery_method == APP
@env.ui.info "Enter the code from your authenticator."
else
@env.ui.info "Default method is " \
"'#{two_factor_default_delivery_method}'."
end
other_delivery_methods =
two_factor_delivery_methods - [APP]
if other_delivery_methods.any?
other_delivery_methods_sentence = other_delivery_methods
.map { |word| "'#{word}'" }
.join(' or ')
@env.ui.info "You can also type #{other_delivery_methods_sentence} " \
"to request a new code."
end
end
end
end
end

View File

@ -1,28 +0,0 @@
module VagrantPlugins
module CloudCommand
module Errors
class Error < Vagrant::Errors::VagrantError
error_namespace("cloud_command.errors")
end
class ServerError < Error
error_key(:server_error)
end
class ServerUnreachable < Error
error_key(:server_unreachable)
end
class Unauthorized < Error
error_key(:unauthorized)
end
class Unexpected < Error
error_key(:unexpected_error)
end
class TwoFactorRequired < Error
end
end
end
end

View File

@ -1,52 +0,0 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module Command
class List < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud list [options] organization"
o.separator ""
o.separator "Search for boxes managed by a specific user/organization"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-j", "--json", "Formats results in JSON") do |j|
options[:check] = j
end
o.on("-l", "--limit", Integer, "Max number of search results (default is 25)") do |l|
options[:check] = l
end
o.on("-p", "--provider", "Comma separated list of providers to filter search. Defaults to all.") do |p|
options[:check] = p
end
o.on("-s", "--sort-by", "Column to sort list (created, downloads, updated)") do |s|
options[:check] = s
end
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |t|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.length > 1
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
# TODO: This endpoint is not implemented yet
0
end
end
end
end
end

View File

@ -1,171 +0,0 @@
en:
cloud_command:
middleware:
authentication:
different_target: |-
Vagrant has detected a custom Vagrant server in use for downloading
box files. An authentication token is currently set which will be
added to the box request. If the custom Vagrant server should not
be receiving the authentication token, please unset it.
Known Vagrant server: %{known_host}
Custom Vagrant server: %{custom_host}
Press ctrl-c to cancel...
publish:
update_continue: |-
%{obj} already exists, updating instead...
box_create:
Creating a box entry...
version_create:
Creating a version entry...
provider_create:
Creating a provider entry...
upload_provider:
Uploading provider with file %{file}
release:
Releasing box...
complete:
Complete! Published %{org}/%{box_name}
confirm:
warn: |-
You are about to publish a box on Vagrant Cloud with the following options:
box: |-
%{org}/%{box_name}: (v%{version}) for provider '%{provider_name}'
private: |-
Private: true
release: |-
Automatic Release: true
box_url: |-
Remote Box file: %{url}
box_description: |-
Box Description: %{description}
box_short_desc: |-
Box Short Description: %{short_description}
version_desc: |-
Version Description: %{version_description}
continue: |-
Do you wish to continue? [y/N]
box:
show_filter_empty: |-
No version matched %{version} for %{org}/%{box_name}
create_success: |-
Created box %{org}/%{box_name}
delete_success: |-
Deleted box %{org}/%{box_name}
delete_warn: |-
This will completely remove %{box} from Vagrant Cloud. This cannot be undone.
update_success: |-
Updated box %{org}/%{box_name}
search:
no_results: |-
No results found for `%{query}`
upload:
no_url: |-
No URL was provided to upload the provider
You will need to run the `vagrant cloud provider upload` command to provide a box
provider:
upload: |-
Uploading box file for '%{org}/%{box_name}' (v%{version}) for provider: '%{provider}'
upload_success: |-
Uploaded provider %{provider} on %{org}/%{box_name} for version %{version}
delete_warn: |-
This will completely remove provider %{provider} on version %{version} from %{box} on Vagrant Cloud. This cannot be undone.
create_success: |-
Created provider %{provider} on %{org}/%{box_name} for version %{version}
delete_success: |-
Deleted provider %{provider} on %{org}/%{box_name} for version %{version}
update_success: |-
Updated provider %{provider} on %{org}/%{box_name} for version %{version}
version:
create_success: |-
Created version %{version} on %{org}/%{box_name} for version %{version}
delete_success: |-
Deleted version %{version} on %{org}/%{box_name}
release_success: |-
Released version %{version} on %{org}/%{box_name}
revoke_success: |-
Revoked version %{version} on %{org}/%{box_name}
update_success: |-
Updated version %{version} on %{org}/%{box_name}
revoke_warn: |-
This will revoke version %{version} from %{box} from Vagrant Cloud. This cannot be undone.
release_warn: |-
This will release version %{version} from %{box} to Vagrant Cloud and be available to download.
delete_warn: |-
This will completely remove version %{version} from %{box} from Vagrant Cloud. This cannot be undone.
errors:
search:
fail: |-
Could not complete search request
publish:
fail: |-
Failed to create box %{org}/%{box_name}
box:
create_fail: |-
Failed to create box %{org}/%{box_name}
delete_fail: |-
Failed to delete box %{org}/%{box_name}
show_fail: |-
Could not get information about box %{org}/%{box_name}
update_fail: |-
Failed to update box %{org}/%{box_name}
whoami:
read_error: |-
Failed to read organization %{org}
provider:
create_fail: |-
Failed to create provider %{provider} on box %{org}/%{box_name} for version %{version}
update_fail: |-
Failed to update provider %{provider} on box %{org}/%{box_name} for version %{version}
delete_fail: |-
Failed to delete provider %{provider} on box %{org}/%{box_name} for version %{version}
upload_fail: |-
Failed to upload provider %{provider} on box %{org}/%{box_name} for version %{version}
version:
create_fail: |-
Failed to create version %{version} on box %{org}/%{box_name}
delete_fail: |-
Failed to delete version %{version} on box %{org}/%{box_name}
release_fail: |-
Failed to release version %{version} on box %{org}/%{box_name}
revoke_fail: |-
Failed to revoke version %{version} on box %{org}/%{box_name}
update_fail: |-
Failed to update version %{version} on box %{org}/%{box_name}
server_error: |-
The Vagrant Cloud server responded with a not-OK response:
%{errors}
server_unreachable: |-
The Vagrant Cloud server is not currently accepting connections. Please check
your network connection and try again later.
unauthorized: |-
Invalid username or password. Please try again.
unexpected_error: |-
An unexpected error occurred: %{error}
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
Vagrant Cloud. 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 Vagrant Cloud account, sign up at
https://www.vagrantcloud.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.

View File

@ -1,35 +0,0 @@
require "vagrant"
require 'vagrant_cloud'
require Vagrant.source_root.join("plugins/commands/cloud/util")
require Vagrant.source_root.join("plugins/commands/cloud/client/client")
module VagrantPlugins
module CloudCommand
class Plugin < Vagrant.plugin("2")
name "vagrant-cloud"
description <<-DESC
Provides the cloud command and internal API access to Vagrant Cloud.
DESC
command(:cloud) do
require_relative "root"
init!
Command::Root
end
action_hook(:cloud_authenticated_boxes, :authenticate_box_url) do |hook|
require_relative "auth/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

View File

@ -1,80 +0,0 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module ProviderCommand
module Command
class Create < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud provider create [options] organization/box-name provider-name version [url]"
o.separator ""
o.separator "Creates a provider entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u|
options[:username] = u
end
o.on("-c", "--checksum CHECKSUM_VALUE", String, "Checksum of the box for this provider. --checksum-type option is required.") do |c|
options[:checksum] = c
end
o.on("-C", "--checksum-type TYPE", String, "Type of checksum used (md5, sha1, sha256, sha384, sha512). --checksum option is required.") do |c|
options[:checksum_type] = c
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 4
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/', 2)
org = box[0]
box_name = box[1]
provider_name = argv[1]
version = argv[2]
url = argv[3]
upload_provider(org, box_name, provider_name, version, url, @client.token, options)
end
def upload_provider(org, box_name, provider_name, version, url, access_token, options)
if !url
@env.ui.warn(I18n.t("cloud_command.upload.no_url"))
end
org = options[:username] if options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
cloud_version = VagrantCloud::Version.new(box, version, nil, nil, access_token)
provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, url, org, box_name,
access_token, nil, options[:checksum], options[:checksum_type])
begin
success = provider.create_provider
@env.ui.success(I18n.t("cloud_command.provider.create_success", provider: provider_name, org: org, box_name: box_name, version: version))
success = success.compact
VagrantPlugins::CloudCommand::Util.format_box_results(success, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.provider.create_fail", provider: provider_name, org: org, box_name: box_name, version: version))
@env.ui.error(e)
return 1
end
end
end
end
end
end
end

View File

@ -1,70 +0,0 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module ProviderCommand
module Command
class Delete < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud provider delete [options] organization/box-name provider-name version"
o.separator ""
o.separator "Deletes a provider entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 3
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
box = argv.first.split('/', 2)
org = box[0]
box_name = box[1]
provider_name = argv[1]
version = argv[2]
@env.ui.warn(I18n.t("cloud_command.provider.delete_warn", provider: provider_name, version:version, box: argv.first))
cont = @env.ui.ask(I18n.t("cloud_command.continue"))
return 1 if cont.strip.downcase != "y"
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
delete_provider(org, box_name, provider_name, version, @client.token, options)
end
def delete_provider(org, box_name, provider_name, version, access_token, options)
org = options[:username] if options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
cloud_version = VagrantCloud::Version.new(box, version, nil, nil, access_token)
provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, nil, nil, nil, access_token)
begin
success = provider.delete
@env.ui.error(I18n.t("cloud_command.provider.delete_success", provider: provider_name, org: org, box_name: box_name, version: version))
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.provider.delete_fail", provider: provider_name, org: org, box_name: box_name, version: version))
@env.ui.error(e)
return 1
end
end
end
end
end
end
end

View File

@ -1,19 +0,0 @@
require "vagrant"
module VagrantPlugins
module CloudCommand
module ProviderCommand
class Plugin < Vagrant.plugin("2")
name "vagrant cloud box"
description <<-DESC
Provider life cycle commands for Vagrant Cloud
DESC
command(:provider) do
require_relative "root"
Command::Root
end
end
end
end
end

View File

@ -1,77 +0,0 @@
module VagrantPlugins
module CloudCommand
module ProviderCommand
module Command
class Root < Vagrant.plugin("2", :command)
def self.synopsis
"Provider commands"
end
def initialize(argv, env)
super
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
@subcommands = Vagrant::Registry.new
@subcommands.register(:create) do
require File.expand_path("../create", __FILE__)
Command::Create
end
@subcommands.register(:delete) do
require File.expand_path("../delete", __FILE__)
Command::Delete
end
@subcommands.register(:update) do
require File.expand_path("../update", __FILE__)
Command::Update
end
@subcommands.register(:upload) do
require File.expand_path("../upload", __FILE__)
Command::Upload
end
end
def execute
if @main_args.include?("-h") || @main_args.include?("--help")
# Print the help for all the provider commands.
return help
end
# If we reached this far then we must have a subcommand. If not,
# then we also just print the help and exit.
command_class = @subcommands.get(@sub_command.to_sym) if @sub_command
return help if !command_class || !@sub_command
@logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}")
# Initialize and execute the command class
command_class.new(@sub_args, @env).execute
end
# Prints the help out for this command
def help
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant cloud provider <subcommand> [<args>]"
opts.separator ""
opts.separator "For various provider actions with Vagrant Cloud"
opts.separator ""
opts.separator "Available subcommands:"
# Add the available subcommands as separators in order to print them
# out as well.
keys = []
@subcommands.each { |key, value| keys << key.to_s }
keys.sort.each do |key|
opts.separator " #{key}"
end
opts.separator ""
opts.separator "For help on any individual subcommand run `vagrant cloud provider <subcommand> -h`"
end
@env.ui.info(opts.help, prefix: false)
end
end
end
end
end
end

View File

@ -1,80 +0,0 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module ProviderCommand
module Command
class Update < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud provider update [options] organization/box-name provider-name version url"
o.separator ""
o.separator "Updates a provider entry on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u|
options[:username] = u
end
o.on("-c", "--checksum CHECKSUM_VALUE", String, "Checksum of the box for this provider. --checksum-type option is required.") do |c|
options[:checksum] = c
end
o.on("-C", "--checksum-type TYPE", String, "Type of checksum used (md5, sha1, sha256, sha384, sha512). --checksum option is required.") do |c|
options[:checksum_type] = c
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 4
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/', 2)
org = box[0]
box_name = box[1]
provider_name = argv[1]
version = argv[2]
url = argv[3]
update_provider(org, box_name, provider_name, version, url, @client.token, options)
end
def update_provider(org, box_name, provider_name, version, url, access_token, options)
if !url
@env.ui.warn(I18n.t("cloud_command.upload.no_url"))
end
org = options[:username] if options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
cloud_version = VagrantCloud::Version.new(box, version, nil, nil, access_token)
provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, url, org, box_name,
access_token, nil, options[:checksum], options[:checksum_type])
begin
success = provider.update
@env.ui.success(I18n.t("cloud_command.provider.update_success", provider:provider_name, org: org, box_name: box_name, version: version))
success = success.delete_if{|_, v|v.nil?}
VagrantPlugins::CloudCommand::Util.format_box_results(success, @env)
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.provider.update_fail", provider:provider_name, org: org, box_name: box_name, version: version))
@env.ui.error(e)
return 1
end
end
end
end
end
end
end

View File

@ -1,75 +0,0 @@
require 'optparse'
require "vagrant/util/uploader"
module VagrantPlugins
module CloudCommand
module ProviderCommand
module Command
class Upload < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud provider upload [options] organization/box-name provider-name version box-file"
o.separator ""
o.separator "Uploads a box file to Vagrant Cloud for a specific provider"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 4
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
box = argv.first.split('/', 2)
org = box[0]
box_name = box[1]
provider_name = argv[1]
version = argv[2]
file = argv[3] # path expand
upload_provider(org, box_name, provider_name, version, file, @client.token, options)
end
def upload_provider(org, box_name, provider_name, version, file, access_token, options)
org = options[:username] if options[:username]
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
account = VagrantPlugins::CloudCommand::Util.account(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, nil, nil, access_token)
cloud_version = VagrantCloud::Version.new(box, version, nil, nil, access_token)
provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, nil, org, box_name, access_token)
ul = Vagrant::Util::Uploader.new(provider.upload_url, file, ui: @env.ui)
ui = Vagrant::UI::Prefixed.new(@env.ui, "cloud")
begin
ui.output(I18n.t("cloud_command.provider.upload", org: org, box_name: box_name, version: version, provider: provider_name))
ui.info("Upload File: #{file}")
ul.upload!
ui.success("Successfully uploaded box '#{org}/#{box_name}' (v#{version}) for '#{provider_name}'")
return 0
rescue Vagrant::Errors::UploaderError, VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.provider.upload_fail", provider: provider_name, org: org, box_name: box_name, version: version))
@env.ui.error(e)
return 1
end
end
end
end
end
end
end

View File

@ -1,183 +0,0 @@
require 'optparse'
require "vagrant/util/uploader"
module VagrantPlugins
module CloudCommand
module Command
class Publish < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud publish [options] organization/box-name version provider-name provider-file"
o.separator ""
o.separator "Create and release a new Vagrant Box on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("--box-version VERSION", String, "Version of box to create") do |v|
options[:box_version] = v
end
o.on("--url URL", String, "Remote URL to download this provider") do |u|
options[:url] = u
end
o.on("-d", "--description DESCRIPTION", String, "Full description of box") do |d|
options[:description] = d
end
o.on("--version-description DESCRIPTION", String, "Description of the version to create") do |v|
options[:version_description] = v
end
o.on("-f", "--force", "Disables confirmation to create or update box") do |f|
options[:force] = f
end
o.on("-p", "--private", "Makes box private") do |p|
options[:private] = p
end
o.on("-r", "--release", "Releases box") do |p|
options[:release] = p
end
o.on("-s", "--short-description DESCRIPTION", String, "Short description of the box") do |s|
options[:short_description] = s
end
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u|
options[:username] = u
end
o.on("-c", "--checksum CHECKSUM_VALUE", String, "Checksum of the box for this provider. --checksum-type option is required.") do |c|
options[:checksum] = c
end
o.on("-C", "--checksum-type TYPE", String, "Type of checksum used (md5, sha1, sha256, sha384, sha512). --checksum option is required.") do |c|
options[:checksum_type] = c
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.empty? || argv.length > 4 || argv.length < 3 || (argv.length == 3 && !options[:url])
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
box = argv.first.split('/', 2)
org = box[0]
box_name = box[1]
version = argv[1]
provider_name = argv[2]
box_file = argv[3]
if !options[:url] && !File.file?(box_file)
raise Vagrant::Errors::BoxFileNotExist,
file: box_file
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
publish_box(org, box_name, version, provider_name, box_file, options, @client.token)
end
def publish_box(org, box_name, version, provider_name, box_file, options, access_token)
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
@env.ui.warn(I18n.t("cloud_command.publish.confirm.warn"))
@env.ui.info(I18n.t("cloud_command.publish.confirm.box", org: org,
box_name: box_name, version: version, provider_name: provider_name))
@env.ui.info(I18n.t("cloud_command.publish.confirm.private")) if options[:private]
@env.ui.info(I18n.t("cloud_command.publish.confirm.release")) if options[:release]
@env.ui.info(I18n.t("cloud_command.publish.confirm.box_url",
url: options[:url])) if options[:url]
@env.ui.info(I18n.t("cloud_command.publish.confirm.box_description",
description: options[:description])) if options[:description]
@env.ui.info(I18n.t("cloud_command.publish.confirm.box_short_desc",
short_description: options[:short_description])) if options[:short_description]
@env.ui.info(I18n.t("cloud_command.publish.confirm.version_desc",
version_description: options[:version_description])) if options[:version_description]
if !options[:force]
cont = @env.ui.ask(I18n.t("cloud_command.continue"))
return 1 if cont.strip.downcase != "y"
end
account = VagrantPlugins::CloudCommand::Util.account(org, access_token, server_url)
box = VagrantCloud::Box.new(account, box_name, nil, options[:short_description], options[:description], access_token)
cloud_version = VagrantCloud::Version.new(box, version, nil, options[:version_description], access_token)
provider = VagrantCloud::Provider.new(cloud_version, provider_name, nil, options[:url], org, box_name,
access_token, nil, options[:checksum], options[:checksum_type])
ui = Vagrant::UI::Prefixed.new(@env.ui, "cloud")
begin
ui.info(I18n.t("cloud_command.publish.box_create"))
box.create
rescue VagrantCloud::ClientError => e
if e.error_code == 422
ui.warn(I18n.t("cloud_command.publish.update_continue", obj: "Box"))
box.update(options)
else
@env.ui.error(I18n.t("cloud_command.errors.publish.fail", org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
end
begin
ui.info(I18n.t("cloud_command.publish.version_create"))
cloud_version.create_version
rescue VagrantCloud::ClientError => e
if e.error_code == 422
ui.warn(I18n.t("cloud_command.publish.update_continue", obj: "Version"))
cloud_version.update
else
@env.ui.error(I18n.t("cloud_command.errors.publish.fail", org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
rescue VagrantCloud::InvalidVersion => e
@env.ui.error(I18n.t("cloud_command.errors.publish.fail", org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
begin
ui.info(I18n.t("cloud_command.publish.provider_create"))
provider.create_provider
rescue VagrantCloud::ClientError => e
if e.error_code == 422
ui.warn(I18n.t("cloud_command.publish.update_continue", obj: "Provider"))
provider.update
else
@env.ui.error(I18n.t("cloud_command.errors.publish.fail", org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
end
begin
if !options[:url]
box_file = File.absolute_path(box_file)
ui.info(I18n.t("cloud_command.publish.upload_provider", file: box_file))
ul = Vagrant::Util::Uploader.new(provider.upload_url, box_file, ui: @env.ui)
ul.upload!
end
if options[:release]
ui.info(I18n.t("cloud_command.publish.release"))
cloud_version.release
end
@env.ui.success(I18n.t("cloud_command.publish.complete", org: org, box_name: box_name))
success = box.read(org, box_name)
success = success.delete_if{|_, v|v.nil?}
VagrantPlugins::CloudCommand::Util.format_box_results(success, @env)
return 0
rescue Vagrant::Errors::UploaderError, VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.publish.fail", org: org, box_name: box_name))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end

View File

@ -1,104 +0,0 @@
module VagrantPlugins
module CloudCommand
module Command
class Root < Vagrant.plugin("2", :command)
def self.synopsis
"manages everything related to Vagrant Cloud"
end
def initialize(argv, env)
super
@main_args, @sub_command, @sub_args = split_main_and_subcommand(argv)
@subcommands = Vagrant::Registry.new
@subcommand_helptext = {}
@subcommands.register(:auth) do
require File.expand_path("../auth/root", __FILE__)
AuthCommand::Command::Root
end
@subcommand_helptext[:auth] = "For various authorization operations on Vagrant Cloud"
@subcommands.register(:box) do
require File.expand_path("../box/root", __FILE__)
BoxCommand::Command::Root
end
@subcommand_helptext[:box] = "For managing a Vagrant box entry on Vagrant Cloud"
# TODO: Uncomment this when API endpoint exists
#@subcommands.register(:list) do
# require File.expand_path("../list", __FILE__)
# List
#end
#@subcommand_helptext[:list] = "Displays a list of Vagrant boxes that the current user manages"
@subcommands.register(:search) do
require File.expand_path("../search", __FILE__)
Search
end
@subcommand_helptext[:search] = "Search Vagrant Cloud for available boxes"
@subcommands.register(:provider) do
require File.expand_path("../provider/root", __FILE__)
ProviderCommand::Command::Root
end
@subcommand_helptext[:provider] = "For managing a Vagrant box's provider options"
@subcommands.register(:publish) do
require File.expand_path("../publish", __FILE__)
Publish
end
@subcommand_helptext[:publish] = "A complete solution for creating or updating a new box on Vagrant Cloud"
@subcommands.register(:version) do
require File.expand_path("../version/root", __FILE__)
VersionCommand::Command::Root
end
@subcommand_helptext[:version] = "For managing a Vagrant box's versions"
end
def execute
if @main_args.include?("-h") || @main_args.include?("--help")
# Print the help for all the box commands.
return help
end
# If we reached this far then we must have a subcommand. If not,
# then we also just print the help and exit.
command_class = @subcommands.get(@sub_command.to_sym) if @sub_command
return help if !command_class || !@sub_command
@logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}")
# Initialize and execute the command class
command_class.new(@sub_args, @env).execute
end
# Prints the help out for this command
def help
opts = OptionParser.new do |opts|
opts.banner = "Usage: vagrant cloud <subcommand> [<args>]"
opts.separator ""
opts.separator "The cloud command can be used for taking actions against"
opts.separator "Vagrant Cloud like searching or uploading a Vagrant Box"
opts.separator ""
opts.separator "Available subcommands:"
# Add the available subcommands as separators in order to print them
# out as well.
keys = []
@subcommands.each { |key, value| keys << key.to_s }
keys.sort.each do |key|
opts.separator " #{key.ljust(15)} #{@subcommand_helptext[key.to_sym]}"
end
opts.separator ""
opts.separator "For help on any individual subcommand run `vagrant cloud <subcommand> -h`"
end
@env.ui.info(opts.help, prefix: false)
end
end
end
end
end

View File

@ -1,83 +0,0 @@
require 'optparse'
module VagrantPlugins
module CloudCommand
module Command
class Search < Vagrant.plugin("2", :command)
def execute
options = {}
opts = OptionParser.new do |o|
o.banner = "Usage: vagrant cloud search [options] query"
o.separator ""
o.separator "Search for boxes managed by a specific"
o.separator "user/organization on Vagrant Cloud"
o.separator ""
o.separator "Options:"
o.separator ""
o.on("-j", "--json", "Formats results in JSON") do |j|
options[:json] = j
end
o.on("-p", "--page PAGE", Integer, "The page to display Default: 1") do |j|
options[:page] = j
end
o.on("-s", "--short", "Shows a simple list of box names") do |s|
options[:short] = s
end
o.on("-o", "--order ORDER", String, "Order to display results ('desc' or 'asc') Default: 'desc'") do |o|
options[:order] = o
end
o.on("-l", "--limit LIMIT", Integer, "Max number of search results Default: 25") do |l|
options[:limit] = l
end
o.on("-p", "--provider PROVIDER", String, "Filter search results to a single provider. Defaults to all.") do |p|
options[:provider] = p
end
o.on("--sort-by SORT", "Field to sort results on (created, downloads, updated) Default: downloads") do |s|
options[:sort] = s
end
o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address to login with") do |u|
options[:username] = u
end
end
# Parse the options
argv = parse_options(opts)
return if !argv
if argv.length > 1
raise Vagrant::Errors::CLIInvalidUsage,
help: opts.help.chomp
end
@client = VagrantPlugins::CloudCommand::Util.client_login(@env, options[:username])
query = argv.first
options[:limit] = 25 if !(options[:limit].to_i < 1) && !options[:limit]
search(query, options, @client.token)
end
def search(query, options, access_token)
server_url = VagrantPlugins::CloudCommand::Util.api_server_url
search = VagrantCloud::Search.new(access_token, server_url)
begin
search_results = search.search(query, options[:provider], options[:sort], options[:order], options[:limit], options[:page])
if !search_results["boxes"].empty?
VagrantPlugins::CloudCommand::Util.format_search_results(search_results["boxes"], options[:short], options[:json], @env)
else
@env.ui.warn(I18n.t("cloud_command.search.no_results", query: query))
end
return 0
rescue VagrantCloud::ClientError => e
@env.ui.error(I18n.t("cloud_command.errors.search.fail"))
@env.ui.error(e)
return 1
end
return 1
end
end
end
end
end

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