Compare commits
1 Commits
master
...
build-win-
Author | SHA1 | Date |
---|---|---|
Chris Roberts | a69ff5054d |
20
.ci/build.sh
20
.ci/build.sh
|
@ -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"
|
432
.ci/common.sh
432
.ci/common.sh
|
@ -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}"
|
|
@ -1,6 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
. "${root}/.ci/common.sh"
|
||||
|
||||
export DEBIAN_FRONTEND="noninteractive"
|
||||
export PATH="${PATH}:${root}/.ci"
|
|
@ -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
176
.ci/slack
|
@ -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
|
27
.ci/test.sh
27
.ci/test.sh
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
||||
### 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:
|
||||
## Make Changes
|
||||
|
||||
* In your forked repository, create a topic branch for your upcoming patch.
|
||||
* Usually this is based on the master branch.
|
||||
* Checkout a new branch based on master; `git checkout -b my-contrib master`
|
||||
Please avoid working directly on the `master` branch.
|
||||
* Make focused commits of logical units and describe them properly.
|
||||
* Avoid re-formatting of the existing code
|
||||
* Usually this is based on the master branch.
|
||||
* Create a branch based on master; `git branch
|
||||
fix/master/my_contribution master` then checkout the new branch with `git
|
||||
checkout fix/master/my_contribution`. Please avoid working directly on the `master` branch.
|
||||
* Make commits of logical units and describe them properly.
|
||||
* 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.
|
||||
* 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.
|
||||
* 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.
|
||||
|
||||
### 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.
|
||||
* 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.
|
||||
|
||||
# Additional Resources
|
||||
|
||||
* [HashiCorp Community Guidelines](https://www.hashicorp.com/community-guidelines)
|
||||
* [General GitHub documentation](https://help.github.com/)
|
||||
* [GitHub pull request documentation](https://help.github.com/send-pull-requests/)
|
||||
|
|
|
@ -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!
|
||||
|
||||
**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
|
||||
Run `vagrant -v` to show the version. If you are not running the latest version
|
||||
of Vagrant, please upgrade before submitting an issue.
|
||||
|
|
|
@ -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 }}
|
|
@ -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 }}
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -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
|
432
CHANGELOG.md
432
CHANGELOG.md
|
@ -2,432 +2,10 @@
|
|||
|
||||
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]
|
||||
|
||||
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: Suppress error messages from checkpoint runs [GH-9645]
|
||||
- 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/windows: Fix slow timeout when updating windows hostname [GH-9578]
|
||||
- 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/salt: Add master_json_config & minion_json_config options [GH-9420]
|
||||
- util/platform: Warn on ArgumentError exceptions from encoding [GH-9506]
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
- 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: 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/platform: Use `--version` instead of `version` for WSL validation [GH-9674]
|
||||
|
||||
## 2.0.3 (March 15, 2018)
|
||||
|
||||
|
@ -552,7 +122,7 @@ BUG FIXES:
|
|||
- 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/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]
|
||||
- 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]
|
||||
|
|
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
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
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
27
README.md
27
README.md
|
@ -19,8 +19,6 @@ between Windows, Mac OS X, and Linux.
|
|||
|
||||
## 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
|
||||
[VirtualBox](https://www.virtualbox.org/) because it is free and works
|
||||
on all major platforms. Vagrant can, however, work with almost any
|
||||
|
@ -33,11 +31,11 @@ installed. After this,
|
|||
|
||||
To build your first virtual environment:
|
||||
|
||||
vagrant init hashicorp/bionic64
|
||||
vagrant init hashicorp/precise32
|
||||
vagrant up
|
||||
|
||||
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.
|
||||
|
||||
## 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
|
||||
[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
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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!
|
||||
bundle exec vagrant help
|
||||
|
||||
### Acceptance Tests
|
||||
|
||||
|
|
36
RELEASE.md
36
RELEASE.md
|
@ -15,20 +15,34 @@ targeted at Vagrant core members who have the ability to cut a release.
|
|||
$ git push --tags
|
||||
```
|
||||
|
||||
1. This will automatically trigger an installer creation, upload the artifacts,
|
||||
and publish the release.
|
||||
1. Trigger an installer creation run within the HashiCorp Bamboo installation.
|
||||
This will take around 45 minutes.
|
||||
|
||||
1. After the release has been published update the `website/config.rb` to point
|
||||
to the latest version, commit, and push.
|
||||
1. Download all the resulting artifacts into the `pkg/dist` folder relative to
|
||||
the Vagrant repository on your local machine.
|
||||
|
||||
1. Publish the webiste by deleting the `stable-website` branch, recreate the branch,
|
||||
and force push. From the `master` branch, run:
|
||||
1. Run `./scripts/sign.sh` with the version that is being created. This must be
|
||||
run from the Vagrant repo root. This will GPG sign and checksum the files.
|
||||
|
||||
```
|
||||
$ git branch -D stable-website
|
||||
$ git branch -b stable-website
|
||||
$ git push -f origin stable-website
|
||||
```
|
||||
1. Run the following command to upload the binaries to the releases site:
|
||||
|
||||
```
|
||||
$ hc-releases upload pkg/dist
|
||||
```
|
||||
|
||||
1. Publish the new index files to the releases site:
|
||||
|
||||
```
|
||||
$ hc-releases publish
|
||||
```
|
||||
|
||||
1. Update `website/config.rb` to point to the latest version, commit, and push.
|
||||
|
||||
1. Tell HashiBot to deploy in ``#deploys`
|
||||
|
||||
```
|
||||
hashibot deploy vagrant
|
||||
```
|
||||
|
||||
1. Update `version.txt` to append `.dev` and add a new blank entry in the
|
||||
CHANGELOG, commit, and push.
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
# Ruby, run unit tests, etc.
|
||||
|
||||
Vagrant.configure("2") do |config|
|
||||
config.vm.box = "hashicorp/bionic64"
|
||||
config.vm.box = "hashicorp/precise64"
|
||||
config.vm.hostname = "vagrant"
|
||||
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|
|
||||
v.memory = "2048"
|
||||
v.memory = "1024"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -29,32 +29,32 @@ $shell = <<-'CONTENTS'
|
|||
export DEBIAN_FRONTEND=noninteractive
|
||||
MARKER_FILE="/usr/local/etc/vagrant_provision_marker"
|
||||
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
|
||||
if [ -f "${MARKER_FILE}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Add ubuntu_rvm repo
|
||||
apt-add-repository -y ppa:rael-gc/rvm
|
||||
|
||||
# Update apt
|
||||
apt-get update --quiet
|
||||
|
||||
# Add vagrant user to sudo group:
|
||||
# ubuntu_rvm only adds users in group sudo to group rvm
|
||||
usermod -a -G sudo vagrant
|
||||
|
||||
# Install basic dependencies and RVM
|
||||
apt-get install -qy build-essential bsdtar rvm
|
||||
# Install basic dependencies
|
||||
apt-get install -qy build-essential bsdtar curl
|
||||
|
||||
# Import the mpapis public key to verify downloaded releases
|
||||
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
|
||||
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 "rvm install ${RUBY_VER}" vagrant
|
||||
su -l -c "rvm --default use ${RUBY_VER}" vagrant
|
||||
# Install RVM
|
||||
su -l -c 'curl -sL https://get.rvm.io | bash -s stable' 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)
|
||||
su -l -c 'ruby --version' vagrant
|
||||
|
@ -63,14 +63,15 @@ su -l -c 'ruby --version' vagrant
|
|||
apt-get install -qy git
|
||||
|
||||
# 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
|
||||
|
||||
# Automatically move into the shared folder, but only add the command
|
||||
# if it's not already there.
|
||||
grep -q 'cd /vagrant' /home/vagrant/.bash_profile 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 ${MARKER_FILE}
|
||||
|
|
37
bin/vagrant
37
bin/vagrant
|
@ -5,11 +5,6 @@
|
|||
# initializing which have historically resulted in stack traces.
|
||||
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
|
||||
argv = ARGV.dup
|
||||
argv_extra = []
|
||||
|
@ -37,21 +32,13 @@ argv.each_index do |i|
|
|||
arg = argv[i]
|
||||
|
||||
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] = ""
|
||||
ENV['VAGRANT_NO_PLUGINS'] = "1"
|
||||
end
|
||||
|
||||
# Do not load plugins when performing plugin operations
|
||||
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"
|
||||
end
|
||||
if arg == "plugin" && argv[i+1] != "list"
|
||||
ENV['VAGRANT_DISABLE_PLUGIN_INIT'] = "1"
|
||||
end
|
||||
|
||||
break
|
||||
|
@ -89,7 +76,6 @@ begin
|
|||
require 'vagrant/bundler'
|
||||
require 'vagrant/cli'
|
||||
require 'vagrant/util/platform'
|
||||
require 'vagrant/util/experimental'
|
||||
|
||||
# Schedule the cleanup of things
|
||||
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)
|
||||
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
|
||||
# Execute the CLI interface, and exit with the proper error code
|
||||
exit_status = env.cli(argv)
|
||||
|
|
|
@ -53,7 +53,7 @@ __vagrantinvestigate() {
|
|||
_vagrant() {
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
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 ]
|
||||
then
|
||||
|
@ -65,7 +65,7 @@ _vagrant() {
|
|||
then
|
||||
case "$prev" in
|
||||
"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}))
|
||||
return 0
|
||||
;;
|
||||
|
@ -75,29 +75,17 @@ _vagrant() {
|
|||
then
|
||||
local vm_list=$(find "${vagrant_state_file}/machines" -mindepth 1 -maxdepth 1 -type d -exec basename {} \;)
|
||||
fi
|
||||
local up_commands="\
|
||||
--provision \
|
||||
--no-provision \
|
||||
--provision-with \
|
||||
--destroy-on-error \
|
||||
--no-destroy-on-error \
|
||||
--parallel \
|
||||
--no-parallel
|
||||
--provider \
|
||||
--install-provider \
|
||||
--no-install-provider \
|
||||
-h \
|
||||
--help"
|
||||
local up_commands="--no-provision"
|
||||
COMPREPLY=($(compgen -W "${up_commands} ${vm_list}" -- ${cur}))
|
||||
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
|
||||
if [[ -f "${vagrant_state_file}" ]]
|
||||
then
|
||||
running_vm_list=$(grep 'active' "${vagrant_state_file}" | sed -e 's/"active"://' | tr ',' '\n' | cut -d '"' -f 2 | tr '\n' ' ')
|
||||
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
|
||||
COMPREPLY=($(compgen -W "${running_vm_list}" -- ${cur}))
|
||||
return 0
|
||||
|
@ -107,11 +95,6 @@ _vagrant() {
|
|||
COMPREPLY=($(compgen -W "${box_commands}" -- ${cur}))
|
||||
return 0
|
||||
;;
|
||||
"cloud")
|
||||
cloud_commands="auth box search provider publish version"
|
||||
COMPREPLY=($(compgen -W "${cloud_commands}" -- ${cur}))
|
||||
return 0
|
||||
;;
|
||||
"plugin")
|
||||
plugin_commands="install license list uninstall update"
|
||||
COMPREPLY=($(compgen -W "${plugin_commands}" -- ${cur}))
|
||||
|
@ -149,7 +132,7 @@ _vagrant() {
|
|||
"box")
|
||||
case "$prev" in
|
||||
"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}))
|
||||
return 0
|
||||
;;
|
||||
|
@ -157,15 +140,6 @@ _vagrant() {
|
|||
;;
|
||||
esac
|
||||
;;
|
||||
"snapshot")
|
||||
case "$prev" in
|
||||
"restore"|"delete")
|
||||
local snapshot_list=$(vagrant snapshot list)
|
||||
COMPREPLY=($(compgen -W "${snapshot_list}" -- ${cur}))
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
|
|
@ -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_DLOAD = /bin/launchctl load -w /System/Library/LaunchDaemons/com.apple.smbd.plist
|
||||
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
|
130
lib/vagrant.rb
130
lib/vagrant.rb
|
@ -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 "rubygems"
|
||||
require "log4r"
|
||||
require "vagrant/util"
|
||||
require "vagrant/plugin/manager"
|
||||
|
||||
# Enable logging if it is requested. We do this 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
|
||||
# logs as long as we have a valid level.
|
||||
if level
|
||||
# NOTE: We must do this little hack to allow
|
||||
# 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 = Log4r::Logger.new("vagrant")
|
||||
logger.outputters = Log4r::Outputter.stderr
|
||||
logger.level = level
|
||||
base_formatter = Log4r::BasicFormatter.new
|
||||
|
@ -68,11 +49,6 @@ if ENV["VAGRANT_LOG"] && ENV["VAGRANT_LOG"] != ""
|
|||
date_pattern: "%F %T"
|
||||
)
|
||||
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)
|
||||
logger = nil
|
||||
end
|
||||
|
@ -89,6 +65,12 @@ require 'i18n'
|
|||
# there are issues with ciphers not being properly loaded.
|
||||
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
|
||||
require 'vagrant/version'
|
||||
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 }
|
||||
end
|
||||
|
||||
# Make the requirement object
|
||||
version = Gem::Requirement.new([version]) if version
|
||||
|
||||
# Now check the plugin gem names
|
||||
require "vagrant/plugin/manager"
|
||||
Plugin::Manager.instance.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
|
||||
|
||||
# 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."
|
||||
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
|
||||
# required. You can specify a list of requirements which will all be checked
|
||||
# against the running Vagrant version.
|
||||
|
@ -245,7 +223,8 @@ module Vagrant
|
|||
logger = Log4r::Logger.new("vagrant::root")
|
||||
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!")
|
||||
return
|
||||
end
|
||||
|
@ -289,6 +268,35 @@ else
|
|||
global_logger.warn("resolv replacement has not been enabled!")
|
||||
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.
|
||||
plugin_load_proc = lambda do |directory|
|
||||
# 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
|
||||
directory.children(true).each(&plugin_load_proc)
|
||||
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
|
||||
|
|
|
@ -15,7 +15,6 @@ module Vagrant
|
|||
autoload :Confirm, "vagrant/action/builtin/confirm"
|
||||
autoload :ConfigValidate, "vagrant/action/builtin/config_validate"
|
||||
autoload :DestroyConfirm, "vagrant/action/builtin/destroy_confirm"
|
||||
autoload :Disk, "vagrant/action/builtin/disk"
|
||||
autoload :EnvSet, "vagrant/action/builtin/env_set"
|
||||
autoload :GracefulHalt, "vagrant/action/builtin/graceful_halt"
|
||||
autoload :HandleBox, "vagrant/action/builtin/handle_box"
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -348,15 +348,9 @@ module Vagrant
|
|||
end
|
||||
|
||||
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"))
|
||||
validate_checksum(
|
||||
opts[:checksum_type], opts[:checksum], box_url)
|
||||
end
|
||||
env[:ui].detail(I18n.t("vagrant.actions.box.add.checksumming"))
|
||||
validate_checksum(
|
||||
opts[:checksum_type], opts[:checksum], box_url)
|
||||
end
|
||||
|
||||
# Add the box!
|
||||
|
@ -533,11 +527,22 @@ module Vagrant
|
|||
end
|
||||
|
||||
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}")
|
||||
|
||||
actual = FileChecksum.new(path, checksum_type).checksum
|
||||
@logger.info("Actual checksum: #{actual}")
|
||||
actual = FileChecksum.new(path, checksum_klass).checksum
|
||||
if actual.casecmp(checksum) != 0
|
||||
raise Errors::BoxChecksumMismatch,
|
||||
actual: actual,
|
||||
|
|
|
@ -40,7 +40,6 @@ module Vagrant
|
|||
# Have download options specified in the environment override
|
||||
# options specified for the machine.
|
||||
download_options = {
|
||||
automatic_check: !env[:box_outdated_force],
|
||||
ca_cert: env[:ca_cert] || machine.config.vm.box_download_ca_cert,
|
||||
ca_path: env[:ca_path] || machine.config.vm.box_download_ca_path,
|
||||
client_cert: env[:client_cert] ||
|
||||
|
@ -51,8 +50,7 @@ module Vagrant
|
|||
|
||||
env[:ui].output(I18n.t(
|
||||
"vagrant.box_outdated_checking_with_refresh",
|
||||
name: box.name,
|
||||
version: box.version))
|
||||
name: box.name))
|
||||
update = nil
|
||||
begin
|
||||
update = box.has_update?(constraints, download_options: download_options)
|
||||
|
@ -60,9 +58,6 @@ module Vagrant
|
|||
env[:ui].warn(I18n.t(
|
||||
"vagrant.box_outdated_metadata_download_error",
|
||||
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
|
||||
raise if !env[:box_outdated_ignore_errors]
|
||||
env[:ui].detail(I18n.t(
|
||||
|
@ -70,23 +65,15 @@ module Vagrant
|
|||
message: e.message))
|
||||
end
|
||||
env[:box_outdated] = update != nil
|
||||
local_update = check_outdated_local(env)
|
||||
if update && (local_update.nil? || (local_update.version < update[1].version))
|
||||
if update
|
||||
env[:ui].warn(I18n.t(
|
||||
"vagrant.box_outdated_single",
|
||||
name: update[0].name,
|
||||
provider: box.provider,
|
||||
current: box.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
|
||||
env[:box_outdated] = false
|
||||
check_outdated_local(env)
|
||||
end
|
||||
|
||||
@app.call(env)
|
||||
|
@ -101,8 +88,19 @@ module Vagrant
|
|||
version ||= ""
|
||||
version += "> #{machine.box.version}"
|
||||
|
||||
env[:box_collection].find(
|
||||
box = env[:box_collection].find(
|
||||
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
|
||||
|
|
|
@ -99,11 +99,8 @@ module Vagrant
|
|||
b.use Confirm, message, force_key
|
||||
end
|
||||
|
||||
# Keep used boxes, even if "force" is applied
|
||||
keep_used_boxes = env[:keep_used_boxes]
|
||||
|
||||
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
|
||||
next
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ module Vagrant
|
|||
|
||||
def call(env)
|
||||
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?
|
||||
raise Errors::ConfigInvalid,
|
||||
|
|
|
@ -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
|
|
@ -25,114 +25,19 @@ module Vagrant
|
|||
# Store in the type map so that --provision-with works properly
|
||||
@_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
|
||||
options = {
|
||||
name: provisioner_name,
|
||||
name: provisioner.name,
|
||||
run: provisioner.run,
|
||||
before: provisioner.before,
|
||||
after: provisioner.after,
|
||||
}
|
||||
|
||||
# Return the result
|
||||
[result, options]
|
||||
end
|
||||
|
||||
@_provisioner_instances = sort_provisioner_instances(@_provisioner_instances)
|
||||
return @_provisioner_instances.compact
|
||||
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
|
||||
# type.
|
||||
def provisioner_type_map(env)
|
||||
|
@ -142,13 +47,6 @@ module Vagrant
|
|||
# Return the type map
|
||||
@_provisioner_types
|
||||
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
|
||||
|
|
|
@ -97,14 +97,8 @@ module Vagrant
|
|||
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|
|
||||
f.write(folder_data)
|
||||
f.write(JSON.dump(folders))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -36,35 +36,17 @@ module Vagrant
|
|||
|
||||
# Get the command and wrap it in a login shell
|
||||
command = ShellQuote.escape(env[:ssh_run_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
|
||||
command = "#{env[:machine].config.ssh.shell} -c '#{command}'"
|
||||
|
||||
# Execute!
|
||||
opts = env[:ssh_opts] || {}
|
||||
opts[:extra_args] ||= []
|
||||
|
||||
# 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") &&
|
||||
!opts[:extra_args].include?("-T") &&
|
||||
env[:tty] &&
|
||||
env[:machine].config.vm.communicator != :winssh
|
||||
env[:tty]
|
||||
opts[:extra_args] << "-t"
|
||||
end
|
||||
|
||||
|
|
|
@ -80,10 +80,8 @@ module Vagrant
|
|||
@app.call(env)
|
||||
|
||||
@env[:ui].info I18n.t("vagrant.actions.general.package.compressing", fullpath: fullpath)
|
||||
|
||||
copy_include_files
|
||||
setup_private_key
|
||||
write_metadata_json
|
||||
compress
|
||||
end
|
||||
|
||||
|
@ -153,22 +151,6 @@ module Vagrant
|
|||
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
|
||||
# it for SSH by default. We have to do this because we now generate
|
||||
# random keypairs on boot, so packaged boxes would stop working
|
||||
|
|
|
@ -2,7 +2,6 @@ require 'log4r'
|
|||
|
||||
require 'vagrant/action/hook'
|
||||
require 'vagrant/util/busy'
|
||||
require 'vagrant/util/experimental'
|
||||
|
||||
module Vagrant
|
||||
module Action
|
||||
|
@ -34,19 +33,6 @@ module Vagrant
|
|||
environment.merge!(@lazy_globals.call) if @lazy_globals
|
||||
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
|
||||
hooks = Vagrant.plugin("2").manager.action_hooks(environment[:action_name])
|
||||
if !hooks.empty?
|
||||
|
@ -66,43 +52,19 @@ module Vagrant
|
|||
ui = environment[:ui] if environment.key?(:ui)
|
||||
int_callback = lambda do
|
||||
if environment[:interrupted]
|
||||
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
|
||||
ui.error I18n.t("vagrant.actions.runner.exit_immediately") if ui
|
||||
abort
|
||||
end
|
||||
|
||||
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
|
||||
ui.warn I18n.t("vagrant.actions.runner.waiting_cleanup") if ui && !@@reported_interrupt
|
||||
environment[:interrupted] = true
|
||||
@@reported_interrupt = true
|
||||
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
|
||||
@logger.info("Running action: #{environment[:action_name]} #{callable_id}")
|
||||
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
|
||||
# the caller wants to use.
|
||||
environment
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
require "log4r"
|
||||
require 'vagrant/util/experimental'
|
||||
require 'vagrant/action/builtin/before_trigger'
|
||||
require 'vagrant/action/builtin/after_trigger'
|
||||
|
||||
module Vagrant
|
||||
module Action
|
||||
|
@ -19,21 +16,8 @@ module Vagrant
|
|||
attr_accessor :actions, :stack
|
||||
|
||||
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 = []
|
||||
@actions = actions.map { |m| finalize_action(m, env) }.flatten
|
||||
@actions = actions.map { |m| finalize_action(m, env) }
|
||||
@logger = Log4r::Logger.new("vagrant::action::warden")
|
||||
@last_error = nil
|
||||
end
|
||||
|
@ -47,16 +31,7 @@ module Vagrant
|
|||
raise Errors::VagrantInterrupt if env[:interrupted]
|
||||
action = @actions.shift
|
||||
@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)
|
||||
env[:hook].call("after_#{hook_name}".to_sym) if hook_name
|
||||
|
||||
raise Errors::VagrantInterrupt if env[:interrupted]
|
||||
@logger.info("Calling OUT action: #{action}")
|
||||
rescue SystemExit
|
||||
|
@ -112,17 +87,7 @@ module Vagrant
|
|||
if klass.is_a?(Class)
|
||||
# A action klass which is to be instantiated with the
|
||||
# app, env, and any arguments given
|
||||
|
||||
# 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)]
|
||||
klass.new(self, env, *args, &block)
|
||||
elsif klass.respond_to?(:call)
|
||||
# Make it a lambda which calls the item then forwards
|
||||
# up the chain
|
||||
|
|
|
@ -71,10 +71,6 @@ module Vagrant
|
|||
thread = Thread.new do
|
||||
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
|
||||
# we've forked...
|
||||
start_pid = Process.pid
|
||||
|
@ -164,16 +160,6 @@ module Vagrant
|
|||
if !errors.empty?
|
||||
raise Errors::BatchMultiError, message: errors.join("\n\n")
|
||||
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
|
||||
|
|
|
@ -16,9 +16,6 @@ module Vagrant
|
|||
class Box
|
||||
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.
|
||||
#
|
||||
# @return [String]
|
||||
|
@ -157,11 +154,6 @@ module Vagrant
|
|||
raise Errors::BoxUpdateNoMetadata, name: @name
|
||||
end
|
||||
|
||||
if download_options.delete(:automatic_check) && !automatic_update_check_allowed?
|
||||
@logger.info("Skipping box update check")
|
||||
return
|
||||
end
|
||||
|
||||
version += ", " if version
|
||||
version ||= ""
|
||||
version += "> #{@version}"
|
||||
|
@ -172,25 +164,6 @@ module Vagrant
|
|||
[md, newer, newer.provider(@provider)]
|
||||
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.
|
||||
#
|
||||
# @param [Pathname] path The full path (filename included) of where
|
||||
|
|
|
@ -116,7 +116,7 @@ module Vagrant
|
|||
# Extract the box into a temporary directory.
|
||||
@logger.debug("Unpacking box into temporary directory: #{temp_dir}")
|
||||
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
|
||||
raise Errors::BoxUnpackageFailure,
|
||||
output: result.stderr.to_s
|
||||
|
@ -233,23 +233,14 @@ module Vagrant
|
|||
version = versiondir.basename.to_s
|
||||
|
||||
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
|
||||
# correct enough then include it.
|
||||
if provider.directory? && provider.join("metadata.json").file?
|
||||
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]
|
||||
else
|
||||
@logger.debug("Invalid box #{box_name}, ignoring: #{provider}")
|
||||
@logger.debug("Invalid box, ignoring: #{provider}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -68,25 +68,11 @@ module Vagrant
|
|||
|
||||
# Returns all the versions supported by this metadata. These
|
||||
# versions are sorted so the last element of the list is the
|
||||
# latest version. Optionally filter versions by a matching
|
||||
# provider.
|
||||
# latest version.
|
||||
#
|
||||
# @return[Array<String>]
|
||||
def versions(**opts)
|
||||
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)
|
||||
end
|
||||
def versions
|
||||
@version_map.keys.sort.map(&:to_s)
|
||||
end
|
||||
|
||||
# Represents a single version within the metadata.
|
||||
|
|
|
@ -24,65 +24,30 @@ module Vagrant
|
|||
|
||||
# Default gem repositories
|
||||
DEFAULT_GEM_SOURCES = [
|
||||
HASHICORP_GEMSTORE,
|
||||
"https://rubygems.org/".freeze
|
||||
"https://rubygems.org/".freeze,
|
||||
HASHICORP_GEMSTORE
|
||||
].freeze
|
||||
|
||||
def self.instance
|
||||
@bundler ||= self.new
|
||||
end
|
||||
|
||||
# @return [Pathname] Global plugin path
|
||||
attr_reader :plugin_gem_path
|
||||
# @return [Pathname] Vagrant environment specific plugin path
|
||||
attr_reader :env_plugin_gem_path
|
||||
|
||||
def initialize
|
||||
@plugin_gem_path = Vagrant.user_data_path.join("gems", RUBY_VERSION).freeze
|
||||
@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
|
||||
|
||||
# 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)
|
||||
if !@initial_specifications
|
||||
@initial_specifications = Gem::Specification.find_all{true}
|
||||
else
|
||||
Gem::Specification.all = @initial_specifications
|
||||
Gem::Specification.reset
|
||||
end
|
||||
|
||||
# Add HashiCorp RubyGems source
|
||||
if !Gem.sources.include?(HASHICORP_GEMSTORE)
|
||||
sources = [HASHICORP_GEMSTORE] + Gem.sources.sources
|
||||
Gem.sources.replace(sources)
|
||||
end
|
||||
Gem.sources << HASHICORP_GEMSTORE
|
||||
|
||||
# Generate dependencies for all registered plugins
|
||||
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
|
||||
|
||||
@logger.debug("Current generated plugin dependency list: #{plugin_deps}")
|
||||
|
@ -113,7 +78,7 @@ module Vagrant
|
|||
# Activate the gems
|
||||
activate_solution(solution)
|
||||
|
||||
full_vagrant_spec_list = @initial_specifications +
|
||||
full_vagrant_spec_list = Gem::Specification.find_all{true} +
|
||||
solution.map(&:full_spec)
|
||||
|
||||
if(defined?(::Bundler))
|
||||
|
@ -126,7 +91,6 @@ module Vagrant
|
|||
end
|
||||
|
||||
Gem::Specification.reset
|
||||
nil
|
||||
end
|
||||
|
||||
# Removes any temporary files created by init
|
||||
|
@ -137,10 +101,9 @@ module Vagrant
|
|||
# Installs the list of plugins.
|
||||
#
|
||||
# @param [Hash] plugins
|
||||
# @param [Boolean] env_local Environment local plugin install
|
||||
# @return [Array<Gem::Specification>]
|
||||
def install(plugins, env_local=false)
|
||||
internal_install(plugins, nil, env_local: env_local)
|
||||
def install(plugins, local=false)
|
||||
internal_install(plugins, nil, local: local)
|
||||
end
|
||||
|
||||
# Installs a local '*.gem' file so that Bundler can find it.
|
||||
|
@ -157,7 +120,7 @@ module Vagrant
|
|||
}
|
||||
}
|
||||
@logger.debug("Installing local plugin - #{plugin_info}")
|
||||
internal_install(plugin_info, nil, env_local: opts[:env_local])
|
||||
internal_install(plugin_info, {})
|
||||
plugin_source.spec
|
||||
end
|
||||
|
||||
|
@ -166,14 +129,14 @@ module Vagrant
|
|||
# @param [Hash] plugins
|
||||
# @param [Array<String>] specific Specific plugin names to update. If
|
||||
# empty or nil, all plugins will be updated.
|
||||
def update(plugins, specific, **opts)
|
||||
def update(plugins, specific)
|
||||
specific ||= []
|
||||
update = opts.merge({gems: specific.empty? ? true : specific})
|
||||
update = {gems: specific.empty? ? true : specific}
|
||||
internal_install(plugins, update)
|
||||
end
|
||||
|
||||
# Clean removes any unused gems.
|
||||
def clean(plugins, **opts)
|
||||
def clean(plugins)
|
||||
@logger.debug("Cleaning Vagrant plugins of stale gems.")
|
||||
# Generate dependencies for all registered plugins
|
||||
plugin_deps = plugins.map do |name, info|
|
||||
|
@ -200,13 +163,6 @@ module Vagrant
|
|||
Gem::Specification.load(spec_path)
|
||||
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.")
|
||||
|
||||
# Resolve the request set to ensure proper activation order
|
||||
|
@ -215,27 +171,11 @@ module Vagrant
|
|||
solution_full_names = solution_specs.map(&:full_name)
|
||||
|
||||
# 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|
|
||||
solution_full_names.include?(spec.full_name)
|
||||
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)}")
|
||||
|
||||
# Now delete all unused specs
|
||||
|
@ -302,19 +242,13 @@ module Vagrant
|
|||
end
|
||||
source_list[name] << source
|
||||
end
|
||||
Gem::Dependency.new(name, *gem_version.split(","))
|
||||
Gem::Dependency.new(name, gem_version)
|
||||
end
|
||||
|
||||
if Vagrant.strict_dependency_enforcement
|
||||
@logger.debug("Enabling strict dependency enforcement")
|
||||
plugin_deps += vagrant_internal_specs.map do |spec|
|
||||
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)
|
||||
end.compact
|
||||
else
|
||||
|
@ -369,61 +303,28 @@ module Vagrant
|
|||
solution = request_set.resolve(installer_set)
|
||||
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.")
|
||||
|
||||
# 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
|
||||
# dependencies are satisfied by gems in the install directory (which will likely not
|
||||
# be true)
|
||||
install_path = extra[:env_local] ? env_plugin_gem_path : plugin_gem_path
|
||||
result = request_set.install_into(install_path.to_s, true,
|
||||
result = request_set.install_into(plugin_gem_path.to_s, true,
|
||||
ignore_dependencies: true,
|
||||
prerelease: Vagrant.prerelease?,
|
||||
wrappers: true
|
||||
)
|
||||
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
|
||||
end
|
||||
|
||||
# Generate the composite resolver set totally all of vagrant (builtin + plugin set)
|
||||
def generate_vagrant_set
|
||||
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)
|
||||
Gem::Resolver.compose_sets(generate_builtin_set, generate_plugin_set)
|
||||
end
|
||||
|
||||
# @return [Array<[Gem::Specification]>] spec list
|
||||
# @return [Array<[Gem::Specification, String]>] spec and directory pairs
|
||||
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 = {}
|
||||
directories = [Gem::Specification.default_specifications_dir]
|
||||
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
|
||||
# full) that should be ignored
|
||||
#
|
||||
# @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) } || []
|
||||
def generate_plugin_set(skip=[])
|
||||
plugin_set = PluginSet.new
|
||||
@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)
|
||||
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
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
require 'log4r'
|
||||
require 'optparse'
|
||||
|
||||
require 'vagrant/util/experimental'
|
||||
|
||||
module Vagrant
|
||||
# Manages the command line interface to Vagrant.
|
||||
class CLI < Vagrant.plugin("2", :command)
|
||||
|
@ -13,11 +11,6 @@ module Vagrant
|
|||
@logger = Log4r::Logger.new("vagrant::cli")
|
||||
@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
|
||||
@logger.info("CLI: #{@main_args.inspect} #{@sub_command.inspect} #{@sub_args.inspect}")
|
||||
end
|
||||
|
@ -30,10 +23,6 @@ module Vagrant
|
|||
return 0
|
||||
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,
|
||||
# then we also just print the help and exit.
|
||||
command_plugin = nil
|
||||
|
@ -62,9 +51,7 @@ module Vagrant
|
|||
# Initialize and execute the command class, returning the exit status.
|
||||
result = 0
|
||||
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
|
||||
@triggers.fire_triggers(@sub_command.to_sym, :after, nil, :command) if Vagrant::Util::Experimental.feature_enabled?("typed_triggers")
|
||||
rescue Interrupt
|
||||
@env.ui.info(I18n.t("vagrant.cli_interrupt"))
|
||||
result = 1
|
||||
|
|
|
@ -129,7 +129,7 @@ module Vagrant
|
|||
path = "(unknown)"
|
||||
if e.backtrace && e.backtrace[0]
|
||||
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|
|
||||
if part =~ /\d+/
|
||||
line = part.to_i
|
||||
|
|
|
@ -60,18 +60,14 @@ module Vagrant
|
|||
#
|
||||
# @param [Environment] env
|
||||
# @return [Hash]
|
||||
def validate(machine, ignore_provider=nil)
|
||||
def validate(machine)
|
||||
# Go through each of the configuration keys and validate
|
||||
errors = {}
|
||||
@keys.each do |_key, instance|
|
||||
if instance.respond_to?(:validate)
|
||||
# Validate this single item, and if we have errors then
|
||||
# we merge them into our total errors list.
|
||||
if _key == :vm
|
||||
result = instance.validate(machine, ignore_provider)
|
||||
else
|
||||
result = instance.validate(machine)
|
||||
end
|
||||
result = instance.validate(machine)
|
||||
if result && !result.empty?
|
||||
errors = Util.merge_errors(errors, result)
|
||||
end
|
||||
|
|
|
@ -8,7 +8,6 @@ require 'log4r'
|
|||
|
||||
require 'vagrant/util/file_mode'
|
||||
require 'vagrant/util/platform'
|
||||
require 'vagrant/util/hash_with_indifferent_access'
|
||||
require "vagrant/util/silence_warnings"
|
||||
require "vagrant/vagrantfile"
|
||||
require "vagrant/version"
|
||||
|
@ -147,7 +146,6 @@ module Vagrant
|
|||
if opts[:local_data_path]
|
||||
@local_data_path = Pathname.new(File.expand_path(opts[:local_data_path], @cwd))
|
||||
end
|
||||
|
||||
@logger.debug("Effective local data path: #{@local_data_path}")
|
||||
|
||||
# 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")
|
||||
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
|
||||
# by using a "clean" action runner
|
||||
hook(:environment_plugins_loaded, runner: Action::Runner.new(env: self))
|
||||
|
@ -208,8 +194,7 @@ module Vagrant
|
|||
home_path: home_path,
|
||||
root_path: root_path,
|
||||
tmp_path: tmp_path,
|
||||
ui: @ui,
|
||||
env: self
|
||||
ui: @ui
|
||||
}
|
||||
end
|
||||
end
|
||||
|
@ -334,7 +319,7 @@ module Vagrant
|
|||
# then look there.
|
||||
root_config = vagrantfile.config
|
||||
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]
|
||||
end
|
||||
|
||||
|
@ -905,13 +890,6 @@ module Vagrant
|
|||
begin
|
||||
@logger.debug("Creating: #{@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
|
||||
raise Errors::LocalDataDirectoryNotAccessible,
|
||||
local_data_path: @local_data_path.to_s
|
||||
|
@ -920,114 +898,6 @@ module Vagrant
|
|||
|
||||
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
|
||||
# doesn't already exist.
|
||||
#
|
||||
|
|
|
@ -160,10 +160,6 @@ module Vagrant
|
|||
error_key(:box_config_changing_box)
|
||||
end
|
||||
|
||||
class BoxFileNotExist < VagrantError
|
||||
error_key(:box_file_not_exist)
|
||||
end
|
||||
|
||||
class BoxMetadataCorrupted < VagrantError
|
||||
error_key(:box_metadata_corrupted)
|
||||
end
|
||||
|
@ -304,10 +300,6 @@ module Vagrant
|
|||
error_key(:command_deprecated)
|
||||
end
|
||||
|
||||
class CommandSuspendAllArgs < VagrantError
|
||||
error_key(:command_suspend_all_arguments)
|
||||
end
|
||||
|
||||
class CommandUnavailable < VagrantError
|
||||
error_key(:command_unavailable)
|
||||
end
|
||||
|
@ -436,10 +428,6 @@ module Vagrant
|
|||
error_key(:machine_action_locked)
|
||||
end
|
||||
|
||||
class MachineFolderNotAccessible < VagrantError
|
||||
error_key(:machine_folder_not_accessible)
|
||||
end
|
||||
|
||||
class MachineGuestNotReady < VagrantError
|
||||
error_key(:machine_guest_not_ready)
|
||||
end
|
||||
|
@ -592,10 +580,6 @@ module Vagrant
|
|||
error_key(:provider_not_found)
|
||||
end
|
||||
|
||||
class ProviderNotFoundSuggestion < VagrantError
|
||||
error_key(:provider_not_found_suggestion)
|
||||
end
|
||||
|
||||
class ProviderNotUsable < VagrantError
|
||||
error_key(:provider_not_usable)
|
||||
end
|
||||
|
@ -648,14 +632,6 @@ module Vagrant
|
|||
error_key(:plugin_source_error)
|
||||
end
|
||||
|
||||
class PluginNoLocalError < VagrantError
|
||||
error_key(:plugin_no_local_error)
|
||||
end
|
||||
|
||||
class PluginMissingLocalError < VagrantError
|
||||
error_key(:plugin_missing_local_error)
|
||||
end
|
||||
|
||||
class PushesNotDefined < VagrantError
|
||||
error_key(:pushes_not_defined)
|
||||
end
|
||||
|
@ -672,10 +648,6 @@ module Vagrant
|
|||
error_key(:push_strategy_not_provided)
|
||||
end
|
||||
|
||||
class RSyncPostCommandError < VagrantError
|
||||
error_key(:rsync_post_command_error)
|
||||
end
|
||||
|
||||
class RSyncError < VagrantError
|
||||
error_key(:rsync_error)
|
||||
end
|
||||
|
@ -804,26 +776,6 @@ module Vagrant
|
|||
error_key(:synced_folder_unusable)
|
||||
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
|
||||
error_key(:ui_expects_tty)
|
||||
end
|
||||
|
@ -832,30 +784,6 @@ module Vagrant
|
|||
error_key(:unimplemented_provider_action)
|
||||
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
|
||||
error_key(:interrupted)
|
||||
end
|
||||
|
@ -900,6 +828,10 @@ module Vagrant
|
|||
error_key(:vboxmanage_not_found_error)
|
||||
end
|
||||
|
||||
class VBoxManageNotFoundWSLError < VagrantError
|
||||
error_key(:vboxmanage_not_found_wsl_error)
|
||||
end
|
||||
|
||||
class VirtualBoxBrokenVersion040214 < VagrantError
|
||||
error_key(:virtualbox_broken_version_040214)
|
||||
end
|
||||
|
@ -1008,10 +940,6 @@ module Vagrant
|
|||
error_key(:power_off, "vagrant.actions.vm.export")
|
||||
end
|
||||
|
||||
class WinRMInvalidCommunicator < VagrantError
|
||||
error_key(:winrm_invalid_communicator)
|
||||
end
|
||||
|
||||
class WSLVagrantVersionMismatch < VagrantError
|
||||
error_key(:wsl_vagrant_version_mismatch)
|
||||
end
|
||||
|
|
|
@ -110,7 +110,6 @@ module Vagrant
|
|||
@ui = Vagrant::UI::Prefixed.new(@env.ui, @name)
|
||||
@ui_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
|
||||
@id = nil
|
||||
|
@ -160,8 +159,6 @@ module Vagrant
|
|||
# as extra data set on the environment hash for the middleware
|
||||
# runner.
|
||||
def action(name, opts=nil)
|
||||
@triggers.fire_triggers(name, :before, @name.to_s, :action)
|
||||
|
||||
@logger.info("Calling action: #{name} on provider #{@provider}")
|
||||
|
||||
opts ||= {}
|
||||
|
@ -172,10 +169,6 @@ module Vagrant
|
|||
|
||||
# Extra env keys are the remaining opts
|
||||
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
|
||||
|
||||
|
@ -192,7 +185,7 @@ module Vagrant
|
|||
locker = @env.method(:lock) if lock && !name.to_s.start_with?("ssh")
|
||||
|
||||
# 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.
|
||||
callable = @provider.action(name)
|
||||
|
||||
|
@ -210,10 +203,6 @@ module Vagrant
|
|||
ui.machine("action", name.to_s, "end")
|
||||
action_result
|
||||
end
|
||||
|
||||
@triggers.fire_triggers(name, :after, @name.to_s, :action)
|
||||
# preserve returning environment after machine action runs
|
||||
return return_env
|
||||
rescue Errors::EnvironmentLockedError
|
||||
raise Errors::MachineActionLockedError,
|
||||
action: name,
|
||||
|
@ -399,10 +388,7 @@ module Vagrant
|
|||
# Read the id file from the data directory if it exists as the
|
||||
# ID for the pre-existing physical representation of this machine.
|
||||
id_file = @data_dir.join("id")
|
||||
id_content = id_file.read.strip if id_file.file?
|
||||
if !id_content.to_s.empty?
|
||||
@id = id_content
|
||||
end
|
||||
@id = id_file.read.chomp if id_file.file?
|
||||
end
|
||||
|
||||
if @id != old_id && @provider
|
||||
|
@ -454,11 +440,9 @@ module Vagrant
|
|||
info[:keys_only] ||= @config.ssh.default.keys_only
|
||||
info[:verify_host_key] ||= @config.ssh.default.verify_host_key
|
||||
info[:username] ||= @config.ssh.default.username
|
||||
info[:remote_user] ||= @config.ssh.default.remote_user
|
||||
info[:compression] ||= @config.ssh.default.compression
|
||||
info[:dsa_authentication] ||= @config.ssh.default.dsa_authentication
|
||||
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
|
||||
# provider-returned data.
|
||||
|
@ -470,9 +454,7 @@ module Vagrant
|
|||
info[:dsa_authentication] = @config.ssh.dsa_authentication
|
||||
info[:username] = @config.ssh.username if @config.ssh.username
|
||||
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[:config] = @config.ssh.config if @config.ssh.config
|
||||
|
||||
# We also set some fields that are purely controlled by Vagrant
|
||||
info[:forward_agent] = @config.ssh.forward_agent
|
||||
|
@ -599,7 +581,7 @@ module Vagrant
|
|||
).chomp
|
||||
end
|
||||
|
||||
if !File.identical?(vagrant_cwd.to_s, @env.root_path.to_s)
|
||||
if vagrant_cwd != @env.root_path.to_s
|
||||
if vagrant_cwd
|
||||
ui.warn(I18n.t(
|
||||
'vagrant.moved_cwd',
|
||||
|
|
|
@ -27,86 +27,13 @@ module Vagrant
|
|||
@instance ||= self.new(user_plugins_file)
|
||||
end
|
||||
|
||||
attr_reader :user_file
|
||||
attr_reader :system_file
|
||||
attr_reader :local_file
|
||||
|
||||
# @param [Pathname] user_file
|
||||
def initialize(user_file)
|
||||
@logger = Log4r::Logger.new("vagrant::plugin::manager")
|
||||
@user_file = StateFile.new(user_file)
|
||||
|
||||
system_path = self.class.system_plugins_file
|
||||
@system_file = nil
|
||||
@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
|
||||
|
||||
# Installs another plugin into our gem directory.
|
||||
|
@ -114,10 +41,7 @@ module Vagrant
|
|||
# @param [String] name Name of the plugin (gem)
|
||||
# @return [Gem::Specification]
|
||||
def install_plugin(name, **opts)
|
||||
if opts[:env_local] && @local_file.nil?
|
||||
raise Errors::PluginNoLocalError
|
||||
end
|
||||
|
||||
local = false
|
||||
if name =~ /\.gem$/
|
||||
# If this is a gem file, then we install that gem locally.
|
||||
local_spec = Vagrant::Bundler.instance.install_local(name, opts)
|
||||
|
@ -135,7 +59,7 @@ module Vagrant
|
|||
if local_spec.nil?
|
||||
result = nil
|
||||
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 result && result.version >= spec.version
|
||||
result = spec
|
||||
|
@ -151,20 +75,18 @@ module Vagrant
|
|||
result = local_spec
|
||||
end
|
||||
# Add the plugin to the state file
|
||||
plugin_file = opts[:env_local] ? @local_file : @user_file
|
||||
plugin_file.add_plugin(
|
||||
@user_file.add_plugin(
|
||||
result.name,
|
||||
version: opts[:version],
|
||||
require: opts[:require],
|
||||
sources: opts[:sources],
|
||||
env_local: !!opts[:env_local],
|
||||
installed_gem_version: result.version.to_s
|
||||
)
|
||||
|
||||
# After install clean plugin gems to remove any cruft. This is useful
|
||||
# for removing outdated dependencies or other versions of an installed
|
||||
# plugin if the plugin is upgraded/downgraded
|
||||
Vagrant::Bundler.instance.clean(installed_plugins, local: !!opts[:local])
|
||||
Vagrant::Bundler.instance.clean(installed_plugins)
|
||||
result
|
||||
rescue Gem::GemNotFoundException
|
||||
raise Errors::PluginGemNotFound, name: name
|
||||
|
@ -175,7 +97,7 @@ module Vagrant
|
|||
# Uninstalls the plugin with the given name.
|
||||
#
|
||||
# @param [String] name
|
||||
def uninstall_plugin(name, **opts)
|
||||
def uninstall_plugin(name)
|
||||
if @system_file
|
||||
if !@user_file.has_plugin?(name) && @system_file.has_plugin?(name)
|
||||
raise Errors::PluginUninstallSystem,
|
||||
|
@ -183,18 +105,7 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
if opts[:env_local] && @local_file.nil?
|
||||
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)
|
||||
@user_file.remove_plugin(name)
|
||||
|
||||
# Clean the environment, removing any old plugins
|
||||
Vagrant::Bundler.instance.clean(installed_plugins)
|
||||
|
@ -203,15 +114,9 @@ module Vagrant
|
|||
end
|
||||
|
||||
# Updates all or a specific set of plugins.
|
||||
def update_plugins(specific, **opts)
|
||||
if opts[:env_local] && @local_file.nil?
|
||||
raise Errors::PluginNoLocalError
|
||||
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|
|
||||
def update_plugins(specific)
|
||||
result = Vagrant::Bundler.instance.update(installed_plugins, specific)
|
||||
installed_plugins.each do |name, info|
|
||||
matching_spec = result.detect{|s| s.name == name}
|
||||
info = Hash[
|
||||
info.map do |key, value|
|
||||
|
@ -219,7 +124,7 @@ module Vagrant
|
|||
end
|
||||
]
|
||||
if matching_spec
|
||||
plugin_file.add_plugin(name, **info.merge(
|
||||
@user_file.add_plugin(name, **info.merge(
|
||||
version: "> 0",
|
||||
installed_gem_version: matching_spec.version.to_s
|
||||
))
|
||||
|
@ -243,11 +148,6 @@ module Vagrant
|
|||
end
|
||||
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
|
||||
Hash[
|
||||
plugin_list.map{|plugin_name, plugin_info|
|
||||
|
@ -291,84 +191,6 @@ module Vagrant
|
|||
|
||||
installed_map.values
|
||||
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
|
||||
|
|
|
@ -7,10 +7,6 @@ module 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.
|
||||
class StateFile
|
||||
|
||||
# @return [Pathname] path to file
|
||||
attr_reader :path
|
||||
|
||||
def initialize(path)
|
||||
@path = path
|
||||
|
||||
|
@ -40,8 +36,7 @@ module Vagrant
|
|||
"gem_version" => opts[:version] || "",
|
||||
"require" => opts[:require] || "",
|
||||
"sources" => opts[:sources] || [],
|
||||
"installed_gem_version" => opts[:installed_gem_version],
|
||||
"env_local" => !!opts[:env_local]
|
||||
"installed_gem_version" => opts[:installed_gem_version]
|
||||
}
|
||||
|
||||
save!
|
||||
|
|
|
@ -19,7 +19,6 @@ module Vagrant
|
|||
autoload :Push, "vagrant/plugin/v2/push"
|
||||
autoload :Provisioner, "vagrant/plugin/v2/provisioner"
|
||||
autoload :SyncedFolder, "vagrant/plugin/v2/synced_folder"
|
||||
autoload :Trigger, "vagrant/plugin/v2/trigger"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -45,7 +45,7 @@ module Vagrant
|
|||
def parse_options(opts=nil)
|
||||
# make sure optparse doesn't use POSIXLY_CORRECT parsing
|
||||
ENV["POSIXLY_CORRECT"] = nil
|
||||
|
||||
|
||||
# Creating a shallow copy of the arguments so the OptionParser
|
||||
# doesn't destroy the originals.
|
||||
argv = @argv.dup
|
||||
|
|
|
@ -117,13 +117,6 @@ module Vagrant
|
|||
# @see #execute
|
||||
def test(command, opts=nil)
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -35,14 +35,6 @@ module Vagrant
|
|||
!!ENV["VAGRANT_INSTALLER_ENV"]
|
||||
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,
|
||||
# if there is one (if we're running in an installer).
|
||||
#
|
||||
|
@ -138,18 +130,6 @@ module Vagrant
|
|||
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
|
||||
#
|
||||
# @return [boolean] enabled or not
|
||||
|
|
|
@ -329,15 +329,10 @@ module Vagrant
|
|||
target = opts[:target] if opts.key?(: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]
|
||||
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
|
||||
lines = message.split("\n") if message != ""
|
||||
|
||||
# Otherwise, make sure to prefix every line properly
|
||||
lines.map do |line|
|
||||
|
|
|
@ -162,8 +162,8 @@ module Vagrant
|
|||
ui = Vagrant::UI::Prefixed.new(env.ui, "vagrant")
|
||||
if latest_version > installed_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)
|
||||
env.ui.info("", channel: :error)
|
||||
ui.info(I18n.t("vagrant.version_upgrade_available", latest_version: latest_version))
|
||||
env.ui.info("")
|
||||
else
|
||||
@logger.debug("vagrant is currently up to date")
|
||||
end
|
||||
|
|
|
@ -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
|
|
@ -1,14 +1,11 @@
|
|||
require "uri"
|
||||
|
||||
require "log4r"
|
||||
require "digest"
|
||||
require "digest/md5"
|
||||
require "digest/sha1"
|
||||
require "vagrant/util/busy"
|
||||
require "vagrant/util/platform"
|
||||
require "vagrant/util/subprocess"
|
||||
require "vagrant/util/curl_helper"
|
||||
require "vagrant/util/file_checksum"
|
||||
|
||||
module Vagrant
|
||||
module Util
|
||||
|
@ -20,7 +17,13 @@ module Vagrant
|
|||
# are properly tracked.
|
||||
#
|
||||
# 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
|
||||
SILENCED_HOSTS = [
|
||||
|
@ -64,11 +67,8 @@ module Vagrant
|
|||
@location_trusted = options[:location_trusted]
|
||||
@checksums = {
|
||||
:md5 => options[:md5],
|
||||
:sha1 => options[:sha1],
|
||||
:sha256 => options[:sha256],
|
||||
:sha384 => options[:sha384],
|
||||
:sha512 => options[:sha512]
|
||||
}.compact
|
||||
:sha1 => options[:sha1]
|
||||
}
|
||||
end
|
||||
|
||||
# This executes the actual download, downloading the source file
|
||||
|
@ -88,7 +88,82 @@ module Vagrant
|
|||
# tell us output so we can parse it out.
|
||||
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
|
||||
|
||||
@logger.info("Downloader starting download: ")
|
||||
|
@ -164,23 +239,36 @@ module Vagrant
|
|||
# @option checksums [String] :sha1 Compare SHA1 checksum
|
||||
# @return [Boolean]
|
||||
def validate_download!(source, path, checksums)
|
||||
checksums.each do |type, expected|
|
||||
actual = FileChecksum.new(path, type).checksum
|
||||
@logger.debug("Validating checksum (#{type}) for #{source}. " \
|
||||
"expected: #{expected} actual: #{actual}")
|
||||
if actual.casecmp(expected) != 0
|
||||
raise Errors::DownloaderChecksumError.new(
|
||||
source: source,
|
||||
path: path,
|
||||
type: type,
|
||||
expected_checksum: expected,
|
||||
actual_checksum: actual
|
||||
)
|
||||
CHECKSUM_MAP.each do |type, klass|
|
||||
if checksums[type]
|
||||
result = checksum_file(klass, path)
|
||||
@logger.debug("Validating checksum (#{type}) for #{source}. " \
|
||||
"expected: #{checksums[type]} actual: #{result}")
|
||||
if checksums[type] != result
|
||||
raise Errors::DownloaderChecksumError.new(
|
||||
source: source,
|
||||
path: path,
|
||||
type: type,
|
||||
expected_checksum: checksums[type],
|
||||
actual_checksum: result
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
true
|
||||
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)
|
||||
options = options.dup
|
||||
options << subprocess_options
|
||||
|
@ -204,21 +292,11 @@ module Vagrant
|
|||
# show an error message.
|
||||
if result.exit_code != 0
|
||||
@logger.warn("Downloader exit code: #{result.exit_code}")
|
||||
check = result.stderr.match(/\n*curl:\s+\((?<code>\d+)\)\s*(?<error>.*)$/)
|
||||
if check && check[:code] == "416"
|
||||
# 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,
|
||||
code: result.exit_code,
|
||||
message: err_msg
|
||||
end
|
||||
parts = result.stderr.split(/\n*curl:\s+\(\d+\)\s*/, 2)
|
||||
parts[1] ||= ""
|
||||
raise Errors::DownloaderError,
|
||||
code: result.exit_code,
|
||||
message: parts[1].chomp
|
||||
end
|
||||
|
||||
result
|
||||
|
|
|
@ -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
|
|
@ -2,9 +2,6 @@
|
|||
# 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
|
||||
# documentation of structure of the class.
|
||||
|
||||
require "vagrant/errors"
|
||||
|
||||
class DigestClass
|
||||
def update(string); end
|
||||
def hexdigest; end
|
||||
|
@ -13,27 +10,13 @@ end
|
|||
class FileChecksum
|
||||
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
|
||||
# ``digest_klass`` should implement the ``DigestClass`` interface. Note
|
||||
# that the built-in Ruby digest classes duck type this properly:
|
||||
# Digest::MD5, Digest::SHA1, etc.
|
||||
def initialize(path, digest_klass)
|
||||
if digest_klass.is_a?(Class)
|
||||
@digest_klass = digest_klass
|
||||
else
|
||||
@digest_klass = load_digest(digest_klass)
|
||||
end
|
||||
|
||||
@path = path
|
||||
@digest_klass = digest_klass
|
||||
@path = path
|
||||
end
|
||||
|
||||
# This calculates the checksum of the file and returns it as a
|
||||
|
@ -57,18 +40,6 @@ class FileChecksum
|
|||
end
|
||||
end
|
||||
|
||||
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
|
||||
return digest.hexdigest
|
||||
end
|
||||
end
|
||||
|
|
|
@ -17,80 +17,44 @@ module Vagrant
|
|||
|
||||
# systemd-networkd.service is in use
|
||||
#
|
||||
# @param [Vagrant::Plugin::V2::Communicator] comm Guest communicator
|
||||
# @return [Boolean]
|
||||
def systemd_networkd?(comm)
|
||||
comm.test("systemctl -q is-active systemd-networkd.service", sudo: true)
|
||||
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)
|
||||
comm.test("sudo systemctl status systemd-networkd.service")
|
||||
end
|
||||
|
||||
# systemd hostname set is via hostnamectl
|
||||
#
|
||||
# @param [Vagrant::Plugin::V2::Communicator] comm Guest communicator
|
||||
# @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)
|
||||
comm.test("command -v hostnamectl && hostnamectl")
|
||||
comm.test("hostnamectl")
|
||||
end
|
||||
|
||||
## netplan helpers
|
||||
|
||||
# netplan is installed
|
||||
#
|
||||
# @param [Vagrant::Plugin::V2::Communicator] comm Guest communicator
|
||||
# @return [Boolean]
|
||||
def netplan?(comm)
|
||||
comm.test("command -v netplan")
|
||||
comm.test("netplan -h")
|
||||
end
|
||||
|
||||
## nmcli helpers
|
||||
|
||||
# nmcli is installed
|
||||
#
|
||||
# @param [Vagrant::Plugin::V2::Communicator] comm Guest communicator
|
||||
# @return [Boolean]
|
||||
def nmcli?(comm)
|
||||
comm.test("command -v nmcli")
|
||||
comm.test("nmcli")
|
||||
end
|
||||
|
||||
# NetworkManager currently controls device
|
||||
#
|
||||
# @param [Vagrant::Plugin::V2::Communicator] comm Guest communicator
|
||||
# @param comm [Communicator]
|
||||
# @param device_name [String]
|
||||
# @return [Boolean]
|
||||
def nm_controlled?(comm, device_name)
|
||||
comm.test("nmcli -t d show #{device_name}") &&
|
||||
!comm.test("nmcli -t d show #{device_name} | grep unmanaged")
|
||||
comm.test("nmcli d show #{device_name}") &&
|
||||
!comm.test("nmcli d show #{device_name} | grep unmanaged")
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -30,7 +30,8 @@ module Vagrant
|
|||
return true
|
||||
end
|
||||
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.
|
||||
return false
|
||||
end
|
||||
|
|
|
@ -5,9 +5,34 @@ module Vagrant
|
|||
module NetworkIP
|
||||
# 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]
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -13,14 +13,6 @@ module Vagrant
|
|||
# This class just contains some platform checking code.
|
||||
class Platform
|
||||
class << self
|
||||
|
||||
def logger
|
||||
if !defined?(@_logger)
|
||||
@_logger = Log4r::Logger.new("vagrant::util::platform")
|
||||
end
|
||||
@_logger
|
||||
end
|
||||
|
||||
def cygwin?
|
||||
if !defined?(@_cygwin)
|
||||
@_cygwin = ENV["VAGRANT_DETECTED_OS"].to_s.downcase.include?("cygwin") ||
|
||||
|
@ -43,10 +35,11 @@ module Vagrant
|
|||
if !defined?(@_wsl)
|
||||
@_wsl = false
|
||||
SilenceWarnings.silence! do
|
||||
# Find 'microsoft' in /proc/version indicative of WSL
|
||||
if File.file?('/proc/version')
|
||||
osversion = File.open('/proc/version', &:gets)
|
||||
if osversion.downcase.include?("microsoft")
|
||||
# Use PATH values to check for `/mnt/c` path indicative of WSL
|
||||
if ENV.fetch("PATH", "").downcase.include?("/mnt/c")
|
||||
# Validate WSL via uname output
|
||||
uname = Subprocess.execute("uname", "-r")
|
||||
if uname.exit_code == 0 && uname.stdout.downcase.include?("microsoft")
|
||||
@_wsl = true
|
||||
end
|
||||
end
|
||||
|
@ -86,49 +79,24 @@ module Vagrant
|
|||
return @_windows_admin
|
||||
end
|
||||
|
||||
# Checks if Hyper-V is accessible to the local user. It will check
|
||||
# if user is in the "Hyper-V Administrators" group, is a Domain
|
||||
# administrator, and finally will run a manual interaction with
|
||||
# Hyper-V to determine if Hyper-V is usable for the current user.
|
||||
# Checks if the user running Vagrant on Windows is a member of the
|
||||
# "Hyper-V Administrators" group.
|
||||
#
|
||||
# From: https://support.microsoft.com/en-us/kb/243330
|
||||
# SID: S-1-5-32-578
|
||||
# Name: BUILTIN\Hyper-V Administrators
|
||||
# SID: S-1-5-21DOMAIN-512
|
||||
# Name: Domain Admins
|
||||
#
|
||||
# @return [Boolean]
|
||||
def windows_hyperv_admin?
|
||||
return @_windows_hyperv_admin if defined?(@_windows_hyperv_admin)
|
||||
|
||||
if ENV["VAGRANT_IS_HYPERV_ADMIN"]
|
||||
return @_windows_hyperv_admin = true
|
||||
end
|
||||
@_windows_hyperv_admin = -> {
|
||||
ps_cmd = "[System.Security.Principal.WindowsIdentity]::GetCurrent().Groups | ForEach-Object { if ($_.Value -eq 'S-1-5-32-578'){ Write-Host 'true'; break }}"
|
||||
output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd)
|
||||
return output == 'true'
|
||||
}.call
|
||||
|
||||
ps_cmd = "Write-Output ([System.Security.Principal.WindowsIdentity]::GetCurrent().Groups | " \
|
||||
"Select-Object Value | ConvertTo-JSON)"
|
||||
output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd)
|
||||
if output
|
||||
groups = begin
|
||||
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 = 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
|
||||
return @_windows_hyperv_admin
|
||||
end
|
||||
|
||||
# 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)
|
||||
|
||||
@_windows_hyperv_enabled = -> {
|
||||
["Get-WindowsOptionalFeature", "Get-WindowsFeature"].each do |cmd_name|
|
||||
ps_cmd = "$(#{cmd_name} -FeatureName Microsoft-Hyper-V-Hypervisor).State"
|
||||
begin
|
||||
output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd)
|
||||
return true if output == "Enabled"
|
||||
rescue Errors::PowerShellInvalidVersion
|
||||
logger.warn("Invalid PowerShell version detected during Hyper-V enable check")
|
||||
return false
|
||||
end
|
||||
end
|
||||
return false
|
||||
ps_cmd = "$(Get-WindowsOptionalFeature -FeatureName Microsoft-Hyper-V-All -Online).State"
|
||||
output = Vagrant::Util::PowerShell.execute_cmd(ps_cmd)
|
||||
return output == 'Enabled'
|
||||
}.call
|
||||
|
||||
return @_windows_hyperv_enabled
|
||||
|
@ -366,12 +326,6 @@ module Vagrant
|
|||
# Lowercase the drive letter, skip the next symbol (which is a
|
||||
# colon from a Windows path) and convert path to UNIX style.
|
||||
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")
|
||||
begin
|
||||
|
@ -435,33 +389,27 @@ module Vagrant
|
|||
# @param [String, Pathname] path Path to convert
|
||||
# @return [String]
|
||||
def wsl_to_windows_path(path)
|
||||
path = path.to_s
|
||||
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)
|
||||
parts = path.split("/")
|
||||
parts.delete_if(&:empty?)
|
||||
root_path = wsl_rootfs
|
||||
# lxrun splits home separate so we need to account
|
||||
# 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"
|
||||
end
|
||||
path = [root_path, *parts].join("\\")
|
||||
[root_path, *parts].join("\\")
|
||||
else
|
||||
path = path.sub("/mnt/", "")
|
||||
path = path.to_s.sub("/mnt/", "")
|
||||
parts = path.split("/")
|
||||
parts.first << ":"
|
||||
path = parts.join("\\")
|
||||
path
|
||||
end
|
||||
else
|
||||
path
|
||||
end
|
||||
path
|
||||
end
|
||||
|
||||
# Takes a windows path and formats it to the
|
||||
|
@ -514,14 +462,6 @@ module Vagrant
|
|||
def wsl_windows_accessible_path
|
||||
if !defined?(@_wsl_windows_accessible_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?
|
||||
access_path = wsl_windows_home.gsub("\\", "/").sub(":", "")
|
||||
access_path[0] = access_path[0].downcase
|
||||
|
@ -541,41 +481,6 @@ module Vagrant
|
|||
path.to_s.start_with?(wsl_windows_accessible_path.to_s)
|
||||
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
|
||||
# simple setup to allow sharing of the user's VAGRANT_HOME directory
|
||||
# within the subsystem
|
||||
|
@ -656,9 +561,9 @@ module Vagrant
|
|||
def wsl_validate_matching_vagrant_versions!
|
||||
valid = false
|
||||
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
|
||||
windows_version = result.stdout.match(/Vagrant (?<version>[\w.-]+)/)
|
||||
windows_version = result.stdout.match(/Installed Version: (?<version>[\w.-]+)/)
|
||||
if windows_version
|
||||
windows_version = windows_version[:version].strip
|
||||
valid = windows_version == Vagrant::VERSION
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
require "base64"
|
||||
require "tmpdir"
|
||||
require_relative "subprocess"
|
||||
require_relative "which"
|
||||
|
@ -21,20 +20,12 @@ module Vagrant
|
|||
if !defined?(@_powershell_executable)
|
||||
@_powershell_executable = "powershell"
|
||||
|
||||
if Which.which(@_powershell_executable).nil?
|
||||
# Try to use WSL interoperability if PowerShell is not symlinked to
|
||||
# the container.
|
||||
if Platform.wsl?
|
||||
@_powershell_executable += ".exe"
|
||||
# Try to use WSL interoperability if PowerShell is not symlinked to
|
||||
# the container.
|
||||
if Which.which(@_powershell_executable).nil? && Platform.wsl?
|
||||
@_powershell_executable += ".exe"
|
||||
|
||||
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
|
||||
if Which.which(@_powershell_executable).nil?
|
||||
@_powershell_executable = nil
|
||||
end
|
||||
end
|
||||
|
@ -50,30 +41,19 @@ module Vagrant
|
|||
# Execute a powershell script.
|
||||
#
|
||||
# @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]
|
||||
def self.execute(path, *args, **opts, &block)
|
||||
validate_install!
|
||||
if opts.delete(:sudo) || opts.delete(:runas)
|
||||
powerup_command(path, args, opts)
|
||||
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 = [
|
||||
executable,
|
||||
"-NoLogo",
|
||||
"-NoProfile",
|
||||
"-NonInteractive",
|
||||
"-ExecutionPolicy", "Bypass",
|
||||
"#{env}&('#{path}')",
|
||||
"&('#{path}')",
|
||||
args
|
||||
].flatten
|
||||
|
||||
|
@ -88,20 +68,10 @@ module Vagrant
|
|||
# Execute a powershell command.
|
||||
#
|
||||
# @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.
|
||||
# Returns stdout string if exit code is zero.
|
||||
def self.execute_cmd(command, **opts)
|
||||
def self.execute_cmd(command)
|
||||
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 = [
|
||||
executable,
|
||||
"-NoLogo",
|
||||
|
@ -109,7 +79,7 @@ module Vagrant
|
|||
"-NonInteractive",
|
||||
"-ExecutionPolicy", "Bypass",
|
||||
"-Command",
|
||||
"#{env}#{command}"
|
||||
command
|
||||
].flatten.compact
|
||||
|
||||
r = Subprocess.execute(*c)
|
||||
|
@ -117,39 +87,6 @@ module Vagrant
|
|||
return r.stdout.chomp
|
||||
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.
|
||||
#
|
||||
# @return [String]
|
||||
|
@ -209,26 +146,18 @@ module Vagrant
|
|||
# @return [Array<String>]
|
||||
def self.powerup_command(path, args, opts)
|
||||
Dir.mktmpdir("vagrant") do |dpath|
|
||||
all_args = [path] + args.flatten.map{ |a|
|
||||
a.gsub(/^['"](.+)['"]$/, "\\1")
|
||||
}
|
||||
arg_list = "\"" + all_args.join("\" \"") + "\""
|
||||
all_args = ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", path] + args
|
||||
arg_list = "@('" + all_args.join("', '") + "')"
|
||||
stdout = File.join(dpath, "stdout.txt")
|
||||
stderr = File.join(dpath, "stderr.txt")
|
||||
exitcode = File.join(dpath, "exitcode.txt")
|
||||
|
||||
script = "& #{arg_list} ; exit $LASTEXITCODE;"
|
||||
script_content = Base64.strict_encode64(script.encode("UTF-16LE", "UTF-8"))
|
||||
script = "$sp = Start-Process -FilePath powershell -ArgumentList #{arg_list} " \
|
||||
"-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
|
||||
wrapper = "$p = Start-Process -FilePath powershell -ArgumentList @('-NoLogo', '-NoProfile', " \
|
||||
"'-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 }"
|
||||
# escape quotes so we can nest our script within a start-process
|
||||
script.gsub!("'", "''")
|
||||
|
||||
cmd = [
|
||||
"powershell",
|
||||
|
@ -236,20 +165,31 @@ module Vagrant
|
|||
"-NoProfile",
|
||||
"-NonInteractive",
|
||||
"-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))
|
||||
r_stdout = result.stdout
|
||||
if File.exist?(stdout)
|
||||
r_stdout += File.read(stdout)
|
||||
r_stdout = File.read(stdout)
|
||||
else
|
||||
r_stdout = result.stdout
|
||||
end
|
||||
r_stderr = result.stderr
|
||||
if File.exist?(stderr)
|
||||
r_stderr += File.read(stderr)
|
||||
r_stderr = File.read(stderr)
|
||||
else
|
||||
r_stderr = result.stderr
|
||||
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
|
||||
|
||||
|
|
|
@ -140,7 +140,7 @@ module Vagrant
|
|||
end
|
||||
|
||||
# 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 += [
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "UserKnownHostsFile=/dev/null"]
|
||||
|
@ -150,24 +150,11 @@ module Vagrant
|
|||
if !plain_mode && options[:private_key_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
|
||||
# percent_expand in misc.c, but '-o' does. when passing the path,
|
||||
# replace '%' in the path with '%%' to escape the '%'
|
||||
path = path.to_s.gsub('%', '%%')
|
||||
private_key_arr = ["-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
|
||||
# Use '-o' instead of '-i' because '-i' does not call
|
||||
# percent_expand in misc.c, but '-o' does. when passing the path,
|
||||
# replace '%' in the path with '%%' to escape the '%'
|
||||
path = path.to_s.gsub('%', '%%')
|
||||
command_options += ["-o", "IdentityFile=\"#{path}\""]
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -178,10 +165,6 @@ module Vagrant
|
|||
"-o", "ForwardX11Trusted=yes"]
|
||||
end
|
||||
|
||||
if ssh_info[:config]
|
||||
command_options += ["-F", ssh_info[:config]]
|
||||
end
|
||||
|
||||
if ssh_info[:proxy_command]
|
||||
command_options += ["-o", "ProxyCommand=#{ssh_info[:proxy_command]}"]
|
||||
end
|
||||
|
|
|
@ -93,7 +93,7 @@ module Vagrant
|
|||
|
||||
# Special installer-related things
|
||||
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
|
||||
# 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...")
|
||||
jailbreak(process.environment)
|
||||
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
|
||||
@logger.info("Vagrant not running in installer, restoring original environment...")
|
||||
jailbreak(process.environment)
|
||||
|
|
|
@ -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
|
|
@ -1,5 +1,4 @@
|
|||
require "vagrant/util/template_renderer"
|
||||
require "log4r"
|
||||
|
||||
module Vagrant
|
||||
# This class provides a way to load and access the contents
|
||||
|
@ -27,7 +26,6 @@ module Vagrant
|
|||
@keys = keys
|
||||
@loader = loader
|
||||
@config, _ = loader.load(keys)
|
||||
@logger = Log4r::Logger.new("vagrant::vagrantfile")
|
||||
end
|
||||
|
||||
# Returns a {Machine} for the given name and provider that
|
||||
|
@ -44,7 +42,7 @@ module Vagrant
|
|||
# @return [Machine]
|
||||
def machine(name, provider, boxes, data_path, env)
|
||||
# Load the configuration for the machine
|
||||
results = machine_config(name, provider, boxes, data_path)
|
||||
results = machine_config(name, provider, boxes)
|
||||
box = results[:box]
|
||||
config = results[:config]
|
||||
config_errors = results[:config_errors]
|
||||
|
@ -109,10 +107,9 @@ module Vagrant
|
|||
# be backed by (required for provider overrides).
|
||||
# @param [BoxCollection] boxes BoxCollection to look up the
|
||||
# box Vagrantfile.
|
||||
# @param [Pathname] data_path Machine data path
|
||||
# @return [Hash<Symbol, Object>] Various configuration parameters for a
|
||||
# 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
|
||||
|
||||
sub_machine = @config.vm.defined_vms[name]
|
||||
|
@ -127,40 +124,23 @@ module Vagrant
|
|||
box_formats = nil
|
||||
if provider != nil
|
||||
provider_plugin = Vagrant.plugin("2").manager.providers[provider]
|
||||
if !provider_plugin && validate_provider
|
||||
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
|
||||
|
||||
if !provider_plugin
|
||||
raise Errors::ProviderNotFound,
|
||||
machine: name, provider: provider, providers: providers_str
|
||||
machine: name, provider: provider
|
||||
end
|
||||
|
||||
if validate_provider
|
||||
provider_cls = provider_plugin[0]
|
||||
provider_options = provider_plugin[1]
|
||||
box_formats = provider_options[:box_format] || provider
|
||||
provider_cls = provider_plugin[0]
|
||||
provider_options = provider_plugin[1]
|
||||
box_formats = provider_options[:box_format] || provider
|
||||
|
||||
# Test if the provider is usable or not
|
||||
begin
|
||||
provider_cls.usable?(true)
|
||||
rescue Errors::VagrantError => e
|
||||
raise Errors::ProviderNotUsable,
|
||||
machine: name.to_s,
|
||||
provider: provider.to_s,
|
||||
message: e.to_s
|
||||
end
|
||||
else
|
||||
box_formats = provider
|
||||
# Test if the provider is usable or not
|
||||
begin
|
||||
provider_cls.usable?(true)
|
||||
rescue Errors::VagrantError => e
|
||||
raise Errors::ProviderNotUsable,
|
||||
machine: name.to_s,
|
||||
provider: provider.to_s,
|
||||
message: e.to_s
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -174,21 +154,8 @@ module Vagrant
|
|||
|
||||
# Track the original box so we know if we changed
|
||||
box = nil
|
||||
initial_box = original_box = config.vm.box
|
||||
initial_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
|
||||
original_box = config.vm.box
|
||||
original_version = config.vm.box_version
|
||||
|
||||
# The proc below loads the box and provider overrides. This is
|
||||
# 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)
|
||||
if box
|
||||
box_vagrantfile = find_vagrantfile(box.directory)
|
||||
if box_vagrantfile && !config.vm.ignore_box_vagrantfile
|
||||
if box_vagrantfile
|
||||
box_config_key =
|
||||
"#{boxes.object_id}_#{box.name}_#{box.provider}".to_sym
|
||||
@loader.set(box_config_key, box_vagrantfile)
|
||||
local_keys.unshift(box_config_key)
|
||||
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
|
||||
|
@ -236,22 +201,6 @@ module Vagrant
|
|||
# Load the box and provider overrides
|
||||
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 {
|
||||
box: box,
|
||||
provider_cls: provider_cls,
|
||||
|
|
|
@ -86,7 +86,7 @@ module VagrantPlugins
|
|||
box_force: options[:force],
|
||||
box_download_ca_cert: options[:ca_cert],
|
||||
box_download_ca_path: options[:ca_path],
|
||||
box_download_client_cert: options[:client_cert],
|
||||
box_client_cert: options[:client_cert],
|
||||
box_download_insecure: options[:insecure],
|
||||
box_download_location_trusted: options[:location_trusted],
|
||||
ui: Vagrant::UI::Prefixed.new(@env.ui, "box"),
|
||||
|
|
|
@ -26,10 +26,6 @@ module VagrantPlugins
|
|||
options[:global] = g
|
||||
end
|
||||
|
||||
o.on("-f", "--force", "Force checks for latest box updates") do |f|
|
||||
options[:force] = f
|
||||
end
|
||||
|
||||
build_download_options(o, download_options)
|
||||
end
|
||||
|
||||
|
@ -44,7 +40,7 @@ module VagrantPlugins
|
|||
|
||||
with_target_vms(argv) do |machine|
|
||||
@env.action_runner.run(Vagrant::Action.action_box_outdated, {
|
||||
box_outdated_force: options[:force],
|
||||
box_outdated_force: true,
|
||||
box_outdated_refresh: true,
|
||||
box_outdated_success_ui: true,
|
||||
machine: machine,
|
||||
|
@ -77,15 +73,7 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
current = Gem::Version.new(box.version)
|
||||
box_versions = md.versions(provider: box.provider)
|
||||
|
||||
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)
|
||||
latest = Gem::Version.new(md.versions.last)
|
||||
if latest <= current
|
||||
@env.ui.success(I18n.t(
|
||||
"vagrant.box_up_to_date",
|
||||
|
|
|
@ -30,10 +30,6 @@ module VagrantPlugins
|
|||
o.on("-f", "--force", "Destroy without confirmation even when box is in use.") do |f|
|
||||
options[:force] = f
|
||||
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
|
||||
|
||||
# Parse the options
|
||||
|
@ -45,7 +41,7 @@ module VagrantPlugins
|
|||
return @env.ui.warn(I18n.t("vagrant.commands.box.no_installed_boxes"), prefix: false)
|
||||
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
|
||||
0
|
||||
|
@ -53,7 +49,7 @@ module VagrantPlugins
|
|||
|
||||
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
|
||||
longest_box = boxes.max_by { |x| x[0].length }
|
||||
longest_box_length = longest_box[0].length
|
||||
|
@ -116,7 +112,6 @@ module VagrantPlugins
|
|||
box_provider: provider,
|
||||
box_version: version,
|
||||
force_confirm_box_remove: skip_confirm,
|
||||
keep_used_boxes: keep_used_boxes,
|
||||
box_remove_all_versions: false,
|
||||
})
|
||||
end
|
||||
|
|
|
@ -80,7 +80,7 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
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|
|
||||
|
@ -100,14 +100,10 @@ module VagrantPlugins
|
|||
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
|
||||
machine.ui.output(I18n.t(
|
||||
"vagrant.errors.box_update_no_box",
|
||||
name: machine.config.vm.box))
|
||||
next
|
||||
end
|
||||
machine.ui.output(I18n.t(
|
||||
"vagrant.errors.box_update_no_box",
|
||||
name: machine.config.vm.box))
|
||||
next
|
||||
end
|
||||
|
||||
name = machine.box.name
|
||||
|
@ -128,13 +124,7 @@ module VagrantPlugins
|
|||
if download_options[:insecure].nil?
|
||||
download_options[:insecure] = machine.config.vm.box_download_insecure
|
||||
end
|
||||
|
||||
begin
|
||||
box_update(box, version, machine.ui, download_options, force)
|
||||
rescue Vagrant::Errors::BoxUpdateNoMetadata => e
|
||||
machine.ui.warn(e)
|
||||
next
|
||||
end
|
||||
box_update(box, version, machine.ui, download_options, force)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -164,7 +154,7 @@ module VagrantPlugins
|
|||
box_version: update[1].version,
|
||||
ui: ui,
|
||||
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_path: download_options[:ca_path],
|
||||
box_download_insecure: download_options[:insecure]
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue