diff -Nru vagrant-2.2.3+dfsg/CHANGELOG.md vagrant-2.2.6+dfsg/CHANGELOG.md --- vagrant-2.2.3+dfsg/CHANGELOG.md 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/CHANGELOG.md 2019-10-14 16:38:13.000000000 +0000 @@ -1,3 +1,110 @@ +## 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 requird [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: diff -Nru vagrant-2.2.3+dfsg/.circleci/config.yml vagrant-2.2.6+dfsg/.circleci/config.yml --- vagrant-2.2.3+dfsg/.circleci/config.yml 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/.circleci/config.yml 2019-10-14 16:38:13.000000000 +0000 @@ -1,5 +1,127 @@ version: 2 +reference: + environment: &ENVIRONMENT + SLACK_TITLE: Vagrant CI + RELEASE_TARGET_REPONAME: vagrant-installers + images: + ruby24: &ruby24 + docker: + - image: circleci/ruby:2.4 + ruby25: &ruby25 + docker: + - image: circleci/ruby:2.5 + ruby26: &ruby26 + docker: + - image: circleci/ruby:2.6 + builder: &builder + environment: + <<: *ENVIRONMENT + docker: + - image: $BUILDER_IMAGE + auth: + username: $BUILDER_USERNAME + password: $BUILDER_PASSWORD + workflows: + public: &PUBLIC_WORKFLOW + filters: + branches: + only: /^pull\/.*/ + master: &MASTER_WORKFLOW + filters: + branches: + only: master + private_build: &PRIVATE_WORKFLOW_BUILD + context: vagrant + filters: + branches: + only: + - /^build-.*/ + tags: + only: /.*/ + jobs: + private_failure: &PRIVATE_FAILURE + run: + name: Failure handler + command: | + if [ -f .output ]; then + slack -m "Vagrant job has failed: *${CIRCLE_JOB}*" -s error -f .output -T 5 + else + slack -m "Vagrant job has failed: *${CIRCLE_JOB}*" -s error + fi + when: on_fail + unit_tests: &unit_tests + steps: + - run: sudo apt-get update ; sudo apt-get -yq install bsdtar + - checkout + - 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: + - ./vendor/bundle + - run: bundle exec rake test:unit jobs: + build: + <<: *builder + steps: + - checkout + - run: gem build vagrant.gemspec + - *PRIVATE_FAILURE + - persist_to_workspace: + root: . + paths: + - ./*.gem + store: + <<: *builder + steps: + - attach_workspace: + at: . + - run: | + gem_name=(vagrant-*.gem) + if [ "${CIRCLE_TAG}" == "" ]; then + remote_gem_name="vagrant-master.gem" + else + remote_gem_name="vagrant.gem" + fi + if [[ "${CIRCLE_BRANCH}" = "build-"* ]]; then + s3_dst="${ASSETS_PRIVATE_LONGTERM}/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/${CIRCLE_BRANCH##build-}/" + else + s3_dst="${ASSETS_PRIVATE_BUCKET}/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/" + fi + aws s3 cp "${gem_name}" "${s3_dst}${remote_gem_name}" > .output 2>&1 + - *PRIVATE_FAILURE + release: + <<: *builder + steps: + - checkout + - attach_workspace: + at: . + - run: | + set +e + gem=(vagrant-*.gem) + gem_version="${gem##vagrant-}" + gem_version="${gem_version%%.gem}" + export GITHUB_TOKEN="${HASHIBOT_TOKEN}" + if [ "${CIRCLE_TAG}" = "" ]; then + version="v${gem_version}+$(git rev-parse --short "${CIRCLE_SHA1}")" + ghr -u ${CIRCLE_PROJECT_USERNAME} -r ${RELEASE_TARGET_REPONAME} -c master -prerelease -delete -replace ${version} ${gem} > .output 2>&1 + else + version="${CIRCLE_TAG}" + ghr -u ${CIRCLE_PROJECT_USERNAME} -r ${RELEASE_TARGET_REPONAME} -c master -delete -replace ${version} ${gem} > .output 2>&1 + fi + slack -m "New Vagrant installers release triggered: *${version}*" + - *PRIVATE_FAILURE + test_ruby24: + <<: *ruby24 + <<: *unit_tests + test_ruby25: + <<: *ruby25 + <<: *unit_tests + test_ruby26: + <<: *ruby26 + <<: *unit_tests build-website: # setting the working_directory along with the checkout path allows us to not have # to cd into the website/ directory for commands @@ -9,30 +131,60 @@ steps: - checkout: path: ~/project - - # restores gem cache - restore_cache: key: static-site-gems-v1-{{ checksum "Gemfile.lock" }} - - run: command: bundle check || bundle install --path vendor/bundle - - # saves gem cache if we have changed the Gemfile - save_cache: key: static-site-gems-v1-{{ checksum "Gemfile.lock" }} paths: - ~/project/website/vendor/bundle - - # middleman build - run: command: bundle exec middleman build - - # website deploy - run: command: ./scripts/deploy.sh - workflows: version: 2 + builds: + jobs: + - build: + <<: *PRIVATE_WORKFLOW_BUILD + - store: + <<: *PRIVATE_WORKFLOW_BUILD + requires: + - build + - release: + <<: *PRIVATE_WORKFLOW_BUILD + requires: + - build + pull_requests: + jobs: + - test_ruby24: + <<: *PUBLIC_WORKFLOW + - test_ruby25: + <<: *PUBLIC_WORKFLOW + - test_ruby26: + <<: *PUBLIC_WORKFLOW + master: + jobs: + - test_ruby24: + <<: *MASTER_WORKFLOW + - test_ruby25: + <<: *MASTER_WORKFLOW + - test_ruby26: + <<: *MASTER_WORKFLOW + - build: + <<: *MASTER_WORKFLOW + context: vagrant + requires: + - test_ruby24 + - test_ruby25 + - test_ruby26 + - store: + <<: *MASTER_WORKFLOW + context: vagrant + requires: + - build website: jobs: - build-website: diff -Nru vagrant-2.2.3+dfsg/contrib/bash/completion.sh vagrant-2.2.6+dfsg/contrib/bash/completion.sh --- vagrant-2.2.3+dfsg/contrib/bash/completion.sh 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/contrib/bash/completion.sh 2019-10-14 16:38:13.000000000 +0000 @@ -85,7 +85,7 @@ 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}" -type f -name "id" | awk -F"/" '{print $(NF-2)}') + running_vm_list=$(find "${vagrant_state_file}/machines" -type f -name "id" | awk -F"/" '{print $(NF-2)}') fi COMPREPLY=($(compgen -W "${running_vm_list}" -- ${cur})) return 0 diff -Nru vagrant-2.2.3+dfsg/debian/changelog vagrant-2.2.6+dfsg/debian/changelog --- vagrant-2.2.3+dfsg/debian/changelog 2019-03-07 14:54:37.000000000 +0000 +++ vagrant-2.2.6+dfsg/debian/changelog 2020-01-03 17:03:51.000000000 +0000 @@ -1,3 +1,36 @@ +vagrant (2.2.6+dfsg-2ubuntu1) focal; urgency=low + + * Merge from Debian unstable. Remaining changes: + - Drop integration-test autopkgtest as it depends on access to the Debian + archive. + + -- Dave Chiluk Fri, 3 Jan 2020 11:50:10 -0600 + +vagrant (2.2.6+dfsg-2) unstable; urgency=medium + + [ Utkarsh Gupta ] + * Add salsa-ci.yml + + [ Antonio Terceiro ] + * Add patch to avoid /etc/exports growing forever + * Add patch for VirtualBox 6.1 support (Closes: #946837) + * Add Breaks: on unsupported VirtualBox versions (Closes: #946838) + + -- Antonio Terceiro Tue, 24 Dec 2019 16:54:36 -0300 + +vagrant (2.2.6+dfsg-1) unstable; urgency=medium + + * New upstream version 2.2.6+dfsg + * Refresh patches + * Bump dependency on ruby-vagrant-cloud to >= 2.0.3 + * autopkgtest: don't try running a box under /tmp + * Bump Standards-Version to 4.4.1; no changes needed + * autopkgtest: drop obsolete resstriction needs-recommends + * debian/rules: drop README.md files outside of doc dirs + * autopkgtest smoke-test: use vagrant-libvirt + + -- Antonio Terceiro Thu, 14 Nov 2019 10:13:03 -0300 + vagrant (2.2.3+dfsg-1ubuntu2) disco; urgency=medium * Drop ruby-i18n Depends to a version we have available in Ubuntu. diff -Nru vagrant-2.2.3+dfsg/debian/control vagrant-2.2.6+dfsg/debian/control --- vagrant-2.2.3+dfsg/debian/control 2019-03-07 14:54:37.000000000 +0000 +++ vagrant-2.2.6+dfsg/debian/control 2020-01-03 16:59:34.000000000 +0000 @@ -13,16 +13,16 @@ ruby-childprocess (>= 0.6.0), ruby-ed25519 (>= 1.2.4), ruby-erubis (>= 2.7.0), - ruby-i18n (>= 0.7.0), + ruby-i18n (>= 1.1.1), ruby-listen (>= 3.1.5), ruby-log4r (>= 1.1.9), ruby-net-scp (>= 1.2.0), ruby-net-sftp (>= 2.1), ruby-net-ssh (>= 5.1.0), ruby-rest-client (>= 1.6.0), - ruby-vagrant-cloud (>= 2.0.2), + ruby-vagrant-cloud (>= 2.0.3), ruby-zip (>= 1.2.2) -Standards-Version: 4.3.0 +Standards-Version: 4.4.1 Vcs-Git: https://salsa.debian.org/ruby-team/vagrant.git Vcs-Browser: https://salsa.debian.org/ruby-team/vagrant Homepage: https://www.vagrantup.com @@ -42,19 +42,20 @@ ruby-childprocess (>= 0.6.0), ruby-ed25519 (>= 1.2.4), ruby-erubis (>= 2.7.0), - ruby-i18n (>= 0.7.0), + ruby-i18n (>= 1.1.1), ruby-listen (>= 3.1.5), ruby-log4r (>= 1.1.9), ruby-net-scp (>= 1.2.0), ruby-net-sftp (>= 2.1), ruby-net-ssh (>= 5.1.0), ruby-rest-client (>= 1.6.0), - ruby-vagrant-cloud (>= 2.0.2), + ruby-vagrant-cloud (>= 2.0.3), ruby-zip (>= 1.2.2), ${misc:Depends}, ${shlibs:Depends} Recommends: vagrant-libvirt Suggests: virtualbox (>= 4.0) +Breaks: virtualbox (>= ${vagrant:UnsupportedVirtualBox}) Description: Tool for building and distributing virtualized development environments This package provides the tools to create and configure lightweight, reproducible, and portable virtual environments. diff -Nru vagrant-2.2.3+dfsg/debian/get-unsupported-virtualbox vagrant-2.2.6+dfsg/debian/get-unsupported-virtualbox --- vagrant-2.2.3+dfsg/debian/get-unsupported-virtualbox 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/debian/get-unsupported-virtualbox 2019-12-24 19:54:36.000000000 +0000 @@ -0,0 +1,8 @@ +#!/usr/bin/ruby + +supported = Dir['plugins/providers/virtualbox/driver/version_*.rb'].map do |f| + File.basename(f, '.rb').sub('version_', '').split('_').map(&:to_i) +end.sort.last +unsupported = supported.clone +unsupported[-1] +=1 +puts unsupported.join('.') diff -Nru vagrant-2.2.3+dfsg/debian/patches/0005-Relax-dependency-resolution.patch vagrant-2.2.6+dfsg/debian/patches/0005-Relax-dependency-resolution.patch --- vagrant-2.2.3+dfsg/debian/patches/0005-Relax-dependency-resolution.patch 2019-02-05 23:52:31.000000000 +0000 +++ vagrant-2.2.6+dfsg/debian/patches/0005-Relax-dependency-resolution.patch 2019-12-25 03:49:51.000000000 +0000 @@ -22,7 +22,7 @@ - gem 'vagrant-spec', git: "https://github.com/hashicorp/vagrant-spec.git" -end diff --git a/vagrant.gemspec b/vagrant.gemspec -index 885c0d3..3065007 100644 +index 2ca4a69..55730e5 100644 --- a/vagrant.gemspec +++ b/vagrant.gemspec @@ -16,39 +16,25 @@ Gem::Specification.new do |s| @@ -47,7 +47,7 @@ - s.add_dependency "winrm", "~> 2.1" - s.add_dependency "winrm-fs", "~> 1.0" - s.add_dependency "winrm-elevated", "~> 1.1" -- s.add_dependency "vagrant_cloud", "~> 2.0.2" +- s.add_dependency "vagrant_cloud", "~> 2.0.3" - - # NOTE: The ruby_dep gem is an implicit dependency from the listen gem. Later versions - # of the ruby_dep gem impose an aggressive constraint on the required ruby version (>= 2.2.5). diff -Nru vagrant-2.2.3+dfsg/debian/patches/0006-nfs-avoid-adding-extra-newlines-to-etc-exports.patch vagrant-2.2.6+dfsg/debian/patches/0006-nfs-avoid-adding-extra-newlines-to-etc-exports.patch --- vagrant-2.2.3+dfsg/debian/patches/0006-nfs-avoid-adding-extra-newlines-to-etc-exports.patch 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/debian/patches/0006-nfs-avoid-adding-extra-newlines-to-etc-exports.patch 2019-12-24 19:54:36.000000000 +0000 @@ -0,0 +1,40 @@ +From: Antonio Terceiro +Date: Sun, 17 Nov 2019 00:27:06 -0300 +Subject: nfs: avoid adding extra newlines to /etc/exports + +StringBlockEditor already adds the necessary newlines. That extra +newline was making /etc/exports longer and longer, full of empty lines, +because StringBlockEditor doesn't know about it and does not remove it. + +Forwarded: https://github.com/hashicorp/vagrant/pull/11201 +--- + plugins/hosts/linux/cap/nfs.rb | 2 +- + test/unit/plugins/hosts/linux/cap/nfs_test.rb | 2 +- + 2 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/plugins/hosts/linux/cap/nfs.rb b/plugins/hosts/linux/cap/nfs.rb +index 630dc0f..3d650cf 100644 +--- a/plugins/hosts/linux/cap/nfs.rb ++++ b/plugins/hosts/linux/cap/nfs.rb +@@ -77,7 +77,7 @@ module VagrantPlugins + sleep 0.5 + + nfs_cleanup("#{Process.uid} #{id}") +- output = "#{nfs_exports_content}\n#{output}" ++ output = nfs_exports_content + output + nfs_write_exports(output) + + if nfs_running?(nfs_check_command) +diff --git a/test/unit/plugins/hosts/linux/cap/nfs_test.rb b/test/unit/plugins/hosts/linux/cap/nfs_test.rb +index 575035a..14e5502 100644 +--- a/test/unit/plugins/hosts/linux/cap/nfs_test.rb ++++ b/test/unit/plugins/hosts/linux/cap/nfs_test.rb +@@ -188,7 +188,7 @@ EOH + :linux__nfs_options=>["rw","all_squash"]}} + valid_id = SecureRandom.uuid + content =<<-EOH +-\n# VAGRANT-BEGIN: #{Process.uid} #{valid_id} ++# VAGRANT-BEGIN: #{Process.uid} #{valid_id} + "/home/vagrant" 127.0.0.1(rw,all_squash,anonuid=,anongid=,fsid=) + "/newhome/otherproject" 127.0.0.1(rw,all_squash,anonuid=,anongid=,fsid=) + # VAGRANT-END: #{Process.uid} #{valid_id} diff -Nru vagrant-2.2.3+dfsg/debian/patches/0007-Add-VirtualBox-provider-support-for-version-6.1.x.patch vagrant-2.2.6+dfsg/debian/patches/0007-Add-VirtualBox-provider-support-for-version-6.1.x.patch --- vagrant-2.2.3+dfsg/debian/patches/0007-Add-VirtualBox-provider-support-for-version-6.1.x.patch 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/debian/patches/0007-Add-VirtualBox-provider-support-for-version-6.1.x.patch 2019-12-24 19:54:36.000000000 +0000 @@ -0,0 +1,59 @@ +From: Brian Cain +Date: Wed, 11 Dec 2019 13:25:11 -0800 +Subject: Add VirtualBox provider support for version 6.1.x + +This commit adds support for VirtualBox version 6.1.x. It simply +inherits from the base 6.0.x provider class. +--- + plugins/providers/virtualbox/driver/meta.rb | 1 + + plugins/providers/virtualbox/driver/version_6_1.rb | 16 ++++++++++++++++ + plugins/providers/virtualbox/plugin.rb | 1 + + 3 files changed, 18 insertions(+) + create mode 100644 plugins/providers/virtualbox/driver/version_6_1.rb + +diff --git a/plugins/providers/virtualbox/driver/meta.rb b/plugins/providers/virtualbox/driver/meta.rb +index 24547b4..ed01373 100644 +--- a/plugins/providers/virtualbox/driver/meta.rb ++++ b/plugins/providers/virtualbox/driver/meta.rb +@@ -64,6 +64,7 @@ module VagrantPlugins + "5.1" => Version_5_1, + "5.2" => Version_5_2, + "6.0" => Version_6_0, ++ "6.1" => Version_6_1, + } + + if @@version.start_with?("4.2.14") +diff --git a/plugins/providers/virtualbox/driver/version_6_1.rb b/plugins/providers/virtualbox/driver/version_6_1.rb +new file mode 100644 +index 0000000..ebe417e +--- /dev/null ++++ b/plugins/providers/virtualbox/driver/version_6_1.rb +@@ -0,0 +1,16 @@ ++require File.expand_path("../version_6_0", __FILE__) ++ ++module VagrantPlugins ++ module ProviderVirtualBox ++ module Driver ++ # Driver for VirtualBox 6.1.x ++ class Version_6_1 < Version_6_0 ++ def initialize(uuid) ++ super ++ ++ @logger = Log4r::Logger.new("vagrant::provider::virtualbox_6_1") ++ end ++ end ++ end ++ end ++end +diff --git a/plugins/providers/virtualbox/plugin.rb b/plugins/providers/virtualbox/plugin.rb +index e058ec6..f2fbf47 100644 +--- a/plugins/providers/virtualbox/plugin.rb ++++ b/plugins/providers/virtualbox/plugin.rb +@@ -59,6 +59,7 @@ module VagrantPlugins + autoload :Version_5_1, File.expand_path("../driver/version_5_1", __FILE__) + autoload :Version_5_2, File.expand_path("../driver/version_5_2", __FILE__) + autoload :Version_6_0, File.expand_path("../driver/version_6_0", __FILE__) ++ autoload :Version_6_1, File.expand_path("../driver/version_6_1", __FILE__) + end + + module Model diff -Nru vagrant-2.2.3+dfsg/debian/patches/series vagrant-2.2.6+dfsg/debian/patches/series --- vagrant-2.2.3+dfsg/debian/patches/series 2019-02-05 23:52:31.000000000 +0000 +++ vagrant-2.2.6+dfsg/debian/patches/series 2019-12-25 03:49:51.000000000 +0000 @@ -3,3 +3,5 @@ 0003-linux-cap-halt-don-t-wait-for-shutdown-h-now-to-fini.patch 0004-Support-system-installed-plugins.patch 0005-Relax-dependency-resolution.patch +0006-nfs-avoid-adding-extra-newlines-to-etc-exports.patch +0007-Add-VirtualBox-provider-support-for-version-6.1.x.patch diff -Nru vagrant-2.2.3+dfsg/debian/rules vagrant-2.2.6+dfsg/debian/rules --- vagrant-2.2.3+dfsg/debian/rules 2019-02-05 23:52:31.000000000 +0000 +++ vagrant-2.2.6+dfsg/debian/rules 2019-12-25 03:49:51.000000000 +0000 @@ -11,3 +11,13 @@ override_dh_auto_build: dh_auto_build pod2man -c "" -r "" debian/dh_vagrant_plugin > debian/dh_vagrant_plugin.1 + +override_dh_auto_install: virtualbox_support + dh_auto_install + find debian/vagrant/usr/share/rubygems-integration/ -name README.md -delete + +unsupported_virtualbox = $(shell ./debian/get-unsupported-virtualbox) + +virtualbox_support: + @echo 'vagrant:UnsupportedVirtualBox=$(unsupported_virtualbox)' >> debian/vagrant.substvars + @echo 'Unsupported VirtualBox: >= $(unsupported_virtualbox)' diff -Nru vagrant-2.2.3+dfsg/debian/salsa-ci.yml vagrant-2.2.6+dfsg/debian/salsa-ci.yml --- vagrant-2.2.3+dfsg/debian/salsa-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/debian/salsa-ci.yml 2019-12-24 19:54:36.000000000 +0000 @@ -0,0 +1,4 @@ +--- +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml diff -Nru vagrant-2.2.3+dfsg/debian/tests/control vagrant-2.2.6+dfsg/debian/tests/control --- vagrant-2.2.3+dfsg/debian/tests/control 2019-02-11 15:23:28.000000000 +0000 +++ vagrant-2.2.6+dfsg/debian/tests/control 2020-01-03 17:20:03.000000000 +0000 @@ -1 +1,3 @@ Tests: smoke-test +Depends: @, vagrant-libvirt + diff -Nru vagrant-2.2.3+dfsg/debian/tests/smoke-test vagrant-2.2.6+dfsg/debian/tests/smoke-test --- vagrant-2.2.3+dfsg/debian/tests/smoke-test 2019-02-05 23:52:31.000000000 +0000 +++ vagrant-2.2.6+dfsg/debian/tests/smoke-test 2019-12-25 03:49:51.000000000 +0000 @@ -3,7 +3,7 @@ exec 2>&1 set -ex -export VAGRANT_DEFAULT_PROVIDER=docker +export VAGRANT_DEFAULT_PROVIDER=libvirt if [ -z "$ADTTMP" ]; then export PATH=$(pwd)/bin:"$PATH" diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/action/builtin/after_trigger.rb vagrant-2.2.6+dfsg/lib/vagrant/action/builtin/after_trigger.rb --- vagrant-2.2.3+dfsg/lib/vagrant/action/builtin/after_trigger.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/action/builtin/after_trigger.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,31 @@ +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 diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/action/builtin/before_trigger.rb vagrant-2.2.6+dfsg/lib/vagrant/action/builtin/before_trigger.rb --- vagrant-2.2.3+dfsg/lib/vagrant/action/builtin/before_trigger.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/action/builtin/before_trigger.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,28 @@ +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 diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/action/builtin/box_add.rb vagrant-2.2.6+dfsg/lib/vagrant/action/builtin/box_add.rb --- vagrant-2.2.3+dfsg/lib/vagrant/action/builtin/box_add.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/action/builtin/box_add.rb 2019-10-14 16:38:13.000000000 +0000 @@ -527,22 +527,11 @@ end def validate_checksum(checksum_type, checksum, path) - 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("Validating checksum with #{checksum_type}") @logger.info("Expected checksum: #{checksum}") - actual = FileChecksum.new(path, checksum_klass).checksum + actual = FileChecksum.new(path, checksum_type).checksum + @logger.info("Actual checksum: #{actual}") if actual.casecmp(checksum) != 0 raise Errors::BoxChecksumMismatch, actual: actual, diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/action/builtin/box_remove.rb vagrant-2.2.6+dfsg/lib/vagrant/action/builtin/box_remove.rb --- vagrant-2.2.3+dfsg/lib/vagrant/action/builtin/box_remove.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/action/builtin/box_remove.rb 2019-10-14 16:38:13.000000000 +0000 @@ -99,8 +99,11 @@ 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] + if !result[:result] || keep_used_boxes # They said "no", so continue with the next box next end diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/action/builtin/mixin_provisioners.rb vagrant-2.2.6+dfsg/lib/vagrant/action/builtin/mixin_provisioners.rb --- vagrant-2.2.3+dfsg/lib/vagrant/action/builtin/mixin_provisioners.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/action/builtin/mixin_provisioners.rb 2019-10-14 16:38:13.000000000 +0000 @@ -29,15 +29,92 @@ options = { 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] + 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) @@ -47,6 +124,13 @@ # 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 diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/action/builtin/mixin_synced_folders.rb vagrant-2.2.6+dfsg/lib/vagrant/action/builtin/mixin_synced_folders.rb --- vagrant-2.2.3+dfsg/lib/vagrant/action/builtin/mixin_synced_folders.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/action/builtin/mixin_synced_folders.rb 2019-10-14 16:38:13.000000000 +0000 @@ -97,8 +97,14 @@ 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(JSON.dump(folders)) + f.write(folder_data) end end diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/action/general/package.rb vagrant-2.2.6+dfsg/lib/vagrant/action/general/package.rb --- vagrant-2.2.3+dfsg/lib/vagrant/action/general/package.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/action/general/package.rb 2019-10-14 16:38:13.000000000 +0000 @@ -80,8 +80,10 @@ @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 @@ -151,6 +153,22 @@ 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 diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/action/runner.rb vagrant-2.2.6+dfsg/lib/vagrant/action/runner.rb --- vagrant-2.2.3+dfsg/lib/vagrant/action/runner.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/action/runner.rb 2019-10-14 16:38:13.000000000 +0000 @@ -2,6 +2,7 @@ require 'vagrant/action/hook' require 'vagrant/util/busy' +require 'vagrant/util/experimental' module Vagrant module Action @@ -33,6 +34,19 @@ 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? @@ -52,19 +66,43 @@ ui = environment[:ui] if environment.key?(:ui) int_callback = lambda do if environment[:interrupted] - ui.error I18n.t("vagrant.actions.runner.exit_immediately") if ui + if ui + begin + ui.error I18n.t("vagrant.actions.runner.exit_immediately") + rescue ThreadError + # We're being called in a trap-context. Wrap in a thread. + Thread.new { + ui.error I18n.t("vagrant.actions.runner.exit_immediately") + }.join(THREAD_MAX_JOIN_TIMEOUT) + end + end abort end - ui.warn I18n.t("vagrant.actions.runner.waiting_cleanup") if ui && !@@reported_interrupt + if ui && !@@reported_interrupt + begin + ui.warn I18n.t("vagrant.actions.runner.waiting_cleanup") + rescue ThreadError + # We're being called in a trap-context. Wrap in a thread. + Thread.new { + ui.warn I18n.t("vagrant.actions.runner.waiting_cleanup") + }.join(THREAD_MAX_JOIN_TIMEOUT) + end + end environment[:interrupted] = true @@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 diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/action/warden.rb vagrant-2.2.6+dfsg/lib/vagrant/action/warden.rb --- vagrant-2.2.3+dfsg/lib/vagrant/action/warden.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/action/warden.rb 2019-10-14 16:38:13.000000000 +0000 @@ -1,4 +1,7 @@ require "log4r" +require 'vagrant/util/experimental' +require 'vagrant/action/builtin/before_trigger' +require 'vagrant/action/builtin/after_trigger' module Vagrant module Action @@ -16,8 +19,21 @@ 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) } + @actions = actions.map { |m| finalize_action(m, env) }.flatten @logger = Log4r::Logger.new("vagrant::action::warden") @last_error = nil end @@ -87,7 +103,17 @@ if klass.is_a?(Class) # A action klass which is to be instantiated with the # app, env, and any arguments given - klass.new(self, env, *args, &block) + + # We wrap the action class in two Trigger method calls so that + # action triggers can fire before and after each given action in the stack. + klass_name = klass.name + [Vagrant::Action::Builtin::BeforeTriggerAction.new(self, env, + klass_name, + @triggers), + klass.new(self, env, *args, &block), + Vagrant::Action::Builtin::AfterTriggerAction.new(self, env, + klass_name, + @triggers)] elsif klass.respond_to?(:call) # Make it a lambda which calls the item then forwards # up the chain diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/batch_action.rb vagrant-2.2.6+dfsg/lib/vagrant/batch_action.rb --- vagrant-2.2.3+dfsg/lib/vagrant/batch_action.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/batch_action.rb 2019-10-14 16:38:13.000000000 +0000 @@ -71,6 +71,10 @@ 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 @@ -160,6 +164,16 @@ 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 diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/box_collection.rb vagrant-2.2.6+dfsg/lib/vagrant/box_collection.rb --- vagrant-2.2.3+dfsg/lib/vagrant/box_collection.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/box_collection.rb 2019-10-14 16:38:13.000000000 +0000 @@ -116,7 +116,7 @@ # Extract the box into a temporary directory. @logger.debug("Unpacking box into temporary directory: #{temp_dir}") result = Util::Subprocess.execute( - "bsdtar", "-v", "-x", "-m", "-s", "|\\\\\|/|", "-C", temp_dir.to_s, "-f", path.to_s) + "bsdtar", "--no-same-owner", "--no-same-permissions", "-v", "-x", "-m", "-s", "|\\\\\|/|", "-C", temp_dir.to_s, "-f", path.to_s) if result.exit_code != 0 raise Errors::BoxUnpackageFailure, output: result.stderr.to_s @@ -233,14 +233,23 @@ 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})") + @logger.debug("Box: #{box_name} (#{provider_name}, #{version})") results << [box_name, version, provider_name] else - @logger.debug("Invalid box, ignoring: #{provider}") + @logger.debug("Invalid box #{box_name}, ignoring: #{provider}") end end end diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/bundler.rb vagrant-2.2.6+dfsg/lib/vagrant/bundler.rb --- vagrant-2.2.3+dfsg/lib/vagrant/bundler.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/bundler.rb 2019-10-14 16:38:13.000000000 +0000 @@ -40,6 +40,20 @@ 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 @@ -62,12 +76,8 @@ # Add HashiCorp RubyGems source if !Gem.sources.include?(HASHICORP_GEMSTORE) - current_sources = Gem.sources.sources.dup - Gem.sources.clear - Gem.sources << HASHICORP_GEMSTORE - current_sources.each do |src| - Gem.sources << src - end + sources = [HASHICORP_GEMSTORE] + Gem.sources.sources + Gem.sources.replace(sources) end # Generate dependencies for all registered plugins @@ -292,13 +302,19 @@ end source_list[name] << source end - Gem::Dependency.new(name, gem_version) + Gem::Dependency.new(name, *gem_version.split(",")) 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 @@ -401,8 +417,13 @@ Gem::Resolver.compose_sets(*sets) end - # @return [Array<[Gem::Specification, String]>] spec and directory pairs + # @return [Array<[Gem::Specification]>] spec list 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| diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/cli.rb vagrant-2.2.6+dfsg/lib/vagrant/cli.rb --- vagrant-2.2.3+dfsg/lib/vagrant/cli.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/cli.rb 2019-10-14 16:38:13.000000000 +0000 @@ -1,6 +1,8 @@ require 'log4r' require 'optparse' +require 'vagrant/util/experimental' + module Vagrant # Manages the command line interface to Vagrant. class CLI < Vagrant.plugin("2", :command) @@ -11,6 +13,11 @@ @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 @@ -55,7 +62,9 @@ # 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 diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/config/loader.rb vagrant-2.2.6+dfsg/lib/vagrant/config/loader.rb --- vagrant-2.2.3+dfsg/lib/vagrant/config/loader.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/config/loader.rb 2019-10-14 16:38:13.000000000 +0000 @@ -129,7 +129,7 @@ path = "(unknown)" if e.backtrace && e.backtrace[0] backtrace_tokens = e.backtrace[0].split(":") - path = backtrace_tokens[0] + path = e.backtrace.first.slice(0, e.backtrace.first.rindex(':')).rpartition(':').first backtrace_tokens.each do |part| if part =~ /\d+/ line = part.to_i diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/environment.rb vagrant-2.2.6+dfsg/lib/vagrant/environment.rb --- vagrant-2.2.3+dfsg/lib/vagrant/environment.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/environment.rb 2019-10-14 16:38:13.000000000 +0000 @@ -175,9 +175,7 @@ # Load any global plugins Vagrant::Plugin::Manager.instance.load_plugins(plugins) - if !vagrantfile.config.vagrant.plugins.empty? - plugins = process_configured_plugins - end + plugins = process_configured_plugins # Call the hooks that does not require configurations to be loaded # by using a "clean" action runner @@ -210,7 +208,8 @@ home_path: home_path, root_path: root_path, tmp_path: tmp_path, - ui: @ui + ui: @ui, + env: self } end end @@ -921,6 +920,49 @@ 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 @@ -938,7 +980,7 @@ # Check if defined plugins are installed installed = Plugin::Manager.instance.installed_plugins needs_install = [] - config_plugins = vagrantfile.config.vagrant.plugins + config_plugins = find_configured_plugins config_plugins.each do |name, info| if !installed[name] needs_install << name @@ -979,7 +1021,11 @@ ui.warn(I18n.t("vagrant.plugins.local.install_rerun_command")) exit(-1) end - Vagrant::Plugin::Manager.instance.local_file.installed_plugins + 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 diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/errors.rb vagrant-2.2.6+dfsg/lib/vagrant/errors.rb --- vagrant-2.2.3+dfsg/lib/vagrant/errors.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/errors.rb 2019-10-14 16:38:13.000000000 +0000 @@ -804,6 +804,10 @@ 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 diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/machine.rb vagrant-2.2.6+dfsg/lib/vagrant/machine.rb --- vagrant-2.2.3+dfsg/lib/vagrant/machine.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/machine.rb 2019-10-14 16:38:13.000000000 +0000 @@ -110,7 +110,7 @@ @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) + @triggers = Vagrant::Plugin::V2::Trigger.new(@env, @config.trigger, self, @ui) # Read the ID, which is usually in local storage @id = nil @@ -160,10 +160,7 @@ # as extra data set on the environment hash for the middleware # runner. def action(name, opts=nil) - plugins = Vagrant::Plugin::Manager.instance.installed_plugins - if !plugins.keys.include?("vagrant-triggers") - @triggers.fire_triggers(name, :before, @name.to_s) - end + @triggers.fire_triggers(name, :before, @name.to_s, :action) @logger.info("Calling action: #{name} on provider #{@provider}") @@ -175,6 +172,10 @@ # 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 @@ -210,9 +211,7 @@ action_result end - if !plugins.keys.include?("vagrant-triggers") - @triggers.fire_triggers(name, :after, @name.to_s) - end + @triggers.fire_triggers(name, :after, @name.to_s, :action) # preserve returning environment after machine action runs return return_env rescue Errors::EnvironmentLockedError @@ -400,7 +399,10 @@ # 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 = id_file.read.chomp if id_file.file? + id_content = id_file.read.strip if id_file.file? + if !id_content.to_s.empty? + @id = id_content + end end if @id != old_id && @provider diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/plugin/v2/trigger.rb vagrant-2.2.6+dfsg/lib/vagrant/plugin/v2/trigger.rb --- vagrant-2.2.3+dfsg/lib/vagrant/plugin/v2/trigger.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/plugin/v2/trigger.rb 2019-10-14 16:38:13.000000000 +0000 @@ -20,20 +20,35 @@ # @param [Vagrant::Environment] env Vagrant environment # @param [Kernel_V2::TriggerConfig] config Trigger configuration # @param [Vagrant::Machine] machine Active Machine - def initialize(env, config, 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 + # 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) + 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 @@ -51,27 +66,41 @@ guest_name: guest_name end - triggers = filter_triggers(triggers, guest_name) + triggers = filter_triggers(triggers, guest_name, type) if !triggers.empty? @logger.info("Firing trigger for action #{action} on guest #{guest_name}") - @machine.ui.info(I18n.t("vagrant.trigger.start", stage: stage, action: action)) + @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) + 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 @@ -81,7 +110,7 @@ match = false if trigger.only_on trigger.only_on.each do |o| - if o.match(guest_name) + if o.match(guest_name.to_s) # trigger matches on current guest, so we're fine to use it match = true break @@ -91,6 +120,10 @@ 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) @@ -110,10 +143,10 @@ @logger.debug("Running trigger #{trigger.id}...") if trigger.name - @machine.ui.info(I18n.t("vagrant.trigger.fire_with_name", + @ui.info(I18n.t("vagrant.trigger.fire_with_name", name: trigger.name)) else - @machine.ui.info(I18n.t("vagrant.trigger.fire")) + @ui.info(I18n.t("vagrant.trigger.fire")) end if trigger.info @@ -146,14 +179,14 @@ # # @param [String] message The string to be printed def info(message) - @machine.ui.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) - @machine.ui.warn(message) + @ui.warn(message) end # Runs a script on a guest @@ -167,14 +200,14 @@ cmd = Shellwords.split(config.inline) end - @machine.ui.detail(I18n.t("vagrant.trigger.run.inline", command: config.inline)) + @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) - @machine.ui.detail(I18n.t("vagrant.trigger.run.script", path: config.path)) + @ui.detail(I18n.t("vagrant.trigger.run.script", path: config.path)) end # Pick an execution method to run the script or inline string with @@ -199,22 +232,22 @@ options[:color] = :red if !config.keep_color end - @machine.ui.detail(data, options) + @ui.detail(data, options) end if !exit_codes.include?(result.exit_code) raise Errors::TriggersBadExitCodes, code: result.exit_code end rescue => e - @machine.ui.error(I18n.t("vagrant.errors.triggers_run_fail")) - @machine.ui.error(e.message) + @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...") - @machine.ui.warn(I18n.t("vagrant.trigger.on_error_continue")) + @ui.warn(I18n.t("vagrant.trigger.on_error_continue")) end end end @@ -223,7 +256,16 @@ # # @param [ShellProvisioner/Config] config A Shell provisioner config def run_remote(config, on_error, exit_codes) - unless @machine.state.id == :running + 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, @@ -258,8 +300,17 @@ # # @param [Integer] code Code to exit Vagrant on def trigger_abort(exit_code) - @machine.ui.warn(I18n.t("vagrant.trigger.abort")) - exit(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 diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/shared_helpers.rb vagrant-2.2.6+dfsg/lib/vagrant/shared_helpers.rb --- vagrant-2.2.3+dfsg/lib/vagrant/shared_helpers.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/shared_helpers.rb 2019-10-14 16:38:13.000000000 +0000 @@ -35,6 +35,14 @@ !!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). # diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/util/downloader.rb vagrant-2.2.6+dfsg/lib/vagrant/util/downloader.rb --- vagrant-2.2.3+dfsg/lib/vagrant/util/downloader.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/util/downloader.rb 2019-10-14 16:38:13.000000000 +0000 @@ -1,12 +1,14 @@ 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,12 +22,6 @@ # 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 - # Supported file checksum - CHECKSUM_MAP = { - :md5 => Digest::MD5, - :sha1 => Digest::SHA1 - }.freeze - # Hosts that do not require notification on redirect SILENCED_HOSTS = [ "vagrantcloud.com".freeze, @@ -68,8 +64,11 @@ @location_trusted = options[:location_trusted] @checksums = { :md5 => options[:md5], - :sha1 => options[:sha1] - } + :sha1 => options[:sha1], + :sha256 => options[:sha256], + :sha384 => options[:sha384], + :sha512 => options[:sha512] + }.compact end # This executes the actual download, downloading the source file @@ -165,36 +164,23 @@ # @option checksums [String] :sha1 Compare SHA1 checksum # @return [Boolean] def validate_download!(source, path, checksums) - 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 + 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 + ) 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 diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/util/file_checksum.rb vagrant-2.2.6+dfsg/lib/vagrant/util/file_checksum.rb --- vagrant-2.2.3+dfsg/lib/vagrant/util/file_checksum.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/util/file_checksum.rb 2019-10-14 16:38:13.000000000 +0000 @@ -10,13 +10,27 @@ 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) - @digest_klass = digest_klass - @path = path + if digest_klass.is_a?(Class) + @digest_klass = digest_klass + else + @digest_klass = load_digest(digest_klass) + end + + @path = path end # This calculates the checksum of the file and returns it as a @@ -40,6 +54,17 @@ end end - return digest.hexdigest + digest.hexdigest + end + + private + + def load_digest(type) + digest = CHECKSUM_MAP[type.to_s.to_sym] + if digest.nil? + raise Errors::BoxChecksumInvalidType, + type: type.to_s + end + digest end end diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/util/guest_inspection.rb vagrant-2.2.6+dfsg/lib/vagrant/util/guest_inspection.rb --- vagrant-2.2.3+dfsg/lib/vagrant/util/guest_inspection.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/util/guest_inspection.rb 2019-10-14 16:38:13.000000000 +0000 @@ -23,6 +23,25 @@ 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 diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/util/subprocess.rb vagrant-2.2.6+dfsg/lib/vagrant/util/subprocess.rb --- vagrant-2.2.3+dfsg/lib/vagrant/util/subprocess.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/util/subprocess.rb 2019-10-14 16:38:13.000000000 +0000 @@ -129,10 +129,10 @@ 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_LD_LIBRARY_PATH"] + 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_LD_LIBRARY_PATH"]}") - process.environment["LD_LIBRARY_PATH"] = ENV["VAGRANT_APPIMAGE_LD_LIBRARY_PATH"].to_s + @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 diff -Nru vagrant-2.2.3+dfsg/lib/vagrant/vagrantfile.rb vagrant-2.2.6+dfsg/lib/vagrant/vagrantfile.rb --- vagrant-2.2.3+dfsg/lib/vagrant/vagrantfile.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/lib/vagrant/vagrantfile.rb 2019-10-14 16:38:13.000000000 +0000 @@ -112,7 +112,7 @@ # @param [Pathname] data_path Machine data path # @return [Hash] Various configuration parameters for a # machine. See the main documentation body for more info. - def machine_config(name, provider, boxes, data_path=nil) + def machine_config(name, provider, boxes, data_path=nil, validate_provider=true) keys = @keys.dup sub_machine = @config.vm.defined_vms[name] @@ -127,7 +127,7 @@ box_formats = nil if provider != nil provider_plugin = Vagrant.plugin("2").manager.providers[provider] - if !provider_plugin + if !provider_plugin && validate_provider providers = Vagrant.plugin("2").manager.providers.to_hash.keys if providers providers_str = providers.join(', ') @@ -145,18 +145,22 @@ machine: name, provider: provider, providers: providers_str end - 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 + if validate_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 end end diff -Nru vagrant-2.2.3+dfsg/LICENSE vagrant-2.2.6+dfsg/LICENSE --- vagrant-2.2.3+dfsg/LICENSE 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/LICENSE 2019-10-14 16:38:13.000000000 +0000 @@ -1,6 +1,6 @@ The MIT License -Copyright (c) 2010-2018 Mitchell Hashimoto +Copyright (c) 2010-2019 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 diff -Nru vagrant-2.2.3+dfsg/plugins/commands/box/command/add.rb vagrant-2.2.6+dfsg/plugins/commands/box/command/add.rb --- vagrant-2.2.3+dfsg/plugins/commands/box/command/add.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/box/command/add.rb 2019-10-14 16:38:13.000000000 +0000 @@ -86,7 +86,7 @@ box_force: options[:force], box_download_ca_cert: options[:ca_cert], box_download_ca_path: options[:ca_path], - box_client_cert: options[:client_cert], + box_download_client_cert: options[:client_cert], box_download_insecure: options[:insecure], box_download_location_trusted: options[:location_trusted], ui: Vagrant::UI::Prefixed.new(@env.ui, "box"), diff -Nru vagrant-2.2.3+dfsg/plugins/commands/box/command/prune.rb vagrant-2.2.6+dfsg/plugins/commands/box/command/prune.rb --- vagrant-2.2.3+dfsg/plugins/commands/box/command/prune.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/box/command/prune.rb 2019-10-14 16:38:13.000000000 +0000 @@ -30,6 +30,10 @@ 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 @@ -41,7 +45,7 @@ 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]) + delete_oldest_boxes(boxes, options[:provider], options[:force], options[:name], options[:dry_run], options[:keep]) # Success, exit status 0 0 @@ -49,7 +53,7 @@ private - def delete_oldest_boxes(boxes, only_provider, skip_confirm, only_name, dry_run) + def delete_oldest_boxes(boxes, only_provider, skip_confirm, only_name, dry_run, keep_used_boxes) # Find the longest box name longest_box = boxes.max_by { |x| x[0].length } longest_box_length = longest_box[0].length @@ -112,6 +116,7 @@ box_provider: provider, box_version: version, force_confirm_box_remove: skip_confirm, + keep_used_boxes: keep_used_boxes, box_remove_all_versions: false, }) end diff -Nru vagrant-2.2.3+dfsg/plugins/commands/box/command/update.rb vagrant-2.2.6+dfsg/plugins/commands/box/command/update.rb --- vagrant-2.2.3+dfsg/plugins/commands/box/command/update.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/box/command/update.rb 2019-10-14 16:38:13.000000000 +0000 @@ -80,7 +80,7 @@ end to_update = [ - [name, provider, boxes[name][provider].sort.last], + [name, provider, boxes[name][provider].sort_by{|n| Gem::Version.new(n)}.last], ] to_update.each do |n, p, v| @@ -128,7 +128,13 @@ if download_options[:insecure].nil? download_options[:insecure] = machine.config.vm.box_download_insecure end - box_update(box, version, machine.ui, download_options, force) + + begin + box_update(box, version, machine.ui, download_options, force) + rescue Vagrant::Errors::BoxUpdateNoMetadata => e + machine.ui.warn(e) + next + end end end @@ -158,7 +164,7 @@ box_version: update[1].version, ui: ui, box_force: force, - box_client_cert: download_options[:client_cert], + box_download_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] diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/auth/middleware/add_authentication.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/auth/middleware/add_authentication.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/auth/middleware/add_authentication.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/auth/middleware/add_authentication.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,83 @@ +require "cgi" +require "uri" + +require Vagrant.source_root.join("plugins/commands/cloud/client/client") + +module VagrantPlugins + module CloudCommand + class AddAuthentication + REPLACEMENT_HOSTS = [ + "app.vagrantup.com".freeze, + "atlas.hashicorp.com".freeze + ].freeze + TARGET_HOST = "vagrantcloud.com".freeze + CUSTOM_HOST_NOTIFY_WAIT = 5 + + def self.custom_host_notified! + @_host_notify = true + end + + def self.custom_host_notified? + if defined?(@_host_notify) + @_host_notify + else + false + end + end + + def initialize(app, env) + @app = app + CloudCommand::Plugin.init! + end + + def call(env) + client = Client.new(env[:env]) + token = client.token + + env[:box_urls].map! do |url| + begin + u = URI.parse(url) + if u.host != TARGET_HOST && REPLACEMENT_HOSTS.include?(u.host) + u.host = TARGET_HOST + u.to_s + else + url + end + rescue URI::Error + url + end + end + + server_uri = URI.parse(Vagrant.server_url.to_s) + + if token && !server_uri.host.to_s.empty? + env[:box_urls].map! do |url| + u = URI.parse(url) + + if u.host == server_uri.host + if server_uri.host != TARGET_HOST && !self.class.custom_host_notified? + env[:ui].warn(I18n.t("cloud_command.middleware.authentication.different_target", + custom_host: server_uri.host, known_host: TARGET_HOST) + "\n") + sleep CUSTOM_HOST_NOTIFY_WAIT + self.class.custom_host_notified! + end + + q = CGI.parse(u.query || "") + + current = q["access_token"] + if current && current.empty? + q["access_token"] = token + end + + u.query = URI.encode_www_form(q) + end + + u.to_s + end + end + + @app.call(env) + end.freeze + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/box/create.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/box/create.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/box/create.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/box/create.rb 2019-10-14 16:38:13.000000000 +0000 @@ -19,7 +19,7 @@ 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 |t| + 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| diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/box/delete.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/box/delete.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/box/delete.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/box/delete.rb 2019-10-14 16:38:13.000000000 +0000 @@ -16,7 +16,7 @@ o.separator "Options:" o.separator "" - o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |t| + o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u| options[:username] = u end end diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/locales/en.yml vagrant-2.2.6+dfsg/plugins/commands/cloud/locales/en.yml --- vagrant-2.2.3+dfsg/plugins/commands/cloud/locales/en.yml 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/locales/en.yml 2019-10-14 16:38:13.000000000 +0000 @@ -1,5 +1,17 @@ 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... diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/plugin.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/plugin.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/plugin.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/plugin.rb 2019-10-14 16:38:13.000000000 +0000 @@ -17,6 +17,11 @@ 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! diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/provider/create.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/provider/create.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/provider/create.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/provider/create.rb 2019-10-14 16:38:13.000000000 +0000 @@ -16,9 +16,15 @@ o.separator "Options:" o.separator "" - o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |t| + 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 @@ -52,16 +58,17 @@ 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) + 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.delete_if{|_, v|v.nil?} + @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(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 diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/provider/delete.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/provider/delete.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/provider/delete.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/provider/delete.rb 2019-10-14 16:38:13.000000000 +0000 @@ -16,7 +16,7 @@ o.separator "Options:" o.separator "" - o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |t| + o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u| options[:username] = u end end diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/provider/update.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/provider/update.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/provider/update.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/provider/update.rb 2019-10-14 16:38:13.000000000 +0000 @@ -16,9 +16,15 @@ o.separator "Options:" o.separator "" - o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |t| + 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 @@ -52,7 +58,8 @@ 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) + 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 diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/provider/upload.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/provider/upload.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/provider/upload.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/provider/upload.rb 2019-10-14 16:38:13.000000000 +0000 @@ -17,7 +17,7 @@ o.separator "Options:" o.separator "" - o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |t| + o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u| options[:username] = u end end diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/publish.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/publish.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/publish.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/publish.rb 2019-10-14 16:38:13.000000000 +0000 @@ -40,9 +40,15 @@ 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 |t| + 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 @@ -97,7 +103,8 @@ 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) + 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") diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/search.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/search.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/search.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/search.rb 2019-10-14 16:38:13.000000000 +0000 @@ -37,7 +37,7 @@ o.on("--sort-by SORT", "Field to sort results on (created, downloads, updated) Default: downloads") do |s| options[:sort] = s end - o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |t| + o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address to login with") do |u| options[:username] = u end end diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/version/create.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/version/create.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/version/create.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/version/create.rb 2019-10-14 16:38:13.000000000 +0000 @@ -19,7 +19,7 @@ o.on("-d", "--description DESCRIPTION", String, "A description for this version") do |d| options[:description] = d end - o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |t| + o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u| options[:username] = u end end diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/version/delete.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/version/delete.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/version/delete.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/version/delete.rb 2019-10-14 16:38:13.000000000 +0000 @@ -15,7 +15,7 @@ o.separator "" o.separator "Options:" o.separator "" - o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |t| + o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u| options[:username] = u end end diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/version/release.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/version/release.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/version/release.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/version/release.rb 2019-10-14 16:38:13.000000000 +0000 @@ -15,7 +15,7 @@ o.separator "" o.separator "Options:" o.separator "" - o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |t| + o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u| options[:username] = u end end diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/version/revoke.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/version/revoke.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/version/revoke.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/version/revoke.rb 2019-10-14 16:38:13.000000000 +0000 @@ -15,7 +15,7 @@ o.separator "" o.separator "Options:" o.separator "" - o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |t| + o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u| options[:username] = u end end diff -Nru vagrant-2.2.3+dfsg/plugins/commands/cloud/version/update.rb vagrant-2.2.6+dfsg/plugins/commands/cloud/version/update.rb --- vagrant-2.2.3+dfsg/plugins/commands/cloud/version/update.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/cloud/version/update.rb 2019-10-14 16:38:13.000000000 +0000 @@ -19,7 +19,7 @@ o.on("-d", "--description DESCRIPTION", "A description for this version") do |d| options[:description] = d end - o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |t| + o.on("-u", "--username USERNAME_OR_EMAIL", String, "Vagrant Cloud username or email address") do |u| options[:username] = u end end diff -Nru vagrant-2.2.3+dfsg/plugins/commands/global-status/command.rb vagrant-2.2.6+dfsg/plugins/commands/global-status/command.rb --- vagrant-2.2.3+dfsg/plugins/commands/global-status/command.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/global-status/command.rb 2019-10-14 16:38:13.000000000 +0000 @@ -65,6 +65,17 @@ @env.machine_index.delete(deletable) if deletable end + # Machine-readable (non-formatted) output + @env.ui.machine("metadata", "machine-count", entries.length.to_s); + entries.each do |entry| + opts = { "target" => entry.name.to_s } + @env.ui.machine("machine-id", entry.id.to_s[0...7], opts) + @env.ui.machine("provider-name", entry.provider.to_s, opts) + @env.ui.machine("machine-home", entry.vagrantfile_path.to_s, opts) + @env.ui.machine("state", entry.state.to_s, opts) + end + + # Human-readable (table formatted) output total_width = 0 columns.each do |header, method| header = header.ljust(widths[method]) if widths[method] diff -Nru vagrant-2.2.3+dfsg/plugins/commands/login/middleware/add_authentication.rb vagrant-2.2.6+dfsg/plugins/commands/login/middleware/add_authentication.rb --- vagrant-2.2.3+dfsg/plugins/commands/login/middleware/add_authentication.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/login/middleware/add_authentication.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -require "cgi" -require "uri" - -require_relative "../client" - -module VagrantPlugins - module LoginCommand - class AddAuthentication - REPLACEMENT_HOSTS = [ - "app.vagrantup.com".freeze, - "atlas.hashicorp.com".freeze - ].freeze - TARGET_HOST = "vagrantcloud.com".freeze - CUSTOM_HOST_NOTIFY_WAIT = 5 - - def self.custom_host_notified! - @_host_notify = true - end - - def self.custom_host_notified? - if defined?(@_host_notify) - @_host_notify - else - false - end - end - - def initialize(app, env) - @app = app - LoginCommand::Plugin.init! - end - - def call(env) - client = Client.new(env[:env]) - token = client.token - - env[:box_urls].map! do |url| - begin - u = URI.parse(url) - if u.host != TARGET_HOST && REPLACEMENT_HOSTS.include?(u.host) - u.host = TARGET_HOST - u.to_s - else - url - end - rescue URI::Error - url - end - end - - server_uri = URI.parse(Vagrant.server_url.to_s) - - if token && !server_uri.host.to_s.empty? - env[:box_urls].map! do |url| - u = URI.parse(url) - - if u.host == server_uri.host - if server_uri.host != TARGET_HOST && !self.class.custom_host_notified? - env[:ui].warn(I18n.t("login_command.middleware.authentication.different_target", - custom_host: server_uri.host, known_host: TARGET_HOST) + "\n") - sleep CUSTOM_HOST_NOTIFY_WAIT - self.class.custom_host_notified! - end - - q = CGI.parse(u.query || "") - - current = q["access_token"] - if current && current.empty? - q["access_token"] = token - end - - u.query = URI.encode_www_form(q) - end - - u.to_s - end - end - - @app.call(env) - end.freeze - end - end -end diff -Nru vagrant-2.2.3+dfsg/plugins/commands/login/plugin.rb vagrant-2.2.6+dfsg/plugins/commands/login/plugin.rb --- vagrant-2.2.3+dfsg/plugins/commands/login/plugin.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/login/plugin.rb 2019-10-14 16:38:13.000000000 +0000 @@ -17,11 +17,6 @@ VagrantPlugins::CloudCommand::AuthCommand::Command::Login end - action_hook(:cloud_authenticated_boxes, :authenticate_box_url) do |hook| - require_relative "middleware/add_authentication" - hook.prepend(AddAuthentication) - end - protected def self.init! diff -Nru vagrant-2.2.3+dfsg/plugins/commands/snapshot/command/list.rb vagrant-2.2.6+dfsg/plugins/commands/snapshot/command/list.rb --- vagrant-2.2.3+dfsg/plugins/commands/snapshot/command/list.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/snapshot/command/list.rb 2019-10-14 16:38:13.000000000 +0000 @@ -32,8 +32,9 @@ next end + vm.ui.output("", prefix: true) snapshots.each do |snapshot| - vm.ui.output(snapshot, prefix: false) + vm.ui.detail(snapshot, prefix: false) end end diff -Nru vagrant-2.2.3+dfsg/plugins/commands/snapshot/command/pop.rb vagrant-2.2.6+dfsg/plugins/commands/snapshot/command/pop.rb --- vagrant-2.2.3+dfsg/plugins/commands/snapshot/command/pop.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/snapshot/command/pop.rb 2019-10-14 16:38:13.000000000 +0000 @@ -23,8 +23,10 @@ opts = OptionParser.new do |o| o.banner = "Usage: vagrant snapshot pop [options] [vm-name]" o.separator "" + o.separator "Restore state that was pushed onto the snapshot stack" + o.separator "with `vagrant snapshot push`." + o.separator "" build_start_options(o, options) - o.separator "Restore state that was pushed with `vagrant snapshot push`." o.on("--no-delete", "Don't delete the snapshot after the restore") do options[:snapshot_delete] = false diff -Nru vagrant-2.2.3+dfsg/plugins/commands/snapshot/command/save.rb vagrant-2.2.6+dfsg/plugins/commands/snapshot/command/save.rb --- vagrant-2.2.3+dfsg/plugins/commands/snapshot/command/save.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/commands/snapshot/command/save.rb 2019-10-14 16:38:13.000000000 +0000 @@ -31,7 +31,15 @@ help: opts.help.chomp end - name = argv.pop + # If no snapshot name is given, the backup name is the same as the machine name. + # If there is a name given, we need to remove it and save it as `name`. Otherwise + # `with_target_vms` will treat the snapshot name as a guest name. + if argv.size < 2 + name = argv.first + else + name = argv.pop + end + with_target_vms(argv) do |vm| if !vm.provider.capability?(:snapshot_list) raise Vagrant::Errors::SnapshotNotSupported diff -Nru vagrant-2.2.3+dfsg/plugins/communicators/ssh/communicator.rb vagrant-2.2.6+dfsg/plugins/communicators/ssh/communicator.rb --- vagrant-2.2.3+dfsg/plugins/communicators/ssh/communicator.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/communicators/ssh/communicator.rb 2019-10-14 16:38:13.000000000 +0000 @@ -290,14 +290,47 @@ def upload(from, to) @logger.debug("Uploading: #{from} to #{to}") - scp_connect do |scp| - if File.directory?(from) - # Recursively upload directories - scp.upload!(from, to, recursive: true) + if File.directory?(from) + if from.end_with?(".") + @logger.debug("Uploading directory contents of: #{from}") + from = from.sub(/\.$/, "") else - # Open file read only to fix issue [GH-1036] - scp.upload!(File.open(from, "r"), to) + @logger.debug("Uploading full directory container of: #{from}") + to = File.join(to, File.basename(File.expand_path(from))) + end + end + + scp_connect do |scp| + uploader = lambda do |path, remote_dest=nil| + if File.directory?(path) + Dir.new(path).each do |entry| + next if entry == "." || entry == ".." + full_path = File.join(path, entry) + dest = File.join(to, path.sub(/^#{Regexp.escape(from)}/, "")) + create_remote_directory(dest) + uploader.call(full_path, dest) + end + else + if remote_dest + dest = File.join(remote_dest, File.basename(path)) + else + dest = to + if to.end_with?(File::SEPARATOR) + dest = File.join(to, File.basename(path)) + end + end + @logger.debug("Ensuring remote directory exists for destination upload") + create_remote_directory(File.dirname(dest)) + @logger.debug("Uploading file #{path} to remote #{dest}") + upload_file = File.open(path, "rb") + begin + scp.upload!(upload_file, dest) + ensure + upload_file.close + end + end end + uploader.call(from) end rescue RuntimeError => e # Net::SCP raises a runtime error for this so the only way we have @@ -731,6 +764,10 @@ template.sub("%ENV_KEY%", env_key).sub("%ENV_VALUE%", env_value) + "\n" end + def create_remote_directory(dir) + execute("mkdir -p \"#{dir}\"") + end + def machine_config_ssh @machine.config.ssh end diff -Nru vagrant-2.2.3+dfsg/plugins/communicators/winrm/shell.rb vagrant-2.2.6+dfsg/plugins/communicators/winrm/shell.rb --- vagrant-2.2.3+dfsg/plugins/communicators/winrm/shell.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/communicators/winrm/shell.rb 2019-10-14 16:38:13.000000000 +0000 @@ -108,6 +108,13 @@ # @return [FixNum] Total size transfered from host to guest def upload(from, to) file_manager = WinRM::FS::FileManager.new(connection) + if from.is_a?(String) && File.directory?(from) + if from.end_with?(".") + from = from[0, from.length - 1] + else + to = File.join(to, File.basename(File.expand_path(from))) + end + end if from.is_a?(Array) # Preserve return FixNum of bytes transfered return_bytes = 0 diff -Nru vagrant-2.2.3+dfsg/plugins/guests/alpine/cap/change_host_name.rb vagrant-2.2.6+dfsg/plugins/guests/alpine/cap/change_host_name.rb --- vagrant-2.2.3+dfsg/plugins/guests/alpine/cap/change_host_name.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/alpine/cap/change_host_name.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,83 @@ +module VagrantPlugins + module GuestAlpine + module Cap + class ChangeHostName + def self.change_host_name(machine, name) + new(machine, name).change! + end + + attr_reader :machine, :new_hostname + + def initialize(machine, new_hostname) + @machine = machine + @new_hostname = new_hostname + end + + def change! + return unless should_change? + + update_etc_hostname + update_etc_hosts + refresh_hostname_service + update_mailname + renew_dhcp + end + + def should_change? + new_hostname != current_hostname + end + + def current_hostname + @current_hostname ||= fetch_current_hostname + end + + def fetch_current_hostname + hostname = '' + machine.communicate.sudo 'hostname -f' do |type, data| + hostname = data.chomp if type == :stdout && hostname.empty? + end + + hostname + end + + def update_etc_hostname + machine.communicate.sudo("echo '#{short_hostname}' > /etc/hostname") + end + + # /etc/hosts should resemble: + # 127.0.0.1 localhost + # 127.0.1.1 host.fqdn.com host.fqdn host + def update_etc_hosts + if machine.communicate.test("grep '#{current_hostname}' /etc/hosts") + # Current hostname entry is in /etc/hosts + ip_address = '([0-9]{1,3}\.){3}[0-9]{1,3}' + search = "^(#{ip_address})\\s+#{Regexp.escape(current_hostname)}(\\s.*)?$" + replace = "\\1 #{new_hostname} #{short_hostname}" + expression = ['s', search, replace, 'g'].join('@') + + machine.communicate.sudo("sed -ri '#{expression}' /etc/hosts") + else + # Current hostname entry isn't in /etc/hosts, just append it + machine.communicate.sudo("echo '127.0.1.1 #{new_hostname} #{short_hostname}' >>/etc/hosts") + end + end + + def refresh_hostname_service + machine.communicate.sudo('hostname -F /etc/hostname') + end + + def update_mailname + machine.communicate.sudo('hostname -f > /etc/mailname') + end + + def renew_dhcp + machine.communicate.sudo('ifdown -a; ifup -a; ifup eth0') + end + + def short_hostname + new_hostname.split('.').first + end + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/alpine/cap/configure_networks.rb vagrant-2.2.6+dfsg/plugins/guests/alpine/cap/configure_networks.rb --- vagrant-2.2.3+dfsg/plugins/guests/alpine/cap/configure_networks.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/alpine/cap/configure_networks.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,64 @@ +# rubocop:disable Metrics/MethodLength +# rubocop:disable Metrics/AbcSize +# rubocop:disable Style/BracesAroundHashParameters +# +# FIXME: address disabled warnings +# +require 'set' +require 'tempfile' +require 'pathname' +require 'vagrant/util/template_renderer' + +module VagrantPlugins + module GuestAlpine + module Cap + class ConfigureNetworks + include Vagrant::Util + def self.configure_networks(machine, networks) + machine.communicate.tap do |comm| + # First, remove any previous network modifications + # from the interface file. + comm.sudo("sed -e '/^#VAGRANT-BEGIN/,$ d' /etc/network/interfaces > /tmp/vagrant-network-interfaces.pre") + comm.sudo("sed -ne '/^#VAGRANT-END/,$ p' /etc/network/interfaces | tail -n +2 > /tmp/vagrant-network-interfaces.post") + + # Accumulate the configurations to add to the interfaces file as + # well as what interfaces we're actually configuring since we use that + # later. + interfaces = Set.new + entries = [] + networks.each do |network| + interfaces.add(network[:interface]) + entry = TemplateRenderer.render("guests/alpine/network_#{network[:type]}", { options: network }) + entries << entry + end + + # Perform the careful dance necessary to reconfigure + # the network interfaces + temp = Tempfile.new('vagrant') + temp.binmode + temp.write(entries.join("\n")) + temp.close + + comm.upload(temp.path, '/tmp/vagrant-network-entry') + + # Bring down all the interfaces we're reconfiguring. By bringing down + # each specifically, we avoid reconfiguring eth0 (the NAT interface) so + # SSH never dies. + interfaces.each do |interface| + comm.sudo("/sbin/ifdown eth#{interface} 2> /dev/null") + comm.sudo("/sbin/ip addr flush dev eth#{interface} 2> /dev/null") + end + + comm.sudo('cat /tmp/vagrant-network-interfaces.pre /tmp/vagrant-network-entry /tmp/vagrant-network-interfaces.post > /etc/network/interfaces') + comm.sudo('rm -f /tmp/vagrant-network-interfaces.pre /tmp/vagrant-network-entry /tmp/vagrant-network-interfaces.post') + + # Bring back up each network interface, reconfigured + interfaces.each do |interface| + comm.sudo("/sbin/ifup eth#{interface}") + end + end + end + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/alpine/cap/halt.rb vagrant-2.2.6+dfsg/plugins/guests/alpine/cap/halt.rb --- vagrant-2.2.3+dfsg/plugins/guests/alpine/cap/halt.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/alpine/cap/halt.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,21 @@ +# rubocop:disable Style/RedundantBegin +# rubocop:disable Lint/HandleExceptions +# +# FIXME: address disabled warnings +# +module VagrantPlugins + module GuestAlpine + module Cap + class Halt + def self.halt(machine) + begin + machine.communicate.sudo('poweroff') + rescue Net::SSH::Disconnect, IOError + # Ignore, this probably means connection closed because it + # shut down and SSHd was stopped. + end + end + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/alpine/cap/nfs_client.rb vagrant-2.2.6+dfsg/plugins/guests/alpine/cap/nfs_client.rb --- vagrant-2.2.3+dfsg/plugins/guests/alpine/cap/nfs_client.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/alpine/cap/nfs_client.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,14 @@ +module VagrantPlugins + module GuestAlpine + module Cap + class NFSClient + def self.nfs_client_install(machine) + machine.communicate.sudo('apk update') + machine.communicate.sudo('apk add --upgrade nfs-utils') + machine.communicate.sudo('rc-update add rpc.statd') + machine.communicate.sudo('rc-service rpc.statd start') + end + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/alpine/cap/rsync.rb vagrant-2.2.6+dfsg/plugins/guests/alpine/cap/rsync.rb --- vagrant-2.2.3+dfsg/plugins/guests/alpine/cap/rsync.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/alpine/cap/rsync.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,17 @@ +module VagrantPlugins + module GuestAlpine + module Cap + class RSync + def self.rsync_installed(machine) + machine.communicate.test('test -f /usr/bin/rsync') + end + + def self.rsync_install(machine) + machine.communicate.tap do |comm| + comm.sudo('apk add rsync') + end + end + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/alpine/cap/smb.rb vagrant-2.2.6+dfsg/plugins/guests/alpine/cap/smb.rb --- vagrant-2.2.3+dfsg/plugins/guests/alpine/cap/smb.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/alpine/cap/smb.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,13 @@ +module VagrantPlugins + module GuestAlpine + module Cap + class SMB + def self.smb_install(machine) + machine.communicate.tap do |comm| + comm.sudo('apk add cifs-utils') + end + end + end + end + end +end \ No newline at end of file diff -Nru vagrant-2.2.3+dfsg/plugins/guests/alpine/guest.rb vagrant-2.2.6+dfsg/plugins/guests/alpine/guest.rb --- vagrant-2.2.3+dfsg/plugins/guests/alpine/guest.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/alpine/guest.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,11 @@ +require 'vagrant' + +module VagrantPlugins + module GuestAlpine + class Guest < Vagrant.plugin('2', :guest) + def detect?(machine) + machine.communicate.test('cat /etc/alpine-release') + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/alpine/plugin.rb vagrant-2.2.6+dfsg/plugins/guests/alpine/plugin.rb --- vagrant-2.2.3+dfsg/plugins/guests/alpine/plugin.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/alpine/plugin.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,50 @@ +require 'vagrant' + +module VagrantPlugins + module GuestAlpine + class Plugin < Vagrant.plugin('2') + name 'Alpine guest' + description 'Alpine Linux guest support.' + + guest(:alpine, :linux) do + require File.expand_path('../guest', __FILE__) + Guest + end + + guest_capability(:alpine, :configure_networks) do + require_relative 'cap/configure_networks' + Cap::ConfigureNetworks + end + + guest_capability(:alpine, :halt) do + require_relative 'cap/halt' + Cap::Halt + end + + guest_capability(:alpine, :change_host_name) do + require_relative 'cap/change_host_name' + Cap::ChangeHostName + end + + guest_capability(:alpine, :nfs_client_install) do + require_relative 'cap/nfs_client' + Cap::NFSClient + end + + guest_capability(:alpine, :rsync_installed) do + require_relative 'cap/rsync' + Cap::RSync + end + + guest_capability(:alpine, :rsync_install) do + require_relative 'cap/rsync' + Cap::RSync + end + + guest_capability(:alpine, :smb_install) do + require_relative 'cap/smb' + Cap::SMB + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/alt/cap/change_host_name.rb vagrant-2.2.6+dfsg/plugins/guests/alt/cap/change_host_name.rb --- vagrant-2.2.3+dfsg/plugins/guests/alt/cap/change_host_name.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/alt/cap/change_host_name.rb 2019-10-14 16:38:13.000000000 +0000 @@ -16,9 +16,6 @@ NEW_HOSTNAME_FULL='#{name}' NEW_HOSTNAME_SHORT="${NEW_HOSTNAME_FULL%%.*}" - # Update sysconfig - sed -i 's/\\(HOSTNAME=\\).*/\\1#{name}/' /etc/sysconfig/network - # Set the hostname - use hostnamectl if available if command -v hostnamectl; then hostnamectl set-hostname --static '#{name}' @@ -35,8 +32,30 @@ sed -i -e "s/\(\s\)$CURRENT_HOSTNAME_SHORT\(\s\)/\1$NEW_HOSTNAME_SHORT\2/g" -e "s/\(\s\)$CURRENT_HOSTNAME_SHORT$/\1$NEW_HOSTNAME_SHORT/g" /etc/hosts fi - # Restart network - service network restart + # Persist hostname change across reboots + if [ -f /etc/sysconfig/network ]; then + sed -i 's/\\(HOSTNAME=\\).*/\\1#{name}/' /etc/sysconfig/network + elif [ -f /etc/hostname ]; then + sed -i 's/.*/#{name}/' /etc/hostname + else + echo 'Unrecognized system. Hostname change may not persist across reboots.' + exit 0 + fi + + # Restart the network if we find a recognized SYS V init script + if command -v service; then + if [ -f /etc/init.d/network ]; then + service network restart + elif [ -f /etc/init.d/networking ]; then + service networking restart + elif [ -f /etc/init.d/NetworkManager ]; then + service NetworkManager restart + else + echo 'Unrecognized system. Networking was not restarted following hostname change.' + exit 0 + fi + fi + EOH end end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/bsd/cap/mount_virtualbox_shared_folder.rb vagrant-2.2.6+dfsg/plugins/guests/bsd/cap/mount_virtualbox_shared_folder.rb --- vagrant-2.2.3+dfsg/plugins/guests/bsd/cap/mount_virtualbox_shared_folder.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/bsd/cap/mount_virtualbox_shared_folder.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,16 @@ +module VagrantPlugins + module GuestBSD + module Cap + class MountVirtualBoxSharedFolder + # BSD-based guests do not currently support VirtualBox synced folders. + # Instead of raising an error about a missing capability, this defines + # the capability and then provides a more detailed error message, + # linking to sources on the Internet where the problem is + # better-described. + def self.mount_virtualbox_shared_folder(machine, name, guestpath, options) + raise Vagrant::Errors::VirtualBoxMountNotSupportedBSD + end + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/bsd/cap/virtualbox.rb vagrant-2.2.6+dfsg/plugins/guests/bsd/cap/virtualbox.rb --- vagrant-2.2.3+dfsg/plugins/guests/bsd/cap/virtualbox.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/bsd/cap/virtualbox.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -module VagrantPlugins - module GuestBSD - module Cap - class VirtualBox - # BSD-based guests do not currently support VirtualBox synced folders. - # Instead of raising an error about a missing capability, this defines - # the capability and then provides a more detailed error message, - # linking to sources on the Internet where the problem is - # better-described. - def self.mount_virtualbox_shared_folder(machine, name, guestpath, options) - raise Vagrant::Errors::VirtualBoxMountNotSupportedBSD - end - end - end - end -end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/bsd/plugin.rb vagrant-2.2.6+dfsg/plugins/guests/bsd/plugin.rb --- vagrant-2.2.3+dfsg/plugins/guests/bsd/plugin.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/bsd/plugin.rb 2019-10-14 16:38:13.000000000 +0000 @@ -42,8 +42,8 @@ end guest_capability(:bsd, :mount_virtualbox_shared_folder) do - require_relative "cap/virtualbox" - Cap::VirtualBox + require_relative "cap/mount_virtualbox_shared_folder" + Cap::MountVirtualBoxSharedFolder end guest_capability(:bsd, :remove_public_key) do diff -Nru vagrant-2.2.3+dfsg/plugins/guests/coreos/cap/change_host_name.rb vagrant-2.2.6+dfsg/plugins/guests/coreos/cap/change_host_name.rb --- vagrant-2.2.3+dfsg/plugins/guests/coreos/cap/change_host_name.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/coreos/cap/change_host_name.rb 2019-10-14 16:38:13.000000000 +0000 @@ -1,18 +1,35 @@ +require "tempfile" +require "yaml" + module VagrantPlugins module GuestCoreOS module Cap class ChangeHostName + extend Vagrant::Util::GuestInspection::Linux + def self.change_host_name(machine, name) comm = machine.communicate - if !comm.test("hostname -f | grep '^#{name}$'", sudo: false) - basename = name.split(".", 2)[0] - comm.sudo("hostname '#{basename}'") + if systemd_unit_file?(comm, "system-cloudinit*") + file = Tempfile.new("vagrant-coreos-hostname") + file.puts("#cloud-config\n") + file.puts({"hostname" => name}.to_yaml) + file.close + + dst = "/var/tmp/hostname.yml" + svc_path = dst.tr("/", "-")[1..-1] + comm.upload(file.path, dst) + comm.sudo("systemctl start system-cloudinit@#{svc_path}.service") + else + if !comm.test("hostname -f | grep '^#{name}$'", sudo: false) + basename = name.split(".", 2)[0] + comm.sudo("hostname '#{basename}'") - # Note that when working with CoreOS, we explicitly do not add the - # entry to /etc/hosts because this file does not exist on CoreOS. - # We could create it, but the recommended approach on CoreOS is to - # use Fleet to manage /etc/hosts files. + # Note that when working with CoreOS, we explicitly do not add the + # entry to /etc/hosts because this file does not exist on CoreOS. + # We could create it, but the recommended approach on CoreOS is to + # use Fleet to manage /etc/hosts files. + end end end end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/coreos/cap/configure_networks.rb vagrant-2.2.6+dfsg/plugins/guests/coreos/cap/configure_networks.rb --- vagrant-2.2.3+dfsg/plugins/guests/coreos/cap/configure_networks.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/coreos/cap/configure_networks.rb 2019-10-14 16:38:13.000000000 +0000 @@ -6,79 +6,63 @@ module GuestCoreOS module Cap class ConfigureNetworks - include Vagrant::Util + extend Vagrant::Util::GuestInspection::Linux - def self.configure_networks(machine, networks) - machine.communicate.tap do |comm| - # Read network interface names - interfaces = [] - comm.sudo("ifconfig | grep -E '(e[n,t][h,s,p][[:digit:]]([a-z][[:digit:]])?)' | cut -f1 -d:") do |_, result| - interfaces = result.split("\n") - end - - primary_machine_config = machine.env.active_machines.first - primary_machine = machine.env.machine(*primary_machine_config, true) + DEFAULT_ENVIRONMENT_IP = "127.0.0.1".freeze - primary_machine_ip = get_ip(primary_machine) - current_ip = get_ip(machine) - if current_ip == primary_machine_ip - entry = TemplateRenderer.render("guests/coreos/etcd.service", options: { - my_ip: current_ip, - }) - else - connection_string = "#{primary_machine_ip}:7001" - entry = TemplateRenderer.render("guests/coreos/etcd.service", options: { - connection_string: connection_string, - my_ip: current_ip, - }) - end - - Tempfile.open("vagrant-coreos-configure-networks") do |f| - f.binmode - f.write(entry) - f.fsync - f.close - comm.upload(f.path, "/tmp/etcd-cluster.service") + def self.configure_networks(machine, networks) + cloud_config = {} + # Locate configured IP addresses to drop in /etc/environment + # for export. If no addresses found, fall back to default + public_ip = catch(:public_ip) do + machine.config.vm.networks.each do |type, opts| + next if type != :public_network + throw(:public_ip, opts[:ip]) if opts[:ip] end - - # Build a list of commands - commands = [] - - # Stop default systemd - commands << "systemctl stop etcd" - - # Configure interfaces - # FIXME: fix matching of interfaces with IP addresses - networks.each do |network| - iface = interfaces[network[:interface].to_i] - commands << "ifconfig #{iface} #{network[:ip]} netmask #{network[:netmask]}".squeeze(" ") + DEFAULT_ENVIRONMENT_IP + end + private_ip = catch(:private_ip) do + machine.config.vm.networks.each do |type, opts| + next if type != :private_network + throw(:private_ip, opts[:ip]) if opts[:ip] end - - commands << <<-EOH.gsub(/^ {14}/, '') - mv /tmp/etcd-cluster.service /media/state/units/ - systemctl restart local-enable.service - - # Restart default etcd - systemctl start etcd - EOH - - # Run all network configuration commands in one communicator session. - comm.sudo(commands.join("\n")) + public_ip end - end - - private - - def self.get_ip(machine) - ip = nil - machine.config.vm.networks.each do |type, opts| - if type == :private_network && opts[:ip] - ip = opts[:ip] - break + cloud_config["write_files"] = [ + {"path" => "/etc/environment", + "content" => "COREOS_PUBLIC_IPV4=#{public_ip}\nCOREOS_PRIVATE_IPV4=#{private_ip}"} + ] + + # Generate configuration for any static network interfaces + # which have been defined + interfaces = machine.guest.capability(:network_interfaces) + units = networks.map do |network| + iface = network[:interface].to_i + unit_name = "50-vagrant#{iface}.network" + device = interfaces[iface] + if network[:type].to_s == "dhcp" + network_content = "DHCP=yes" + else + prefix = IPAddr.new("255.255.255.255/#{network[:netmask]}").to_i.to_s(2).count("1") + address = "#{network[:ip]}/#{prefix}" + network_content = "Address=#{address}" end + {"name" => unit_name, + "runtime" => "no", + "content" => "[Match]\nName=#{device}\n[Network]\n#{network_content}"} end + cloud_config["coreos"] = {"units" => units.compact} - ip + # Upload configuration and apply + file = Tempfile.new("vagrant-coreos-networks") + file.puts("#cloud-config\n") + file.puts(cloud_config.to_yaml) + file.close + + dst = "/var/tmp/networks.yml" + svc_path = dst.tr("/", "-")[1..-1] + machine.communicate.upload(file.path, dst) + machine.communicate.sudo("systemctl start system-cloudinit@#{svc_path}.service") end end end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/debian/cap/configure_networks.rb vagrant-2.2.6+dfsg/plugins/guests/debian/cap/configure_networks.rb --- vagrant-2.2.3+dfsg/plugins/guests/debian/cap/configure_networks.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/debian/cap/configure_networks.rb 2019-10-14 16:38:13.000000000 +0000 @@ -89,7 +89,9 @@ net_conf << "[Match]" net_conf << "Name=#{dev_name}" net_conf << "[Network]" - if network[:ip] + if network[:type].to_s == "dhcp" + net_conf << "DHCP=yes" + else mask = network[:netmask] if mask && IPAddr.new(network[:ip]).ipv4? begin @@ -102,8 +104,6 @@ net_conf << "DHCP=no" net_conf << "Address=#{address}" net_conf << "Gateway=#{network[:gateway]}" if network[:gateway] - else - net_conf << "DHCP=yes" end remote_path = upload_tmp_file(comm, net_conf.join("\n")) diff -Nru vagrant-2.2.3+dfsg/plugins/guests/freebsd/cap/configure_networks.rb vagrant-2.2.6+dfsg/plugins/guests/freebsd/cap/configure_networks.rb --- vagrant-2.2.3+dfsg/plugins/guests/freebsd/cap/configure_networks.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/freebsd/cap/configure_networks.rb 2019-10-14 16:38:13.000000000 +0000 @@ -18,8 +18,8 @@ # Remove any previous network additions to the configuration file. commands << "sed -i '' -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' /etc/rc.conf" - comm.sudo("ifconfig -a | grep -o '^[0-9a-z]*' | grep -v '^lo'", options) do |_, stdout| - interfaces = stdout.split("\n") + comm.sudo("ifconfig -l ether", options) do |_, stdout| + interfaces = stdout.split end networks.each.with_index do |network, i| diff -Nru vagrant-2.2.3+dfsg/plugins/guests/freebsd/cap/mount_virtualbox_shared_folder.rb vagrant-2.2.6+dfsg/plugins/guests/freebsd/cap/mount_virtualbox_shared_folder.rb --- vagrant-2.2.3+dfsg/plugins/guests/freebsd/cap/mount_virtualbox_shared_folder.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/freebsd/cap/mount_virtualbox_shared_folder.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,76 @@ +require_relative "../../../synced_folders/unix_mount_helpers" + +module VagrantPlugins + module GuestFreeBSD + module Cap + class MountVirtualBoxSharedFolder + extend SyncedFolder::UnixMountHelpers + + def self.mount_virtualbox_shared_folder(machine, name, guestpath, options) + guest_path = Shellwords.escape(guestpath) + + @@logger.debug("Mounting #{name} (#{options[:hostpath]} to #{guestpath})") + + builtin_mount_type = "-cit vboxvfs" + addon_mount_type = "-t vboxvfs" + + mount_options = options.fetch(:mount_options, []) + detected_ids = detect_owner_group_ids(machine, guest_path, mount_options, options) + mount_uid = detected_ids[:uid] + mount_gid = detected_ids[:gid] + + mount_options << "uid=#{mount_uid}" + mount_options << "gid=#{mount_gid}" + mount_options = mount_options.join(',') + mount_command = "mount #{addon_mount_type} -o #{mount_options} #{name} #{guest_path}" + + # Create the guest path if it doesn't exist + machine.communicate.sudo("mkdir -p #{guest_path}") + + stderr = "" + result = machine.communicate.sudo(mount_command, error_check: false) do |type, data| + stderr << data if type == :stderr + end + + if result != 0 + if stderr.include?("-cit") + @@logger.info("Detected builtin vboxvfs module, modifying mount command") + mount_command.sub!(addon_mount_type, builtin_mount_type) + end + + # Attempt to mount the folder. We retry here a few times because + # it can fail early on. + stderr = "" + retryable(on: Vagrant::Errors::VirtualBoxMountFailed, tries: 3, sleep: 5) do + machine.communicate.sudo(mount_command, + error_class: Vagrant::Errors::VirtualBoxMountFailed, + error_key: :virtualbox_mount_failed, + command: mount_command, + output: stderr, + ) { |type, data| stderr = data if type == :stderr } + end + end + + # Chown the directory to the proper user. We skip this if the + # mount options contained a readonly flag, because it won't work. + if !options[:mount_options] || !options[:mount_options].include?("ro") + chown_command = "chown #{mount_uid}:#{mount_gid} #{guest_path}" + machine.communicate.sudo(chown_command) + end + + emit_upstart_notification(machine, guest_path) + end + + + def self.unmount_virtualbox_shared_folder(machine, guestpath, options) + guest_path = Shellwords.escape(guestpath) + + result = machine.communicate.sudo("umount #{guest_path}", error_check: false) + if result == 0 + machine.communicate.sudo("rmdir #{guest_path}", error_check: false) + end + end + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/freebsd/plugin.rb vagrant-2.2.6+dfsg/plugins/guests/freebsd/plugin.rb --- vagrant-2.2.3+dfsg/plugins/guests/freebsd/plugin.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/freebsd/plugin.rb 2019-10-14 16:38:13.000000000 +0000 @@ -50,6 +50,16 @@ require_relative "cap/shell_expand_guest_path" Cap::ShellExpandGuestPath end + + guest_capability(:freebsd, :mount_virtualbox_shared_folder) do + require_relative "cap/mount_virtualbox_shared_folder" + Cap::MountVirtualBoxSharedFolder + end + + guest_capability(:freebsd, :unmount_virtualbox_shared_folder) do + require_relative "cap/mount_virtualbox_shared_folder" + Cap::MountVirtualBoxSharedFolder + end end end end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/solaris11/cap/configure_networks.rb vagrant-2.2.6+dfsg/plugins/guests/solaris11/cap/configure_networks.rb --- vagrant-2.2.3+dfsg/plugins/guests/solaris11/cap/configure_networks.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/solaris11/cap/configure_networks.rb 2019-10-14 16:38:13.000000000 +0000 @@ -14,6 +14,9 @@ cidr = mask.split(".").map { |e| e.to_i.to_s(2).rjust(8, "0") }.join.count("1").to_s if network[:type].to_sym == :static + unless machine.communicate.test("ipadm show-if #{device}") + machine.communicate.execute("#{su_cmd} ipadm create-ip #{device}") + end if machine.communicate.test("ipadm | grep #{device}/v4") machine.communicate.execute("#{su_cmd} ipadm delete-addr #{device}/v4") end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/suse/cap/change_host_name.rb vagrant-2.2.6+dfsg/plugins/guests/suse/cap/change_host_name.rb --- vagrant-2.2.3+dfsg/plugins/guests/suse/cap/change_host_name.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/suse/cap/change_host_name.rb 2019-10-14 16:38:13.000000000 +0000 @@ -5,11 +5,10 @@ def self.change_host_name(machine, name) comm = machine.communicate - if !comm.test("hostname -f | grep '^#{name}$'", sudo: false) + if !comm.test("getent hosts '#{name}'", sudo: false) basename = name.split(".", 2)[0] comm.sudo <<-EOH.gsub(/^ {14}/, '') - echo '#{basename}' > /etc/HOSTNAME - hostname '#{basename}' + hostnamectl set-hostname '#{basename}' # Prepend ourselves to /etc/hosts grep -w '#{name}' /etc/hosts || { diff -Nru vagrant-2.2.3+dfsg/plugins/guests/suse/cap/nfs_client.rb vagrant-2.2.6+dfsg/plugins/guests/suse/cap/nfs_client.rb --- vagrant-2.2.3+dfsg/plugins/guests/suse/cap/nfs_client.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/suse/cap/nfs_client.rb 2019-10-14 16:38:13.000000000 +0000 @@ -5,8 +5,8 @@ def self.nfs_client_install(machine) machine.communicate.sudo <<-EOH.gsub(/^ {12}/, '') zypper -n install nfs-client - /sbin/service rpcbind restart - /sbin/service nfs restart + /usr/bin/systemctl restart rpcbind + /usr/bin/systemctl restart nfs-client.target EOH end end diff -Nru vagrant-2.2.3+dfsg/plugins/guests/windows/cap/reboot.rb vagrant-2.2.6+dfsg/plugins/guests/windows/cap/reboot.rb --- vagrant-2.2.3+dfsg/plugins/guests/windows/cap/reboot.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/guests/windows/cap/reboot.rb 2019-10-14 16:38:13.000000000 +0000 @@ -21,12 +21,14 @@ @logger.debug("A reboot is already in progress") end + machine.ui.info(I18n.t("vagrant.guests.capabilities.rebooting")) + @logger.debug("Waiting for machine to finish rebooting") wait_remaining = MAX_REBOOT_RETRY_DURATION begin wait_for_reboot(machine) - rescue Vagrant::Errors::MachineGuestNotReady, WinRM::WinRMHTTPTransportError => e + rescue HTTPClient::ConnectTimeoutError, Vagrant::Errors::MachineGuestNotReady, WinRM::WinRMHTTPTransportError => e raise if wait_remaining < 0 @logger.warn("Machine not ready, cannot start reboot yet. Trying again") sleep(5) diff -Nru vagrant-2.2.3+dfsg/plugins/hosts/bsd/cap/nfs.rb vagrant-2.2.6+dfsg/plugins/hosts/bsd/cap/nfs.rb --- vagrant-2.2.3+dfsg/plugins/hosts/bsd/cap/nfs.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/hosts/bsd/cap/nfs.rb 2019-10-14 16:38:13.000000000 +0000 @@ -26,8 +26,8 @@ logger.debug("Compiling map of sub-directories for NFS exports...") dirmap = {} folders.sort_by { |_, opts| opts[:hostpath] }.each do |_, opts| + opts[:hostpath] = environment.host.capability(:resolve_host_path, opts[:hostpath].gsub('"', '\"')) hostpath = opts[:hostpath].dup - hostpath.gsub!('"', '\"') found = false dirmap.each do |dirs, diropts| @@ -120,7 +120,7 @@ end def self.nfs_exports_template(environment) - "nfs/exports" + "nfs/exports_bsd" end def self.nfs_installed(environment) diff -Nru vagrant-2.2.3+dfsg/plugins/hosts/bsd/cap/path.rb vagrant-2.2.6+dfsg/plugins/hosts/bsd/cap/path.rb --- vagrant-2.2.3+dfsg/plugins/hosts/bsd/cap/path.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/hosts/bsd/cap/path.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,11 @@ +module VagrantPlugins + module HostBSD + module Cap + class Path + def self.resolve_host_path(env, path) + path + end + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/hosts/bsd/plugin.rb vagrant-2.2.6+dfsg/plugins/hosts/bsd/plugin.rb --- vagrant-2.2.3+dfsg/plugins/hosts/bsd/plugin.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/hosts/bsd/plugin.rb 2019-10-14 16:38:13.000000000 +0000 @@ -36,6 +36,11 @@ Cap::NFS end + host_capability("bsd", "resolve_host_path") do + require_relative "cap/path" + Cap::Path + end + host_capability("bsd", "set_ssh_key_permissions") do require_relative "cap/ssh" Cap::SSH diff -Nru vagrant-2.2.3+dfsg/plugins/hosts/darwin/cap/path.rb vagrant-2.2.6+dfsg/plugins/hosts/darwin/cap/path.rb --- vagrant-2.2.3+dfsg/plugins/hosts/darwin/cap/path.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/hosts/darwin/cap/path.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,58 @@ +module VagrantPlugins + module HostDarwin + module Cap + class Path + @@logger = Log4r::Logger.new("vagrant::host::darwin::path") + + FIRMLINK_DEFS = "/usr/share/firmlinks".freeze + FIRMLINK_DATA_PATH = "/System/Volumes/Data".freeze + + # Resolve the given host path to the actual + # usable system path by detecting firmlinks + # if available on the current system + # + # @param [String] path Host system path + # @return [String] resolved path + def self.resolve_host_path(env, path) + path = File.expand_path(path) + firmlink = firmlink_map.detect do |mount_path, data_path| + path.start_with?(mount_path) + end + return path if firmlink.nil? + current_prefix, new_suffix = firmlink + new_prefix = File.join(FIRMLINK_DATA_PATH, new_suffix) + new_path = path.sub(current_prefix, new_prefix) + @@logger.debug("Resolved given path `#{path}` to `#{new_path}`") + new_path + end + + # Generate mapping of firmlinks if available on the host + # + # @return [Hash] + def self.firmlink_map + if !@firmlink_map + return @firmlink_map = {} if !File.exist?(FIRMLINK_DEFS) + begin + @firmlink_map = Hash[ + File.readlines(FIRMLINK_DEFS).map { |d| + d.strip.split(/\s+/, 2) + } + ] + rescue => err + @@logger.warn("Failed to parse firmlink definitions: #{err}") + @firmlink_map = {} + end + end + @firmlink_map + end + + # @private + # Reset the cached values for capability. This is not considered a public + # API and should only be used for testing. + def self.reset! + instance_variables.each(&method(:remove_instance_variable)) + end + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/hosts/darwin/plugin.rb vagrant-2.2.6+dfsg/plugins/hosts/darwin/plugin.rb --- vagrant-2.2.3+dfsg/plugins/hosts/darwin/plugin.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/hosts/darwin/plugin.rb 2019-10-14 16:38:13.000000000 +0000 @@ -16,6 +16,11 @@ Cap::ProviderInstallVirtualBox end + host_capability("darwin", "resolve_host_path") do + require_relative "cap/path" + Cap::Path + end + host_capability("darwin", "rdp_client") do require_relative "cap/rdp" Cap::RDP diff -Nru vagrant-2.2.3+dfsg/plugins/hosts/freebsd/cap/nfs.rb vagrant-2.2.6+dfsg/plugins/hosts/freebsd/cap/nfs.rb --- vagrant-2.2.3+dfsg/plugins/hosts/freebsd/cap/nfs.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/hosts/freebsd/cap/nfs.rb 2019-10-14 16:38:13.000000000 +0000 @@ -19,7 +19,7 @@ end def self.nfs_exports_template(environment) - "nfs/exports_freebsd" + "nfs/exports_bsd" end def self.nfs_restart_command(environment) diff -Nru vagrant-2.2.3+dfsg/plugins/hosts/suse/cap/nfs.rb vagrant-2.2.6+dfsg/plugins/hosts/suse/cap/nfs.rb --- vagrant-2.2.3+dfsg/plugins/hosts/suse/cap/nfs.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/hosts/suse/cap/nfs.rb 2019-10-14 16:38:13.000000000 +0000 @@ -7,11 +7,11 @@ end def self.nfs_check_command(env) - "/sbin/service nfsserver status" + "systemctl status --no-pager nfs-server" end def self.nfs_start_command(env) - "/sbin/service nfsserver start" + "systemctl start --no-pager nfs-server" end end end diff -Nru vagrant-2.2.3+dfsg/plugins/hosts/void/cap/nfs.rb vagrant-2.2.6+dfsg/plugins/hosts/void/cap/nfs.rb --- vagrant-2.2.3+dfsg/plugins/hosts/void/cap/nfs.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/hosts/void/cap/nfs.rb 2019-10-14 16:38:13.000000000 +0000 @@ -15,7 +15,7 @@ end def self.nfs_installed(env) - result = Vagrant::Util::Subprocess.execute("/usr/bin/xbps-query nfs-utils") + result = Vagrant::Util::Subprocess.execute("/usr/bin/xbps-query", "nfs-utils") result.exit_code == 0 end end diff -Nru vagrant-2.2.3+dfsg/plugins/kernel_v2/config/trigger.rb vagrant-2.2.6+dfsg/plugins/kernel_v2/config/trigger.rb --- vagrant-2.2.3+dfsg/plugins/kernel_v2/config/trigger.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/kernel_v2/config/trigger.rb 2019-10-14 16:38:13.000000000 +0000 @@ -44,18 +44,22 @@ command.flatten! blk = block - if !block_given? && command.last.is_a?(Hash) - # We were given a hash rather than a block, - # so the last element should be the "config block" - # and the rest are commands for the trigger - blk = command.pop + if command.last.is_a?(Hash) + if block_given? + extra_cfg = command.pop + else + # We were given a hash rather than a block, + # so the last element should be the "config block" + # and the rest are commands for the trigger + blk = command.pop + end elsif !block_given? raise Vagrant::Errors::TriggersNoBlockGiven, command: command end command.each do |cmd| - trigger = create_trigger(cmd, blk) + trigger = create_trigger(cmd, blk, extra_cfg) @_before_triggers << trigger end end @@ -69,18 +73,22 @@ command.flatten! blk = block - if !block_given? && command.last.is_a?(Hash) - # We were given a hash rather than a block, - # so the last element should be the "config block" - # and the rest are commands for the trigger - blk = command.pop + if command.last.is_a?(Hash) + if block_given? + extra_cfg = command.pop + else + # We were given a hash rather than a block, + # so the last element should be the "config block" + # and the rest are commands for the trigger + blk = command.pop + end elsif !block_given? raise Vagrant::Errors::TriggersNoBlockGiven, command: command end command.each do |cmd| - trigger = create_trigger(cmd, blk) + trigger = create_trigger(cmd, blk, extra_cfg) @_after_triggers << trigger end end @@ -95,13 +103,15 @@ # # @param [Symbol] command Vagrant command to create trigger on # @param [Block] block The defined config block + # @param [Hash] extra_cfg Extra configurations for a block defined trigger (Optional) # @return [VagrantConfigTrigger] - def create_trigger(command, block) + def create_trigger(command, block, extra_cfg=nil) trigger = VagrantConfigTrigger.new(command) if block.is_a?(Hash) trigger.set_options(block) else block.call(trigger, VagrantConfigTrigger) + trigger.set_options(extra_cfg) if extra_cfg end return trigger end diff -Nru vagrant-2.2.3+dfsg/plugins/kernel_v2/config/vagrant.rb vagrant-2.2.6+dfsg/plugins/kernel_v2/config/vagrant.rb --- vagrant-2.2.3+dfsg/plugins/kernel_v2/config/vagrant.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/kernel_v2/config/vagrant.rb 2019-10-14 16:38:13.000000000 +0000 @@ -7,7 +7,8 @@ attr_accessor :sensitive attr_accessor :plugins - VALID_PLUGIN_KEYS = [:sources, :version, :entry_point].freeze + VALID_PLUGIN_KEYS = ["sources", "version", "entry_point"].map(&:freeze).freeze + INVALID_PLUGIN_FORMAT = :invalid_plugin_format def initialize @host = UNSET_VALUE @@ -43,13 +44,21 @@ errors << I18n.t("vagrant.config.root.sensitive_bad_type") end - @plugins.each do |plugin_name, plugin_info| - invalid_keys = plugin_info.keys - VALID_PLUGIN_KEYS - if !invalid_keys.empty? - errors << I18n.t("vagrant.config.root.plugins_bad_key", - plugin_name: plugin_name, - plugin_key: invalid_keys.join(", ") - ) + if @plugins == INVALID_PLUGIN_FORMAT + errors << I18n.t("vagrant.config.root.plugins_invalid_format") + else + @plugins.each do |plugin_name, plugin_info| + if plugin_info.is_a?(Hash) + invalid_keys = plugin_info.keys - VALID_PLUGIN_KEYS + if !invalid_keys.empty? + errors << I18n.t("vagrant.config.root.plugins_bad_key", + plugin_name: plugin_name, + plugin_key: invalid_keys.join(", ") + ) + end + else + errors << I18n.t("vagrant.config.root.plugins_invalid_format") + end end end @@ -61,18 +70,26 @@ end def format_plugins(val) - result = case val - when String - {val => {}} - when Array - Hash[val.map{|item| [item.to_s, {}]}] - else - val - end - result.keys.each do |key| - result[key] = Hash[result[key].map{|k,v| [k.to_sym, v]}] + case val + when String + {val => Vagrant::Util::HashWithIndifferentAccess.new} + when Array + val.inject(Vagrant::Util::HashWithIndifferentAccess.new) { |memo, item| + memo.merge(format_plugins(item)) + } + when Hash + Vagrant::Util::HashWithIndifferentAccess.new.tap { |h| + val.each_pair { |k, v| + if v.is_a?(Hash) + h[k] = Vagrant::Util::HashWithIndifferentAccess.new(v) + else + h[k] = v + end + } + } + else + INVALID_PLUGIN_FORMAT end - result end end end diff -Nru vagrant-2.2.3+dfsg/plugins/kernel_v2/config/vm_provisioner.rb vagrant-2.2.6+dfsg/plugins/kernel_v2/config/vm_provisioner.rb --- vagrant-2.2.3+dfsg/plugins/kernel_v2/config/vm_provisioner.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/kernel_v2/config/vm_provisioner.rb 2019-10-14 16:38:13.000000000 +0000 @@ -3,7 +3,10 @@ module VagrantPlugins module Kernel_V2 # Represents a single configured provisioner for a VM. - class VagrantConfigProvisioner + class VagrantConfigProvisioner < Vagrant.plugin("2", :config) + # Defaults + VALID_BEFORE_AFTER_TYPES = [:each, :all].freeze + # Unique name for this provisioner # # @return [String] @@ -29,7 +32,7 @@ # @return [Object] attr_accessor :config - # When to run this provisioner. Either "once" or "always" + # When to run this provisioner. Either "once", "always", or "never" # # @return [String] attr_accessor :run @@ -40,7 +43,17 @@ # @return [Boolean] attr_accessor :preserve_order - def initialize(name, type) + # The name of a provisioner to run before it has started + # + # @return [String, Symbol] + attr_accessor :before + + # The name of a provisioner to run after it is finished + # + # @return [String, Symbol] + attr_accessor :after + + def initialize(name, type, **options) @logger = Log4r::Logger.new("vagrant::config::vm::provisioner") @logger.debug("Provisioner defined: #{name}") @@ -51,6 +64,8 @@ @preserve_order = false @run = nil @type = type + @before = options[:before] + @after = options[:after] # Attempt to find the provisioner... if !Vagrant.plugin("2").manager.provisioners[type] @@ -90,6 +105,75 @@ @config.finalize! end + # Validates the before/after options + # + # @param [Vagrant::Machine] machine - machine to validate against + # @param [Array] provisioners - Array of defined provisioners for the guest machine + # @return [Array] array of strings of error messages from config option validation + def validate(machine, provisioners) + errors = _detected_errors + + provisioner_names = provisioners.map { |i| i.name.to_s if i.name != name }.compact + + if @before && @after + errors << I18n.t("vagrant.provisioners.base.both_before_after_set") + end + + if @before + if !VALID_BEFORE_AFTER_TYPES.include?(@before) + if @before.is_a?(Symbol) && !VALID_BEFORE_AFTER_TYPES.include?(@before) + errors << I18n.t("vagrant.provisioners.base.invalid_alias_value", opt: "before", alias: VALID_BEFORE_AFTER_TYPES.join(", ")) + elsif !@before.is_a?(String) && !VALID_BEFORE_AFTER_TYPES.include?(@before) + errors << I18n.t("vagrant.provisioners.base.wrong_type", opt: "before") + end + + if !provisioner_names.include?(@before) + errors << I18n.t("vagrant.provisioners.base.missing_provisioner_name", + name: @before, + machine_name: machine.name, + action: "before", + provisioner_name: @name) + end + + dep_prov = provisioners.find_all { |i| i.name.to_s == @before && (i.before || i.after) } + + if !dep_prov.empty? + errors << I18n.t("vagrant.provisioners.base.dependency_provisioner_dependency", + name: @name, + dep_name: dep_prov.first.name.to_s) + end + end + end + + if @after + if !VALID_BEFORE_AFTER_TYPES.include?(@after) + if @after.is_a?(Symbol) + errors << I18n.t("vagrant.provisioners.base.invalid_alias_value", opt: "after", alias: VALID_BEFORE_AFTER_TYPES.join(", ")) + elsif !@after.is_a?(String) + errors << I18n.t("vagrant.provisioners.base.wrong_type", opt: "after") + end + + if !provisioner_names.include?(@after) + errors << I18n.t("vagrant.provisioners.base.missing_provisioner_name", + name: @after, + machine_name: machine.name, + action: "after", + provisioner_name: @name) + end + + dep_prov = provisioners.find_all { |i| i.name.to_s == @after && (i.before || i.after) } + + if !dep_prov.empty? + errors << I18n.t("vagrant.provisioners.base.dependency_provisioner_dependency", + name: @name, + dep_name: dep_prov.first.name.to_s) + end + end + end + + {"provisioner" => errors} + end + # Returns whether the provisioner used was invalid or not. A provisioner # is invalid if it can't be found. # diff -Nru vagrant-2.2.3+dfsg/plugins/kernel_v2/config/vm.rb vagrant-2.2.6+dfsg/plugins/kernel_v2/config/vm.rb --- vagrant-2.2.3+dfsg/plugins/kernel_v2/config/vm.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/kernel_v2/config/vm.rb 2019-10-14 16:38:13.000000000 +0000 @@ -7,6 +7,7 @@ require "vagrant/config/v2/util" require "vagrant/util/platform" require "vagrant/util/presence" +require "vagrant/util/experimental" require File.expand_path("../vm_provisioner", __FILE__) require File.expand_path("../vm_subvm", __FILE__) @@ -20,6 +21,7 @@ attr_accessor :allowed_synced_folder_types attr_accessor :base_mac + attr_accessor :base_address attr_accessor :boot_timeout attr_accessor :box attr_accessor :ignore_box_vagrantfile @@ -50,6 +52,7 @@ @allowed_synced_folder_types = UNSET_VALUE @base_mac = UNSET_VALUE + @base_address = UNSET_VALUE @boot_timeout = UNSET_VALUE @box = UNSET_VALUE @ignore_box_vagrantfile = UNSET_VALUE @@ -329,7 +332,19 @@ end if !prov - prov = VagrantConfigProvisioner.new(name, type.to_sym) + if options.key?(:before) + before = options.delete(:before) + end + if options.key?(:after) + after = options.delete(:after) + end + + if Vagrant::Util::Experimental.feature_enabled?("dependency_provisioners") + opts = {before: before, after: after} + prov = VagrantConfigProvisioner.new(name, type.to_sym, opts) + else + prov = VagrantConfigProvisioner.new(name, type.to_sym) + end @provisioners << prov end @@ -377,6 +392,7 @@ # Defaults @allowed_synced_folder_types = nil if @allowed_synced_folder_types == UNSET_VALUE @base_mac = nil if @base_mac == UNSET_VALUE + @base_address = nil if @base_address == UNSET_VALUE @boot_timeout = 300 if @boot_timeout == UNSET_VALUE @box = nil if @box == UNSET_VALUE @ignore_box_vagrantfile = false if @ignore_box_vagrantfile == UNSET_VALUE @@ -493,7 +509,7 @@ line = "(unknown)" if e.backtrace && e.backtrace[0] - line = e.backtrace[0].split(":")[1] + line = e.backtrace.first.slice(0, e.backtrace.first.rindex(':')).rpartition(':').last end raise Vagrant::Errors::VagrantfileLoadError, @@ -757,6 +773,11 @@ next end + provisioner_errors = vm_provisioner.validate(machine, @provisioners) + if provisioner_errors + errors = Vagrant::Config::V2::Util.merge_errors(errors, provisioner_errors) + end + if vm_provisioner.config provisioner_errors = vm_provisioner.config.validate(machine) if provisioner_errors diff -Nru vagrant-2.2.3+dfsg/plugins/kernel_v2/config/vm_trigger.rb vagrant-2.2.6+dfsg/plugins/kernel_v2/config/vm_trigger.rb --- vagrant-2.2.3+dfsg/plugins/kernel_v2/config/vm_trigger.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/kernel_v2/config/vm_trigger.rb 2019-10-14 16:38:13.000000000 +0000 @@ -9,6 +9,7 @@ # Defaults DEFAULT_ON_ERROR = :halt DEFAULT_EXIT_CODE = 0 + VALID_TRIGGER_TYPES = [:command, :action, :hook].freeze #------------------------------------------------------------------- # Config class for a given Trigger @@ -89,6 +90,12 @@ # @return [Proc] attr_accessor :ruby + # The type of trigger, which defines where it will fire. If not defined, + # the option will default to `:action` + # + # @return [Symbol] + attr_accessor :type + def initialize(command) @logger = Log4r::Logger.new("vagrant::config::vm::trigger::config") @@ -103,13 +110,14 @@ @exit_codes = UNSET_VALUE @abort = UNSET_VALUE @ruby = UNSET_VALUE + @type = UNSET_VALUE # Internal options @id = SecureRandom.uuid @command = command.to_sym @ruby_block = UNSET_VALUE - @logger.debug("Trigger defined for command: #{command}") + @logger.debug("Trigger defined for: #{command}") end # Config option `ruby` for a trigger which reads in a ruby block and sets @@ -135,6 +143,7 @@ @only_on = nil if @only_on == UNSET_VALUE @exit_codes = DEFAULT_EXIT_CODE if @exit_codes == UNSET_VALUE @abort = nil if @abort == UNSET_VALUE + @type = :action if @type == UNSET_VALUE @ruby_block = nil if @ruby_block == UNSET_VALUE @ruby = nil if @ruby == UNSET_VALUE @@ -187,20 +196,33 @@ if @abort == true @abort = 1 end + + if @type + @type = @type.to_sym + end end # @return [Array] array of strings of error messages from config option validation def validate(machine) errors = _detected_errors - commands = [] - Vagrant.plugin("2").manager.commands.each do |key,data| - commands.push(key) + if @type && !VALID_TRIGGER_TYPES.include?(@type) + errors << I18n.t("vagrant.config.triggers.bad_trigger_type", + type: @type, + trigger: @command, + types: VALID_TRIGGER_TYPES.join(', ')) end - if !commands.include?(@command) && @command != :all - machine.ui.warn(I18n.t("vagrant.config.triggers.bad_command_warning", - cmd: @command)) + if @type == :command || !@type + commands = [] + Vagrant.plugin("2").manager.commands.each do |key,data| + commands.push(key) + end + + if !commands.include?(@command) && @command != :all + machine.ui.warn(I18n.t("vagrant.config.triggers.bad_command_warning", + cmd: @command)) + end end if @run diff -Nru vagrant-2.2.3+dfsg/plugins/providers/docker/action/build.rb vagrant-2.2.6+dfsg/plugins/providers/docker/action/build.rb --- vagrant-2.2.3+dfsg/plugins/providers/docker/action/build.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/docker/action/build.rb 2019-10-14 16:38:13.000000000 +0000 @@ -19,7 +19,7 @@ build_dir ||= machine.provider_config.build_dir git_repo = env[:git_repo] git_repo ||= machine.provider_config.git_repo - + # If we're not building a container, then just skip this step return @app.call(env) if (!build_dir && !git_repo) diff -Nru vagrant-2.2.3+dfsg/plugins/providers/docker/action/compare_synced_folders.rb vagrant-2.2.6+dfsg/plugins/providers/docker/action/compare_synced_folders.rb --- vagrant-2.2.3+dfsg/plugins/providers/docker/action/compare_synced_folders.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/docker/action/compare_synced_folders.rb 2019-10-14 16:38:13.000000000 +0000 @@ -32,14 +32,18 @@ fs.each do |_, data| invalid = false old = existing.delete(data[:guestpath]) - invalid = true if !old + if !old + invalid = true + else + old = File.expand_path(old) + end if !invalid && old - invalid = true if old != data[:hostpath] + invalid = true if old != File.expand_path(data[:hostpath]) end if invalid - invalids[data[:guestpath]] = data[:hostpath] + invalids[File.expand_path(data[:guestpath])] = File.expand_path(data[:hostpath]) end end end diff -Nru vagrant-2.2.3+dfsg/plugins/providers/docker/action/connect_networks.rb vagrant-2.2.6+dfsg/plugins/providers/docker/action/connect_networks.rb --- vagrant-2.2.3+dfsg/plugins/providers/docker/action/connect_networks.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/docker/action/connect_networks.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,77 @@ +require 'ipaddr' +require 'log4r' + +require 'vagrant/util/scoped_hash_override' + +module VagrantPlugins + module DockerProvider + module Action + class ConnectNetworks + + include Vagrant::Util::ScopedHashOverride + + def initialize(app, env) + @app = app + @logger = Log4r::Logger.new('vagrant::plugins::docker::connectnetworks') + end + + # Generate CLI arguments for creating the docker network. + # + # @param [Hash] options Options from the network config + # @returns[Array Network create arguments + def generate_connect_cli_arguments(options) + options.map do |key, value| + # If value is false, option is not set + next if value.to_s == "false" + # If value is true, consider feature flag with no value + opt = value.to_s == "true" ? [] : [value] + opt.unshift("--#{key.to_s.tr("_", "-")}") + end.flatten.compact + end + + # Execute the action + def call(env) + # If we are using a host VM, then don't worry about it + machine = env[:machine] + if machine.provider.host_vm? + @logger.debug("Not setting up networks because docker host_vm is in use") + return @app.call(env) + end + + env[:ui].info(I18n.t("docker_provider.network_connect")) + + connections = env[:docker_connects] || {} + + machine.config.vm.networks.each_with_index do |args, idx| + type, options = args + next if type != :private_network && type != :public_network + + network_options = scoped_hash_override(options, :docker_connect) + network_options.delete_if{|k,_| options.key?(k)} + network_name = connections[idx] + + if !network_name + raise Errors::NetworkNameMissing, + index: idx, + container: machine.name + end + + @logger.debug("Connecting network #{network_name} to container guest #{machine.name}") + if options[:ip] && options[:type] != "dhcp" + if IPAddr.new(options[:ip]).ipv4? + network_options[:ip] = options[:ip] + else + network_options[:ip6] = options[:ip] + end + end + network_options[:alias] = options[:alias] if options[:alias] + connect_opts = generate_connect_cli_arguments(network_options) + machine.provider.driver.connect_network(network_name, machine.id, connect_opts) + end + + @app.call(env) + end + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/providers/docker/action/destroy_network.rb vagrant-2.2.6+dfsg/plugins/providers/docker/action/destroy_network.rb --- vagrant-2.2.3+dfsg/plugins/providers/docker/action/destroy_network.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/docker/action/destroy_network.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,50 @@ +require 'log4r' + +module VagrantPlugins + module DockerProvider + module Action + class DestroyNetwork + + @@lock = Mutex.new + + def initialize(app, env) + @app = app + @logger = Log4r::Logger.new('vagrant::plugins::docker::network') + end + + def call(env) + # If we are using a host VM, then don't worry about it + machine = env[:machine] + if machine.provider.host_vm? + @logger.debug("Not setting up networks because docker host_vm is in use") + return @app.call(env) + end + + @@lock.synchronize do + machine.env.lock("docker-network-destroy", retry: true) do + machine.config.vm.networks.each do |type, options| + next if type != :private_network && type != :public_network + + vagrant_networks = machine.provider.driver.list_network_names.find_all do |n| + n.start_with?("vagrant_network") + end + + vagrant_networks.each do |network_name| + if machine.provider.driver.existing_named_network?(network_name) && + !machine.provider.driver.network_used?(network_name) + env[:ui].info(I18n.t("docker_provider.network_destroy", network_name: network_name)) + machine.provider.driver.rm_network(network_name) + else + @logger.debug("Network #{network_name} not found or in use") + end + end + end + end + end + + @app.call(env) + end + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/providers/docker/action/login.rb vagrant-2.2.6+dfsg/plugins/providers/docker/action/login.rb --- vagrant-2.2.3+dfsg/plugins/providers/docker/action/login.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/docker/action/login.rb 2019-10-14 16:38:13.000000000 +0000 @@ -9,6 +9,21 @@ @logger = Log4r::Logger.new("vagrant::docker::login") end + def login(env, config, driver) + # Login! + env[:ui].output(I18n.t("docker_provider.logging_in")) + driver.login( + config.email, config.username, + config.password, config.auth_server) + + # Continue, so that the auth is protected + # from meddling. + @app.call(env) + + # Log out + driver.logout(config.auth_server) + end + def call(env) config = env[:machine].provider_config driver = env[:machine].provider.driver @@ -16,21 +31,15 @@ # If we don't have a password set, don't auth return @app.call(env) if config.password == "" - # Grab a host VM lock to do the login so that we only login - # once per container for the rest of this process. - env[:machine].provider.host_vm_lock do - # Login! - env[:ui].output(I18n.t("docker_provider.logging_in")) - driver.login( - config.email, config.username, - config.password, config.auth_server) - - # Continue, within the lock, so that the auth is protected - # from meddling. - @app.call(env) - - # Log out - driver.logout(config.auth_server) + if !env[:machine].provider.host_vm? + # no host vm in use, using docker directly + login(env, config, driver) + else + # Grab a host VM lock to do the login so that we only login + # once per container for the rest of this process. + env[:machine].provider.host_vm_lock do + login(env, config, driver) + end end end end diff -Nru vagrant-2.2.3+dfsg/plugins/providers/docker/action/prepare_networks.rb vagrant-2.2.6+dfsg/plugins/providers/docker/action/prepare_networks.rb --- vagrant-2.2.3+dfsg/plugins/providers/docker/action/prepare_networks.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/docker/action/prepare_networks.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,369 @@ +require 'ipaddr' +require 'log4r' + +require 'vagrant/util/scoped_hash_override' + +module VagrantPlugins + module DockerProvider + module Action + class PrepareNetworks + + include Vagrant::Util::ScopedHashOverride + + @@lock = Mutex.new + + def initialize(app, env) + @app = app + @logger = Log4r::Logger.new('vagrant::plugins::docker::preparenetworks') + end + + # Generate CLI arguments for creating the docker network. + # + # @param [Hash] options Options from the network config + # @returns[Array] Network create arguments + def generate_create_cli_arguments(options) + options.map do |key, value| + # If value is false, option is not set + next if value.to_s == "false" + # If value is true, consider feature flag with no value + opt = value.to_s == "true" ? [] : [value] + opt.unshift("--#{key.to_s.tr("_", "-")}") + end.flatten.compact + end + + # @return [Array] interface list + def list_interfaces + Socket.getifaddrs.find_all do |i| + !i.addr.nil? && i.addr.ip? && !i.addr.ipv4_loopback? && + !i.addr.ipv6_loopback? && !i.addr.ipv6_linklocal? + end + end + + # Validates that a network name exists. If it does not + # exist, an exception is raised. + # + # @param [String] network_name Name of existing network + # @param [Hash] env Local call env + # @return [Boolean] + def validate_network_name!(network_name, env) + if !env[:machine].provider.driver.existing_named_network?(network_name) + raise Errors::NetworkNameUndefined, + network_name: network_name + end + true + end + + # Validates that the provided options are compatible with a + # pre-existing network. Raises exceptions on invalid configurations + # + # @param [String] network_name Name of the network + # @param [Hash] root_options Root networking options + # @param [Hash] network_options Docker scoped networking options + # @param [Driver] driver Docker driver + # @return [Boolean] + def validate_network_configuration!(network_name, root_options, network_options, driver) + if root_options[:ip] && + driver.network_containing_address(root_options[:ip]) != network_name + raise Errors::NetworkAddressInvalid, + address: root_options[:ip], + network_name: network_name + end + if network_options[:subnet] && + driver.network_containing_address(network_options[:subnet]) != network_name + raise Errors::NetworkSubnetInvalid, + subnet: network_options[:subnet], + network_name: network_name + end + true + end + + # Generate configuration for private network + # + # @param [Hash] root_options Root networking options + # @param [Hash] net_options Docker scoped networking options + # @param [Hash] env Local call env + # @return [String, Hash] Network name and updated network_options + def process_private_network(root_options, network_options, env) + if root_options[:name] && validate_network_name!(root_options[:name], env) + network_name = root_options[:name] + end + + if root_options[:type].to_s == "dhcp" + if !root_options[:ip] && !root_options[:subnet] + network_name = "vagrant_network" if !network_name + return [network_name, network_options] + end + if root_options[:subnet] + addr = IPAddr.new(root_options[:subnet]) + root_options[:netmask] = addr.prefix + end + end + + if root_options[:ip] + addr = IPAddr.new(root_options[:ip]) + elsif addr.nil? + raise Errors::NetworkIPAddressRequired + end + + # If address is ipv6, enable ipv6 support + network_options[:ipv6] = addr.ipv6? + + # If no mask is provided, attempt to locate any existing + # network which contains the assigned IP address + if !root_options[:netmask] && !network_name + network_name = env[:machine].provider.driver. + network_containing_address(root_options[:ip]) + # When no existing network is found, we are creating + # a new network. Since no mask was provided, default + # to /24 for ipv4 and /64 for ipv6 + if !network_name + root_options[:netmask] = addr.ipv4? ? 24 : 64 + end + end + + # With no network name, process options to find or determine + # name for new network + if !network_name + if !root_options[:subnet] + # Only generate a subnet if not given one + subnet = IPAddr.new("#{addr}/#{root_options[:netmask]}") + network = "#{subnet}/#{root_options[:netmask]}" + else + network = root_options[:subnet] + end + + network_options[:subnet] = network + existing_network = env[:machine].provider.driver. + network_defined?(network) + + if !existing_network + network_name = "vagrant_network_#{network}" + else + if !existing_network.to_s.start_with?("vagrant_network") + env[:ui].warn(I18n.t("docker_provider.subnet_exists", + network_name: existing_network, + subnet: network)) + end + network_name = existing_network + end + end + + [network_name, network_options] + end + + # Generate configuration for public network + # + # TODO: When the Vagrant installer upgrades to Ruby 2.5.x, + # remove all instances of the roundabout way of determining a prefix + # and instead just use the built-in `.prefix` method + # + # @param [Hash] root_options Root networking options + # @param [Hash] net_options Docker scoped networking options + # @param [Hash] env Local call env + # @return [String, Hash] Network name and updated network_options + def process_public_network(root_options, net_options, env) + if root_options[:name] && validate_network_name!(root_options[:name], env) + network_name = root_options[:name] + end + if !network_name + valid_interfaces = list_interfaces + if valid_interfaces.empty? + raise Errors::NetworkNoInterfaces + elsif valid_interfaces.size == 1 + bridge_interface = valid_interfaces.first + elsif i = valid_interfaces.detect{|i| Array(root_options[:bridge]).include?(i.name) } + bridge_interface = i + end + if !bridge_interface + env[:ui].info(I18n.t("vagrant.actions.vm.bridged_networking.available"), + prefix: false) + valid_interfaces.each_with_index do |int, i| + env[:ui].info("#{i + 1}) #{int.name}", prefix: false) + end + env[:ui].info(I18n.t( + "vagrant.actions.vm.bridged_networking.choice_help") + "\n", + prefix: false + ) + end + while !bridge_interface + choice = env[:ui].ask( + I18n.t("vagrant.actions.vm.bridged_networking.select_interface") + " ", + prefix: false) + bridge_interface = valid_interfaces[choice.to_i - 1] + end + base_opts = Vagrant::Util::HashWithIndifferentAccess.new + base_opts[:opt] = "parent=#{bridge_interface.name}" + subnet = IPAddr.new(bridge_interface.addr.ip_address << + "/" << bridge_interface.netmask.ip_unpack.first) + netmask = bridge_interface.netmask.ip_unpack.first + prefix = IPAddr.new("255.255.255.255/#{netmask}").to_i.to_s(2).count("1") + base_opts[:subnet] = "#{subnet}/#{prefix}" + subnet_addr = IPAddr.new(base_opts[:subnet]) + base_opts[:driver] = "macvlan" + base_opts[:gateway] = subnet_addr.succ.to_s + base_opts[:ipv6] = subnet_addr.ipv6? + network_options = base_opts.merge(net_options) + + # Check if network already exists for this subnet + network_name = env[:machine].provider.driver. + network_containing_address(network_options[:gateway]) + if !network_name + network_name = "vagrant_network_public_#{bridge_interface.name}" + end + + # If the network doesn't already exist, gather available address range + # within subnet which docker can provide addressing + if !env[:machine].provider.driver.existing_named_network?(network_name) + if !net_options[:gateway] + network_options[:gateway] = request_public_gateway( + network_options, bridge_interface.name, env) + end + network_options[:ip_range] = request_public_iprange( + network_options, bridge_interface, env) + end + end + [network_name, network_options] + end + + # Request the gateway address for the public network + # + # @param [Hash] network_options Docker scoped networking options + # @param [String] interface The bridge interface used + # @param [Hash] env Local call env + # @return [String] Gateway address + def request_public_gateway(network_options, interface, env) + subnet = IPAddr.new(network_options[:subnet]) + gateway = nil + while !gateway + gateway = env[:ui].ask(I18n.t( + "docker_provider.network_bridge_gateway_request", + interface: interface, + default_gateway: network_options[:gateway]) + " ", + prefix: false + ).strip + if gateway.empty? + gateway = network_options[:gateway] + end + begin + gateway = IPAddr.new(gateway) + if !subnet.include?(gateway) + env[:ui].warn(I18n.t("docker_provider.network_bridge_gateway_outofbounds", + gateway: gateway, + subnet: network_options[:subnet]) + "\n", prefix: false) + end + rescue IPAddr::InvalidAddressError + env[:ui].warn(I18n.t("docker_provider.network_bridge_gateway_invalid", + gateway: gateway) + "\n", prefix: false) + gateway = nil + end + end + gateway.to_s + end + + # Request the IP range allowed for use by docker when creating a new + # public network + # + # TODO: When the Vagrant installer upgrades to Ruby 2.5.x, + # remove all instances of the roundabout way of determining a prefix + # and instead just use the built-in `.prefix` method + # + # @param [Hash] network_options Docker scoped networking options + # @param [Socket::Ifaddr] interface The bridge interface used + # @param [Hash] env Local call env + # @return [String] Address range + def request_public_iprange(network_options, interface, env) + return network_options[:ip_range] if network_options[:ip_range] + subnet = IPAddr.new(network_options[:subnet]) + env[:ui].info(I18n.t( + "docker_provider.network_bridge_iprange_info") + "\n", + prefix: false + ) + range = nil + while !range + range = env[:ui].ask(I18n.t( + "docker_provider.network_bridge_iprange_request", + interface: interface.name, + default_range: network_options[:subnet]) + " ", + prefix: false + ).strip + if range.empty? + range = network_options[:subnet] + end + begin + range = IPAddr.new(range) + if !subnet.include?(range) + netmask = interface.netmask.ip_unpack.first + prefix = IPAddr.new("255.255.255.255/#{netmask}").to_i.to_s(2).count("1") + env[:ui].warn(I18n.t( + "docker_provider.network_bridge_iprange_outofbounds", + subnet: network_options[:subnet], + range: "#{range}/#{prefix}" + ) + "\n", prefix: false) + range = nil + end + rescue IPAddr::InvalidAddressError + env[:ui].warn(I18n.t( + "docker_provider.network_bridge_iprange_invalid", + range: range) + "\n", prefix: false) + range = nil + end + end + + netmask = interface.netmask.ip_unpack.first + prefix = IPAddr.new("255.255.255.255/#{netmask}").to_i.to_s(2).count("1") + "#{range}/#{prefix}" + end + + # Execute the action + def call(env) + # If we are using a host VM, then don't worry about it + machine = env[:machine] + if machine.provider.host_vm? + @logger.debug("Not setting up networks because docker host_vm is in use") + return @app.call(env) + end + + connections = {} + @@lock.synchronize do + machine.env.lock("docker-network-create", retry: true) do + env[:ui].info(I18n.t("docker_provider.network_create")) + machine.config.vm.networks.each_with_index do |net_info, net_idx| + type, options = net_info + network_options = scoped_hash_override(options, :docker_network) + network_options.delete_if{|k,_| options.key?(k)} + + case type + when :public_network + network_name, network_options = process_public_network( + options, network_options, env) + when :private_network + network_name, network_options = process_private_network( + options, network_options, env) + else + next # unsupported type so ignore + end + + if !network_name + raise Errors::NetworkInvalidOption, container: machine.name + end + + if !machine.provider.driver.existing_named_network?(network_name) + @logger.debug("Creating network #{network_name}") + cli_opts = generate_create_cli_arguments(network_options) + machine.provider.driver.create_network(network_name, cli_opts) + else + @logger.debug("Network #{network_name} already created") + validate_network_configuration!(network_name, options, network_options, machine.provider.driver) + end + connections[net_idx] = network_name + end + end + end + + env[:docker_connects] = connections + @app.call(env) + end + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/plugins/providers/docker/action.rb vagrant-2.2.6+dfsg/plugins/providers/docker/action.rb --- vagrant-2.2.3+dfsg/plugins/providers/docker/action.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/docker/action.rb 2019-10-14 16:38:13.000000000 +0000 @@ -161,6 +161,7 @@ b4.use action_halt b4.use HostMachineSyncFoldersDisable b4.use Destroy + b4.use DestroyNetwork b4.use DestroyBuildImage else b4.use Message, @@ -243,6 +244,7 @@ b2.use PrepareNFSValidIds b2.use SyncedFolderCleanup b2.use PrepareNFSSettings + b2.use PrepareNetworks b2.use Login b2.use Build @@ -265,6 +267,7 @@ end end + b2.use ConnectNetworks b2.use Start b2.use WaitForRunning @@ -292,9 +295,11 @@ action_root = Pathname.new(File.expand_path("../action", __FILE__)) autoload :Build, action_root.join("build") autoload :CompareSyncedFolders, action_root.join("compare_synced_folders") + autoload :ConnectNetworks, action_root.join("connect_networks") autoload :Create, action_root.join("create") autoload :Destroy, action_root.join("destroy") autoload :DestroyBuildImage, action_root.join("destroy_build_image") + autoload :DestroyNetwork, action_root.join("destroy_network") autoload :ForwardedPorts, action_root.join("forwarded_ports") autoload :HasSSH, action_root.join("has_ssh") autoload :HostMachine, action_root.join("host_machine") @@ -308,12 +313,13 @@ autoload :IsBuild, action_root.join("is_build") autoload :IsHostMachineCreated, action_root.join("is_host_machine_created") autoload :Login, action_root.join("login") - autoload :Pull, action_root.join("pull") - autoload :PrepareSSH, action_root.join("prepare_ssh") - autoload :Stop, action_root.join("stop") + autoload :PrepareNetworks, action_root.join("prepare_networks") autoload :PrepareNFSValidIds, action_root.join("prepare_nfs_valid_ids") autoload :PrepareNFSSettings, action_root.join("prepare_nfs_settings") + autoload :PrepareSSH, action_root.join("prepare_ssh") + autoload :Pull, action_root.join("pull") autoload :Start, action_root.join("start") + autoload :Stop, action_root.join("stop") autoload :WaitForRunning, action_root.join("wait_for_running") end end diff -Nru vagrant-2.2.3+dfsg/plugins/providers/docker/driver/compose.rb vagrant-2.2.6+dfsg/plugins/providers/docker/driver/compose.rb --- vagrant-2.2.3+dfsg/plugins/providers/docker/driver/compose.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/docker/driver/compose.rb 2019-10-14 16:38:13.000000000 +0000 @@ -34,6 +34,12 @@ @logger.debug("Data directory for composition file `#{@data_directory}`") end + # Updates the docker compose config file with the given arguments + # + # @param [String] dir - local directory or git repo URL + # @param [Hash] opts - valid key: extra_args + # @param [Block] block + # @return [Nil] def build(dir, **opts, &block) name = machine.name.to_s @logger.debug("Applying build for `#{name}` using `#{dir}` directory.") @@ -47,26 +53,26 @@ services[name]["build"]["dockerfile"] = opts[:extra_args][opts[:extra_args].index("--file") + 1] end # Extract any build args that can be found - case opts[:build_args] + case opts[:extra_args] when Array - if opts[:build_args].include?("--build-arg") + if opts[:extra_args].include?("--build-arg") idx = 0 - build_args = {} - while(idx < opts[:build_args].size) - arg_value = opts[:build_args][idx] + extra_args = {} + while(idx < opts[:extra_args].size) + arg_value = opts[:extra_args][idx] idx += 1 if arg_value.start_with?("--build-arg") if !arg_value.include?("=") - arg_value = opts[:build_args][idx] + arg_value = opts[:extra_args][idx] idx += 1 end key, val = arg_value.to_s.split("=", 2).to_s.split("=") - build_args[key] = val + extra_args[key] = val end end end when Hash - services[name]["build"]["args"] = opts[:build_args] + services[name]["build"]["args"] = opts[:extra_args] end end rescue => error @@ -102,7 +108,19 @@ # will be fixed someday and the gsub below can be removed. host.gsub!(/^[^A-Za-z]+/, "") end - host = @machine.env.cwd.join(host).to_s + # if host path is a volume key, don't expand it. + # if both exist (a path and a key) show warning and move on + # otherwise assume it's a realative path and expand the host path + compose_config = get_composition + if compose_config["volumes"] && compose_config["volumes"].keys.include?(host) + if File.directory?(@machine.env.cwd.join(host).to_s) + @machine.env.ui.warn(I18n.t("docker_provider.volume_path_not_expanded", + host: host)) + end + else + @logger.debug("Path expanding #{host} to current Vagrant working dir instead of docker-compose config file directory") + host = @machine.env.cwd.join(host).to_s + end "#{host}:#{guest}" end cmd = Array(params.fetch(:cmd)) diff -Nru vagrant-2.2.3+dfsg/plugins/providers/docker/driver.rb vagrant-2.2.6+dfsg/plugins/providers/docker/driver.rb --- vagrant-2.2.3+dfsg/plugins/providers/docker/driver.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/docker/driver.rb 2019-10-14 16:38:13.000000000 +0000 @@ -152,21 +152,25 @@ def rmi(id) execute('docker', 'rmi', id) return true - rescue Exception => e + rescue => e return false if e.to_s.include?("is using it") raise if !e.to_s.include?("No such image") end + # Inspect the provided container + # + # @param [String] cid ID or name of container + # @return [Hash] def inspect_container(cid) - # DISCUSS: Is there a chance that this json will change after the container - # has been brought up? - @data ||= JSON.parse(execute('docker', 'inspect', cid)).first + JSON.parse(execute('docker', 'inspect', cid)).first end + # @return [Array] list of all container IDs def all_containers execute('docker', 'ps', '-a', '-q', '--no-trunc').to_s.split end + # @return [String] IP address of the docker bridge def docker_bridge_ip output = execute('/sbin/ip', '-4', 'addr', 'show', 'scope', 'global', 'docker0') if output =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/ @@ -177,9 +181,149 @@ end end + # @param [String] network - name of network to connect conatiner to + # @param [String] cid - container id + # @param [Array] opts - An array of flags used for listing networks + def connect_network(network, cid, opts=nil) + command = ['docker', 'network', 'connect', network, cid].push(*opts) + output = execute(*command) + output + end + + # @param [String] network - name of network to create + # @param [Array] opts - An array of flags used for listing networks + def create_network(network, opts=nil) + command = ['docker', 'network', 'create', network].push(*opts) + output = execute(*command) + output + end + + # @param [String] network - name of network to disconnect container from + # @param [String] cid - container id + def disconnect_network(network, cid) + command = ['docker', 'network', 'disconnect', network, cid, "--force"] + output = execute(*command) + output + end + + # @param [Array] networks - list of networks to inspect + # @param [Array] opts - An array of flags used for listing networks + def inspect_network(network, opts=nil) + command = ['docker', 'network', 'inspect'] + Array(network) + command = command.push(*opts) + output = execute(*command) + begin + JSON.load(output) + rescue JSON::ParserError + @logger.warn("Failed to parse network inspection of network: #{network}") + @logger.debug("Failed network output content: `#{output.inspect}`") + nil + end + end + + # @param [String] opts - Flags used for listing networks + def list_network(*opts) + command = ['docker', 'network', 'ls', *opts] + output = execute(*command) + output + end + + # Will delete _all_ defined but unused networks in the docker engine. Even + # networks not created by Vagrant. + # + # @param [Array] opts - An array of flags used for listing networks + def prune_network(opts=nil) + command = ['docker', 'network', 'prune', '--force'].push(*opts) + output = execute(*command) + output + end + + # Delete network(s) + # + # @param [String] network - name of network to remove + def rm_network(*network) + command = ['docker', 'network', 'rm', *network] + output = execute(*command) + output + end + + # @param [Array] opts - An array of flags used for listing networks def execute(*cmd, **opts, &block) @executor.execute(*cmd, **opts, &block) end + + # ###################### + # Docker network helpers + # ###################### + + # Determines if a given network has been defined through vagrant with a given + # subnet string + # + # @param [String] subnet_string - Subnet to look for + # @return [String] network name - Name of network with requested subnet.`nil` if not found + def network_defined?(subnet_string) + all_networks = list_network_names + + network_info = inspect_network(all_networks) + network_info.each do |network| + config = network["IPAM"]["Config"] + if (config.size > 0 && + config.first["Subnet"] == subnet_string) + @logger.debug("Found existing network #{network["Name"]} already configured with #{subnet_string}") + return network["Name"] + end + end + return nil + end + + # Locate network which contains given address + # + # @param [String] address IP address + # @return [String] network name + def network_containing_address(address) + names = list_network_names + networks = inspect_network(names) + return if !networks + networks.each do |net| + next if !net["IPAM"] + config = net["IPAM"]["Config"] + next if !config || config.size < 1 + config.each do |opts| + subnet = IPAddr.new(opts["Subnet"]) + if subnet.include?(address) + return net["Name"] + end + end + end + nil + end + + # Looks to see if a docker network has already been defined + # with the given name + # + # @param [String] network_name - name of network to look for + # @return [Bool] + def existing_named_network?(network_name) + result = list_network_names + result.any?{|net_name| net_name == network_name} + end + + # @return [Array] list of all docker networks + def list_network_names + list_network("--format={{.Name}}").split("\n").map(&:strip) + end + + # Returns true or false if network is in use or not. + # Nil if Vagrant fails to receive proper JSON from `docker network inspect` + # + # @param [String] network - name of network to look for + # @return [Bool,nil] + def network_used?(network) + result = inspect_network(network) + return nil if !result + return result.first["Containers"].size > 0 + end + end end end diff -Nru vagrant-2.2.3+dfsg/plugins/providers/docker/errors.rb vagrant-2.2.6+dfsg/plugins/providers/docker/errors.rb --- vagrant-2.2.3+dfsg/plugins/providers/docker/errors.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/docker/errors.rb 2019-10-14 16:38:13.000000000 +0000 @@ -45,6 +45,34 @@ error_key(:docker_provider_nfs_without_privileged) end + class NetworkAddressInvalid < DockerError + error_key(:network_address_invalid) + end + + class NetworkIPAddressRequired < DockerError + error_key(:network_address_required) + end + + class NetworkSubnetInvalid < DockerError + error_key(:network_subnet_invalid) + end + + class NetworkInvalidOption < DockerError + error_key(:network_invalid_option) + end + + class NetworkNameMissing < DockerError + error_key(:network_name_missing) + end + + class NetworkNameUndefined < DockerError + error_key(:network_name_undefined) + end + + class NetworkNoInterfaces < DockerError + error_key(:network_no_interfaces) + end + class PackageNotSupported < DockerError error_key(:package_not_supported) end diff -Nru vagrant-2.2.3+dfsg/plugins/providers/docker/provider.rb vagrant-2.2.6+dfsg/plugins/providers/docker/provider.rb --- vagrant-2.2.3+dfsg/plugins/providers/docker/provider.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/docker/provider.rb 2019-10-14 16:38:13.000000000 +0000 @@ -11,6 +11,14 @@ class Provider < Vagrant.plugin("2", :provider) @@host_vm_mutex = Mutex.new + def self.usable?(raise_error=false) + Driver.new.execute("docker", "version") + true + rescue Vagrant::Errors::CommandUnavailable, Errors::ExecuteError + raise if raise_error + return false + end + def initialize(machine) @logger = Log4r::Logger.new("vagrant::provider::docker") @machine = machine diff -Nru vagrant-2.2.3+dfsg/plugins/providers/hyperv/scripts/utils/VagrantVM/VagrantVM.psm1 vagrant-2.2.6+dfsg/plugins/providers/hyperv/scripts/utils/VagrantVM/VagrantVM.psm1 --- vagrant-2.2.3+dfsg/plugins/providers/hyperv/scripts/utils/VagrantVM/VagrantVM.psm1 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/hyperv/scripts/utils/VagrantVM/VagrantVM.psm1 2019-10-14 16:38:13.000000000 +0000 @@ -355,6 +355,9 @@ ) $ManagementService = Get-WmiObject -Namespace 'root\virtualization\v2' -Class 'Msvm_VirtualSystemManagementService' + if($null -eq $ManagementService) { + throw 'The Hyper-V Virtual Machine Management Service (VMMS) is not running.' + } # Relative path names will fail when attempting to import a system # definition so always ensure we are using the full path to the diff -Nru vagrant-2.2.3+dfsg/plugins/providers/virtualbox/cap.rb vagrant-2.2.6+dfsg/plugins/providers/virtualbox/cap.rb --- vagrant-2.2.3+dfsg/plugins/providers/virtualbox/cap.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/virtualbox/cap.rb 2019-10-14 16:38:13.000000000 +0000 @@ -29,6 +29,7 @@ # # @return [Array] Snapshot Name def self.snapshot_list(machine) + return [] if machine.id.nil? machine.provider.driver.list_snapshots(machine.id) end end diff -Nru vagrant-2.2.3+dfsg/plugins/providers/virtualbox/driver/version_6_0.rb vagrant-2.2.6+dfsg/plugins/providers/virtualbox/driver/version_6_0.rb --- vagrant-2.2.3+dfsg/plugins/providers/virtualbox/driver/version_6_0.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/virtualbox/driver/version_6_0.rb 2019-10-14 16:38:13.000000000 +0000 @@ -46,7 +46,11 @@ @logger.warn("Failed to locate base path for disks. Using current working directory.") base_path = "." else - base_path = File.dirname(result[:settings_path]) + base_path = result[:settings_path] + if Vagrant::Util::Platform.windows? || Vagrant::Util::Platform.wsl? + base_path.gsub!('\\', '/') + end + base_path = File.dirname(base_path) end @logger.info("Base path for disk import: #{base_path}") @@ -61,14 +65,7 @@ disk_params << "--unit" disk_params << unit_num disk_params << "--disk" - if Vagrant::Util::Platform.windows? - # we use the block form of sub here to ensure that if the specified_name happens to end with a number (which is fairly likely) then - # we won't end up having the character sequence of a \ followed by a number be interpreted as a back reference. For example, if - # specified_name were "abc123", then "\\abc123\\".reverse would be "\\321cba\\", and the \3 would be treated as a back reference by the sub - disk_params << path.reverse.sub("\\#{suggested_name}\\".reverse) { "\\#{specified_name}\\".reverse }.reverse # Replace only last occurrence - else - disk_params << path.reverse.sub("/#{suggested_name}/".reverse, "/#{specified_name}/".reverse).reverse # Replace only last occurrence - end + disk_params << path.reverse.sub("/#{suggested_name}/".reverse, "/#{specified_name}/".reverse).reverse # Replace only last occurrence end execute("import", ovf , *name_params, *disk_params, retryable: true) do |type, data| diff -Nru vagrant-2.2.3+dfsg/plugins/providers/virtualbox/provider.rb vagrant-2.2.6+dfsg/plugins/providers/virtualbox/provider.rb --- vagrant-2.2.3+dfsg/plugins/providers/virtualbox/provider.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/providers/virtualbox/provider.rb 2019-10-14 16:38:13.000000000 +0000 @@ -8,9 +8,10 @@ def self.installed? Driver::Meta.new true - rescue Vagrant::Errors::VirtualBoxInvalidVersion - return false - rescue Vagrant::Errors::VirtualBoxNotDetected + rescue Vagrant::Errors::VirtualBoxInvalidVersion, + Vagrant::Errors::VirtualBoxNotDetected, + Vagrant::Errors::VirtualBoxKernelModuleNotLoaded, + Vagrant::Errors::VirtualBoxInstallIncomplete return false end @@ -19,10 +20,10 @@ # version and all that, which checks for VirtualBox being present Driver::Meta.new true - rescue Vagrant::Errors::VirtualBoxInvalidVersion - raise if raise_error - return false - rescue Vagrant::Errors::VirtualBoxNotDetected + rescue Vagrant::Errors::VirtualBoxInvalidVersion, + Vagrant::Errors::VirtualBoxNotDetected, + Vagrant::Errors::VirtualBoxKernelModuleNotLoaded, + Vagrant::Errors::VirtualBoxInstallIncomplete raise if raise_error return false end diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/ansible/cap/guest/debian/ansible_install.rb vagrant-2.2.6+dfsg/plugins/provisioners/ansible/cap/guest/debian/ansible_install.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/ansible/cap/guest/debian/ansible_install.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/ansible/cap/guest/debian/ansible_install.rb 2019-10-14 16:38:13.000000000 +0000 @@ -8,13 +8,13 @@ module AnsibleInstall - def self.ansible_install(machine, install_mode, ansible_version, pip_args) + def self.ansible_install(machine, install_mode, ansible_version, pip_args, pip_install_cmd="") case install_mode when :pip - pip_setup machine + pip_setup machine, pip_install_cmd Pip::pip_install machine, "ansible", ansible_version, pip_args, true when :pip_args_only - pip_setup machine + pip_setup machine, pip_install_cmd Pip::pip_install machine, "", "", pip_args, false else ansible_apt_install machine @@ -36,10 +36,10 @@ machine.communicate.sudo "apt-get install -y -qq ansible" end - def self.pip_setup(machine) + def self.pip_setup(machine, pip_install_cmd="") machine.communicate.sudo "apt-get update -y -qq" machine.communicate.sudo "apt-get install -y -qq build-essential curl git libssl-dev libffi-dev python-dev" - Pip::get_pip machine + Pip::get_pip machine, pip_install_cmd end end diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/ansible/cap/guest/fedora/ansible_install.rb vagrant-2.2.6+dfsg/plugins/provisioners/ansible/cap/guest/fedora/ansible_install.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/ansible/cap/guest/fedora/ansible_install.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/ansible/cap/guest/fedora/ansible_install.rb 2019-10-14 16:38:13.000000000 +0000 @@ -8,13 +8,13 @@ module Fedora module AnsibleInstall - def self.ansible_install(machine, install_mode, ansible_version, pip_args) + def self.ansible_install(machine, install_mode, ansible_version, pip_args, pip_install_cmd="") case install_mode when :pip - pip_setup machine + pip_setup machine, pip_install_cmd Pip::pip_install machine, "ansible", ansible_version, pip_args, true when :pip_args_only - pip_setup machine + pip_setup machine, pip_install_cmd Pip::pip_install machine, "", "", pip_args, false else rpm_package_manager = Facts::rpm_package_manager(machine) @@ -25,11 +25,11 @@ private - def self.pip_setup(machine) + def self.pip_setup(machine, pip_install_cmd="") rpm_package_manager = Facts::rpm_package_manager(machine) machine.communicate.sudo "#{rpm_package_manager} install -y curl gcc gmp-devel libffi-devel openssl-devel python-crypto python-devel python-dnf python-setuptools redhat-rpm-config" - Pip::get_pip machine + Pip::get_pip machine, pip_install_cmd end end diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/ansible/cap/guest/pip/pip.rb vagrant-2.2.6+dfsg/plugins/provisioners/ansible/cap/guest/pip/pip.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/ansible/cap/guest/pip/pip.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/ansible/cap/guest/pip/pip.rb 2019-10-14 16:38:13.000000000 +0000 @@ -5,6 +5,8 @@ module Guest module Pip + DEFAULT_PIP_INSTALL_CMD = "curl https://bootstrap.pypa.io/get-pip.py | sudo python".freeze + def self.pip_install(machine, package = "", version = "", pip_args = "", upgrade = true) upgrade_arg = "--upgrade" if upgrade version_arg = "" @@ -18,9 +20,19 @@ machine.communicate.sudo "pip install #{args_array.join(' ')}" end - def self.get_pip(machine) + def self.get_pip(machine, pip_install_cmd=DEFAULT_PIP_INSTALL_CMD) + # The objective here is to get pip either by default + # or by the argument passed in. The objective is not + # to circumvent the pip setup by passing in nothing. + # Thus, we stick with the default on an empty string. + # Typecast added in the check for safety. + + if pip_install_cmd.to_s.empty? + pip_install_cmd=DEFAULT_PIP_INSTALL_CMD + end + machine.ui.detail I18n.t("vagrant.provisioners.ansible.installing_pip") - machine.communicate.execute "curl https://bootstrap.pypa.io/get-pip.py | sudo python" + machine.communicate.execute pip_install_cmd end end diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/ansible/cap/guest/redhat/ansible_install.rb vagrant-2.2.6+dfsg/plugins/provisioners/ansible/cap/guest/redhat/ansible_install.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/ansible/cap/guest/redhat/ansible_install.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/ansible/cap/guest/redhat/ansible_install.rb 2019-10-14 16:38:13.000000000 +0000 @@ -8,13 +8,13 @@ module RedHat module AnsibleInstall - def self.ansible_install(machine, install_mode, ansible_version, pip_args) + def self.ansible_install(machine, install_mode, ansible_version, pip_args, pip_install_cmd="") case install_mode when :pip - pip_setup machine + pip_setup machine, pip_install_cmd Pip::pip_install machine, "ansible", ansible_version, pip_args, true when :pip_args_only - pip_setup machine + pip_setup machine, pip_install_cmd Pip::pip_install machine, "", "", pip_args, false else ansible_rpm_install machine @@ -33,11 +33,11 @@ machine.communicate.sudo "#{rpm_package_manager} -y --enablerepo=epel install ansible" end - def self.pip_setup(machine) + def self.pip_setup(machine, pip_install_cmd="") rpm_package_manager = Facts::rpm_package_manager(machine) machine.communicate.sudo("#{rpm_package_manager} -y install curl gcc libffi-devel openssl-devel python-crypto python-devel python-setuptools") - Pip::get_pip machine + Pip::get_pip machine, pip_install_cmd end end diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb vagrant-2.2.6+dfsg/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb 2019-10-14 16:38:13.000000000 +0000 @@ -7,9 +7,9 @@ module Ubuntu module AnsibleInstall - def self.ansible_install(machine, install_mode, ansible_version, pip_args) + def self.ansible_install(machine, install_mode, ansible_version, pip_args, pip_install_cmd="") if install_mode != :default - Debian::AnsibleInstall::ansible_install machine, install_mode, ansible_version, pip_args + Debian::AnsibleInstall::ansible_install machine, install_mode, ansible_version, pip_args, pip_install_cmd else ansible_apt_install machine end diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/ansible/config/guest.rb vagrant-2.2.6+dfsg/plugins/provisioners/ansible/config/guest.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/ansible/config/guest.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/ansible/config/guest.rb 2019-10-14 16:38:13.000000000 +0000 @@ -11,6 +11,7 @@ attr_accessor :install attr_accessor :install_mode attr_accessor :pip_args + attr_accessor :pip_install_cmd def initialize super @@ -18,6 +19,7 @@ @install = UNSET_VALUE @install_mode = UNSET_VALUE @pip_args = UNSET_VALUE + @pip_install_cmd = UNSET_VALUE @provisioning_path = UNSET_VALUE @tmp_path = UNSET_VALUE end @@ -28,6 +30,7 @@ @install = true if @install == UNSET_VALUE @install_mode = :default if @install_mode == UNSET_VALUE @pip_args = "" if @pip_args == UNSET_VALUE + @pip_install_cmd = "" if @pip_install_cmd == UNSET_VALUE @provisioning_path = "/vagrant" if provisioning_path == UNSET_VALUE @tmp_path = "/tmp/vagrant-ansible" if tmp_path == UNSET_VALUE end diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/ansible/provisioner/guest.rb vagrant-2.2.6+dfsg/plugins/provisioners/ansible/provisioner/guest.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/ansible/provisioner/guest.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/ansible/provisioner/guest.rb 2019-10-14 16:38:13.000000000 +0000 @@ -32,7 +32,10 @@ # # Current limitations: # - The installation of a specific Ansible version is only supported by - # the "pip" install_mode. + # the "pip" install_mode. Note that "pip" installation also takes place + # via a default command. If pip needs to be installed differently then + # the command can be overwritten by supplying "pip_install_cmd" in the + # config settings. # - There is no absolute guarantee that the automated installation will replace # a previous Ansible installation (although it works fine in many cases) # @@ -51,7 +54,7 @@ (config.version.to_s.to_sym == :latest || !@machine.guest.capability(:ansible_installed, config.version)) @machine.ui.detail I18n.t("vagrant.provisioners.ansible.installing") - @machine.guest.capability(:ansible_install, config.install_mode, config.version, config.pip_args) + @machine.guest.capability(:ansible_install, config.install_mode, config.version, config.pip_args, config.pip_install_cmd) end # This step will also fetch the Ansible version data into related instance variables diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/chef/cap/freebsd/chef_installed.rb vagrant-2.2.6+dfsg/plugins/provisioners/chef/cap/freebsd/chef_installed.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/chef/cap/freebsd/chef_installed.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/chef/cap/freebsd/chef_installed.rb 2019-10-14 16:38:13.000000000 +0000 @@ -10,7 +10,7 @@ command = "test -x #{knife}" if version != :latest - command << "&& #{knife} --version | grep 'Chef: #{version}'" + command << "&& #{knife} --version | grep '#{version}'" end machine.communicate.test(command, sudo: true) diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/chef/cap/freebsd/chef_install.rb vagrant-2.2.6+dfsg/plugins/provisioners/chef/cap/freebsd/chef_install.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/chef/cap/freebsd/chef_install.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/chef/cap/freebsd/chef_install.rb 2019-10-14 16:38:13.000000000 +0000 @@ -6,7 +6,7 @@ module FreeBSD module ChefInstall def self.chef_install(machine, project, version, channel, omnibus_url, options = {}) - machine.communicate.sudo("pkg install -y -qq curl bash") + machine.communicate.sudo("pkg install -qy curl bash") command = Omnibus.sh_command(project, version, channel, omnibus_url, options) machine.communicate.sudo(command) diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/chef/cap/linux/chef_installed.rb vagrant-2.2.6+dfsg/plugins/provisioners/chef/cap/linux/chef_installed.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/chef/cap/linux/chef_installed.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/chef/cap/linux/chef_installed.rb 2019-10-14 16:38:13.000000000 +0000 @@ -10,7 +10,7 @@ command = "test -x #{knife}" if version != :latest - command << "&& #{knife} --version | grep 'Chef: #{version}'" + command << "&& #{knife} --version | grep '#{version}'" end machine.communicate.test(command, sudo: true) diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/chef/cap/omnios/chef_installed.rb vagrant-2.2.6+dfsg/plugins/provisioners/chef/cap/omnios/chef_installed.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/chef/cap/omnios/chef_installed.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/chef/cap/omnios/chef_installed.rb 2019-10-14 16:38:13.000000000 +0000 @@ -11,7 +11,7 @@ command = "test -x #{knife}" if version != :latest - command << "&& #{knife} --version | grep 'Chef: #{version}'" + command << "&& #{knife} --version | grep '#{version}'" end machine.communicate.test(command, sudo: true) diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/chef/cap/windows/chef_installed.rb vagrant-2.2.6+dfsg/plugins/provisioners/chef/cap/windows/chef_installed.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/chef/cap/windows/chef_installed.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/chef/cap/windows/chef_installed.rb 2019-10-14 16:38:13.000000000 +0000 @@ -7,9 +7,9 @@ # @return [true, false] def self.chef_installed(machine, product, version) if version != :latest - command = 'if ((&knife --version) -Match "Chef: ' + version.to_s + '"){ exit 0 } else { exit 1 }' + command = 'if ((&knife --version) -Match "' + version.to_s + '"){ exit 0 } else { exit 1 }' else - command = 'if ((&knife --version) -Match "Chef: *"){ exit 0 } else { exit 1 }' + command = 'if ((&knife --version) -Match "Chef*"){ exit 0 } else { exit 1 }' end machine.communicate.test(command, sudo: true) end diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/chef/provisioner/base.rb vagrant-2.2.6+dfsg/plugins/provisioners/chef/provisioner/base.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/chef/provisioner/base.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/chef/provisioner/base.rb 2019-10-14 16:38:13.000000000 +0000 @@ -69,7 +69,7 @@ # Checks for the existence of chef binary and error if it # doesn't exist. if windows? - command = "if ((&'#{binary}' -v) -Match 'Chef: *'){ exit 0 } else { exit 1 }" + command = "if ((&'#{binary}' -v) -Match 'Chef*'){ exit 0 } else { exit 1 }" else command = "sh -c 'command -v #{binary}'" end diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/file/provisioner.rb vagrant-2.2.6+dfsg/plugins/provisioners/file/provisioner.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/file/provisioner.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/file/provisioner.rb 2019-10-14 16:38:13.000000000 +0000 @@ -3,43 +3,26 @@ class Provisioner < Vagrant.plugin("2", :provisioner) def provision @machine.communicate.tap do |comm| - source = File.expand_path(config.source) + source = File.expand_path(config.source, @machine.env.cwd) destination = expand_guest_path(config.destination) - # if source is a directory, make it then trim destination with dirname - # Make sure the remote path exists + # If the source is a directory determine if any path modifications + # need to be applied to the source for upload behavior. If the original + # source value ends with a "." or if the original source does not end + # with a "." but the original destination ends with a file separator + # then append a "." character to the new source. This ensures that + # the contents of the directory are uploaded to the destination and + # not folder itself. if File.directory?(source) - # We need to make sure the actual destination folder - # also exists before uploading, otherwise - # you will get nested folders - # - # https://serverfault.com/questions/538368/make-scp-always-overwrite-or-create-directory - # https://unix.stackexchange.com/questions/292641/get-scp-path-behave-like-rsync-path/292732 - command = "mkdir -p \"%s\"" % destination - if !destination.end_with?(File::SEPARATOR) && - !source.end_with?("#{File::SEPARATOR}.") - # We also need to append a '/.' to the source folder so we copy - # the contents rather than the folder itself, in case a users - # destination folder differs from its source - # - # If source is set as `source/` it will lose the trailing - # slash due to how `File.expand_path` works, so we don't need - # a conditional for that case. - if @machine.config.vm.communicator == :winrm - # windows needs an array of paths because of the - # winrm-fs function Vagrant is using to upload file/folder. - source = Dir["#{source}#{File::SEPARATOR}*"] - else - source << "#{File::SEPARATOR}." - end + if config.source.end_with?(".") || + (!config.destination.end_with?(File::SEPARATOR) && + !config.source.end_with?("#{File::SEPARATOR}.")) + source = File.join(source, ".") end - else - command = "mkdir -p \"%s\"" % File.dirname(destination) end - comm.execute(command) @machine.ui.detail(I18n.t("vagrant.actions.vm.provision.file.locations", - src: source, dst: destination)) + src: config.source, dst: config.destination)) # now upload the file comm.upload(source, destination) end diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/shell/config.rb vagrant-2.2.6+dfsg/plugins/provisioners/shell/config.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/shell/config.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/shell/config.rb 2019-10-14 16:38:13.000000000 +0000 @@ -7,6 +7,9 @@ attr_accessor :path attr_accessor :md5 attr_accessor :sha1 + attr_accessor :sha256 + attr_accessor :sha384 + attr_accessor :sha512 attr_accessor :env attr_accessor :upload_path attr_accessor :args @@ -26,6 +29,9 @@ @path = UNSET_VALUE @md5 = UNSET_VALUE @sha1 = UNSET_VALUE + @sha256 = UNSET_VALUE + @sha384 = UNSET_VALUE + @sha512 = UNSET_VALUE @env = UNSET_VALUE @upload_path = UNSET_VALUE @privileged = UNSET_VALUE @@ -45,6 +51,9 @@ @path = nil if @path == UNSET_VALUE @md5 = nil if @md5 == UNSET_VALUE @sha1 = nil if @sha1 == UNSET_VALUE + @sha256 = nil if @sha256 == UNSET_VALUE + @sha384 = nil if @sha384 == UNSET_VALUE + @sha512 = nil if @sha512 == UNSET_VALUE @env = {} if @env == UNSET_VALUE @upload_path = "/tmp/vagrant-shell" if @upload_path == UNSET_VALUE @privileged = true if @privileged == UNSET_VALUE diff -Nru vagrant-2.2.3+dfsg/plugins/provisioners/shell/provisioner.rb vagrant-2.2.6+dfsg/plugins/provisioners/shell/provisioner.rb --- vagrant-2.2.3+dfsg/plugins/provisioners/shell/provisioner.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/provisioners/shell/provisioner.rb 2019-10-14 16:38:13.000000000 +0000 @@ -253,7 +253,10 @@ config.path, download_path, md5: config.md5, - sha1: config.sha1 + sha1: config.sha1, + sha256: config.sha256, + sha384: config.sha384, + sha512: config.sha512 ).download! ext = File.extname(config.path) script = download_path.read diff -Nru vagrant-2.2.3+dfsg/plugins/synced_folders/rsync/command/rsync_auto.rb vagrant-2.2.6+dfsg/plugins/synced_folders/rsync/command/rsync_auto.rb --- vagrant-2.2.3+dfsg/plugins/synced_folders/rsync/command/rsync_auto.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/synced_folders/rsync/command/rsync_auto.rb 2019-10-14 16:38:13.000000000 +0000 @@ -121,9 +121,13 @@ if folder_opts[:exclude] Array(folder_opts[:exclude]).each do |pattern| - ignores << RsyncHelper.exclude_to_regexp(hostpath, pattern.to_s) + ignores << RsyncHelper.exclude_to_regexp(pattern.to_s) end end + + # Always ignore Vagrant + ignores << /.vagrant\// + ignores.uniq! end end diff -Nru vagrant-2.2.3+dfsg/plugins/synced_folders/rsync/default_unix_cap.rb vagrant-2.2.6+dfsg/plugins/synced_folders/rsync/default_unix_cap.rb --- vagrant-2.2.3+dfsg/plugins/synced_folders/rsync/default_unix_cap.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/synced_folders/rsync/default_unix_cap.rb 2019-10-14 16:38:13.000000000 +0000 @@ -29,7 +29,7 @@ def build_rsync_chown(opts) guest_path = Shellwords.escape(opts[:guestpath]) - if(opts[:exclude]) + if(opts[:exclude] && !Array(opts[:exclude]).empty?) exclude_base = Pathname.new(opts[:guestpath]) exclusions = Array(opts[:exclude]).map do |ex_path| ex_path = ex_path.slice(1, ex_path.size) if ex_path.start_with?(File::SEPARATOR) diff -Nru vagrant-2.2.3+dfsg/plugins/synced_folders/rsync/helper.rb vagrant-2.2.6+dfsg/plugins/synced_folders/rsync/helper.rb --- vagrant-2.2.3+dfsg/plugins/synced_folders/rsync/helper.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/synced_folders/rsync/helper.rb 2019-10-14 16:38:13.000000000 +0000 @@ -1,3 +1,4 @@ +require "fileutils" require "ipaddr" require "shellwords" require "tmpdir" @@ -15,7 +16,13 @@ # This converts an rsync exclude pattern to a regular expression # we can send to Listen. - def self.exclude_to_regexp(path, exclude) + # + # Note: Listen expects a path relative to the parameter passed into the + # Listener, not a fully qualified path + # + # @param [String] - exclude path + # @return [Regexp] - A regex of the path, modified, to exclude + def self.exclude_to_regexp(exclude) start_anchor = false if exclude.start_with?("/") @@ -23,9 +30,9 @@ exclude = exclude[1..-1] end - path = "#{path}/" if !path.end_with?("/") - regexp = "^#{Regexp.escape(path)}" - regexp += ".*" if !start_anchor + exclude = "#{exclude}/" if !exclude.end_with?("/") + exclude = "^#{exclude}" + exclude += ".*" if !start_anchor # This is not an ideal solution, but it's a start. We can improve and # keep unit tests passing in the future. @@ -33,9 +40,8 @@ exclude = exclude.gsub("*", "|||PATH|||") exclude = exclude.gsub("|||PATH|||", "[^/]*") exclude = exclude.gsub("|||GLOBAL|||", ".*") - regexp += exclude - Regexp.new(regexp) + Regexp.new(exclude) end def self.rsync_single(machine, ssh_info, opts) @@ -232,6 +238,8 @@ message: err.to_s end end + ensure + FileUtils.remove_entry_secure(controlpath, true) if controlpath end # Check if rsync versions support using chown option diff -Nru vagrant-2.2.3+dfsg/plugins/synced_folders/smb/synced_folder.rb vagrant-2.2.6+dfsg/plugins/synced_folders/smb/synced_folder.rb --- vagrant-2.2.3+dfsg/plugins/synced_folders/smb/synced_folder.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/plugins/synced_folders/smb/synced_folder.rb 2019-10-14 16:38:13.000000000 +0000 @@ -152,6 +152,15 @@ guest: data[:guestpath].to_s)) machine.guest.capability( :mount_smb_shared_folder, data[:smb_id], data[:guestpath], data) + + clean_folder_configuration(data) + end + end + + # Nothing to do here but ensure folder options are scrubbed + def disable(machine, folders, opts) + folders.each do |_, data| + clean_folder_configuration(data) end end @@ -160,6 +169,18 @@ machine.env.host.capability(:smb_cleanup, machine, opts) end end + + protected + + # Remove data that should not be persisted within folder + # specific configuration + # + # @param [Hash] data Folder configuration + def clean_folder_configuration(data) + return if !data.is_a?(Hash) + data.delete(:smb_password) + nil + end end end end diff -Nru vagrant-2.2.3+dfsg/README.md vagrant-2.2.6+dfsg/README.md --- vagrant-2.2.3+dfsg/README.md 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/README.md 2019-10-14 16:38:13.000000000 +0000 @@ -19,6 +19,8 @@ ## 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 @@ -31,11 +33,11 @@ To build your first virtual environment: - vagrant init hashicorp/precise32 + vagrant init hashicorp/bionic64 vagrant up Note: The above `vagrant up` command will also trigger Vagrant to download the -`precise32` box via the specified URL. Vagrant only does this if it detects that +`bionic64` 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 @@ -56,6 +58,14 @@ This will run the unit test suite, which should come back all green! +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: + + 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! diff -Nru vagrant-2.2.3+dfsg/RELEASE.md vagrant-2.2.6+dfsg/RELEASE.md --- vagrant-2.2.3+dfsg/RELEASE.md 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/RELEASE.md 2019-10-14 16:38:13.000000000 +0000 @@ -15,34 +15,20 @@ $ git push --tags ``` -1. Trigger an installer creation run within the HashiCorp Bamboo installation. - This will take around 45 minutes. +1. This will automatically trigger an installer creation, upload the artifacts, + and publish the release. -1. Download all the resulting artifacts into the `pkg/dist` folder relative to - the Vagrant repository on your local machine. +1. After the release has been published update the `website/config.rb` to point + to the latest version, commit, and push. -1. Run `./scripts/sign.sh` with the version that is being created. This must be - run from the Vagrant repo root. This will GPG sign and checksum the files. +1. Publish the webiste by deleting the `stable-website` branch, recreate the branch, + and force push. From the `master` branch, run: -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 - ``` + ``` + $ git branch -D stable-website + $ git branch -b stable-website + $ git push -f origin stable-website + ``` 1. Update `version.txt` to append `.dev` and add a new blank entry in the CHANGELOG, commit, and push. diff -Nru vagrant-2.2.3+dfsg/templates/guests/alpine/network_dhcp.erb vagrant-2.2.6+dfsg/templates/guests/alpine/network_dhcp.erb --- vagrant-2.2.3+dfsg/templates/guests/alpine/network_dhcp.erb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/templates/guests/alpine/network_dhcp.erb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,13 @@ +#VAGRANT-BEGIN +# The contents below are automatically generated by Vagrant. Do not modify. +auto eth<%= options[:interface] %> +iface eth<%= options[:interface] %> inet dhcp +<% if !options[:use_dhcp_assigned_default_route] %> + post-up route del default dev $IFACE || true +<% else %> + # We need to disable eth0, see GH-2648 + post-up route del default dev eth0 + post-up dhclient $IFACE + pre-down route add default dev eth0 +<% end %> +#VAGRANT-END diff -Nru vagrant-2.2.3+dfsg/templates/guests/alpine/network_static.erb vagrant-2.2.6+dfsg/templates/guests/alpine/network_static.erb --- vagrant-2.2.3+dfsg/templates/guests/alpine/network_static.erb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/templates/guests/alpine/network_static.erb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,7 @@ +#VAGRANT-BEGIN +# The contents below are automatically generated by Vagrant. Do not modify. +auto eth<%= options[:interface] %> +iface eth<%= options[:interface] %> inet static + address <%= options[:ip] %> + netmask <%= options[:netmask] %> +#VAGRANT-END diff -Nru vagrant-2.2.3+dfsg/templates/guests/suse/network_static6.erb vagrant-2.2.6+dfsg/templates/guests/suse/network_static6.erb --- vagrant-2.2.3+dfsg/templates/guests/suse/network_static6.erb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/templates/guests/suse/network_static6.erb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,16 @@ +#VAGRANT-BEGIN +# The contents below are automatically generated by Vagrant. Do not modify. +STARTMODE='auto' +BOOTPROTO='static' +IPADDR=<%= options[:ip] %> +<% if options[:netmask] -%> +NETMASK=<%= options[:netmask] %> +<% end -%> +DEVICE=<%= options[:device] %> +<% if options[:gateway] -%> +GATEWAY=<%= options[:gateway] %> +<% end -%> +<% if options[:prefix_length] -%> +PREFIXLEN=<%= options[:prefix_length] %> +<% end -%> +#VAGRANT-END diff -Nru vagrant-2.2.3+dfsg/templates/locales/en.yml vagrant-2.2.6+dfsg/templates/locales/en.yml --- vagrant-2.2.3+dfsg/templates/locales/en.yml 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/templates/locales/en.yml 2019-10-14 16:38:13.000000000 +0000 @@ -82,6 +82,8 @@ * '%{name}' for '%{provider}' (v%{version}) is up to date box_up_to_date_single: |- Box '%{name}' (v%{version}) is running the latest version. + box_version_malformed: + Invalid version '%{version}' for '%{box_name}', ignoring... cfengine_bootstrapping: |- Bootstrapping CFEngine with policy server: %{policy_server}... cfengine_bootstrapping_policy_hub: |- @@ -295,8 +297,11 @@ Trigger configured to continue on error... abort: |- Vagrant has been configured to abort. Terminating now... + abort_threaded: |- + Vagrant has been configured to abort. Vagrant will terminate + after remaining running actions have completed... start: |- - Running triggers %{stage} %{action} ... + Running %{type} triggers %{stage} %{action} ... fire_with_name: |- Running trigger: %{name}... fire: |- @@ -701,10 +706,9 @@ plugins in the `plugins` group in your Gemfile or manually require them in a Vagrantfile. bundler_error: |- - Bundler, the underlying system Vagrant uses to install plugins, - reported an error. The error is shown below. These errors are usually - caused by misconfigured plugin installations or transient network - issues. The error from Bundler is: + Vagrant failed to properly resolve required dependencies. These + errors can commonly be caused by misconfigured plugin installations + or transient network issues. The reported error is: %{message} cant_read_mac_addresses: |- @@ -1502,6 +1506,8 @@ Trigger run failed triggers_guest_not_running: |- Could not run remote script on %{machine_name} because its state is %{state} + triggers_guest_not_exist: |- + Could not run remote script on guest because it does not exist. triggers_bad_exit_codes: |- A script exited with an unacceptable exit code %{code}. triggers_no_block_given: |- @@ -1813,6 +1819,8 @@ sensitive_bad_type: |- Invalid type provided for `sensitive`. The sensitive option expects a string or an array of strings. + plugins_invalid_format: |- + Invalid type provided for `plugins`. plugins_bad_key: |- Invalid plugin configuration detected for `%{plugin_name}` plugin. @@ -1826,6 +1834,8 @@ values are exactly the same, only the name of the option has changed. ssh_config_missing: "`config` file must exist: %{path}" triggers: + bad_trigger_type: |- + The type '%{type}' defined for trigger '%{trigger}' is not valid. Must be one of the following types: '%{types}' bad_command_warning: |- The command '%{cmd}' was not found for this trigger. name_bad_type: |- @@ -1923,6 +1933,14 @@ DrvFs type. Host path: %{path} #------------------------------------------------------------------------------- +# Translations for guests +#------------------------------------------------------------------------------- + guests: + capabilities: + rebooting: |- + Waiting for machine to reboot... + +#------------------------------------------------------------------------------- # Translations for commands. e.g. `vagrant x` #------------------------------------------------------------------------------- commands: @@ -2132,6 +2150,8 @@ choice_help: |- When choosing an interface, it is usually the one that is being used to connect to the internet. + select_interface: |- + Which interface should the network bridge to? specific_not_found: |- Specific bridge '%{bridge}' not found. You may be asked to specify which network to bridge to. @@ -2466,6 +2486,17 @@ VirtualBox has successfully been installed! provisioners: + base: + both_before_after_set: |- + Dependency provisioners cannot currently set both `before` and `after` options. + dependency_provisioner_dependency: |- + Dependency provisioner "%{name}" relies on another dependency provisioner "%{dep_name}". This is currently not supported. + invalid_alias_value: |- + Provisioner option `%{opt}` is not set as a valid type. Must be a string, or one of the alias shortcuts: %{alias} + missing_provisioner_name: |- + Could not find provisioner name `%{name}` defined for machine `%{machine_name}` to run provisioner "%{provisioner_name}" `%{action}`. + wrong_type: |- + Provisioner option `%{opt}` is not set as a valid type. Must be a string. chef: chef_not_detected: |- The chef binary (either `chef-solo` or `chef-client`) was not found on diff -Nru vagrant-2.2.3+dfsg/templates/locales/providers_docker.yml vagrant-2.2.6+dfsg/templates/locales/providers_docker.yml --- vagrant-2.2.3+dfsg/templates/locales/providers_docker.yml 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/templates/locales/providers_docker.yml 2019-10-14 16:38:13.000000000 +0000 @@ -45,6 +45,35 @@ This container requires a host VM, and the state of that VM is unknown. Run `vagrant up` to verify that the container and its host VM is running, then try again. + network_bridge_gateway_invalid: |- + The provided gateway IP address is invalid (%{gateway}). Please + provide a valid IP address. + network_bridge_gateway_outofbounds: |- + The provided gateway IP (%{gateway}) is not within the defined + subnet (%{subnet}). Please provide an IP address within the + defined subnet. + network_bridge_gateway_request: |- + Gateway IP address for %{interface} interface [%{default_gateway}]: + network_bridge_iprange_info: |- + When an explicit address is not provided to a container attached + to this bridged network, docker will supply an address to the + container. This is independent of the local DHCP service that + may be available on the network. + network_bridge_iprange_invalid: |- + The provided IP address range is invalid (%{range}). Please + provide a valid range. + network_bridge_iprange_outofbounds: |- + The provided IP address range (%{range}) is not within the + defined subnet (%{subnet}). Please provide an address range + within the defined subnet. + network_bridge_iprange_request: |- + Available address range for assignment on %{interface} interface [%{default_range}]: + network_create: |- + Creating and configuring docker networks... + network_connect: |- + Enabling network interfaces... + network_destroy: |- + Removing network %{network_name} ... not_created_skip: |- Container not created. Skipping. not_docker_provider: |- @@ -66,12 +95,21 @@ ssh_through_host_vm: |- SSH will be proxied through the Docker virtual machine since we're not running Docker natively. This is just a notice, and not an error. + subnet_exists: |- + A network called '%{network_name}' using subnet '%{subnet}' is already in use. + Using '%{network_name}' instead of creating a new network... synced_folders_changed: |- Vagrant has noticed that the synced folder definitions have changed. With Docker, these synced folder changes won't take effect until you destroy the container and recreate it. waiting_for_running: |- Waiting for container to enter "running" state... + volume_path_not_expanded: |- + Host path `%{host}` exists as a `volumes` key and is a folder on disk. Vagrant + will not expand this key like it used to and instead leave it as is defined. + If this folder is intended to be used, make sure its full path is defined + in your `volumes` config. More information can be found on volumes in the + docker compose documentation. messages: destroying: |- @@ -197,6 +235,38 @@ is functional and properly configured. Host VM ID: %{id} + network_address_invalid: |- + The configured network address is not valid within the configured + subnet of the defined network. Please update the network settings + and try again. + + Configured address: %{address} + Network name: %{network_name} + network_address_required: |- + An IP address is required if not using `type: "dhcp"` or not specifying a `subnet`. + network_invalid_option: |- + Invalid option given for docker network for guest "%{container}". Must specify either + a `subnet` or use `type: "dhcp"`. + network_name_missing: |- + The Docker provider is unable to connect the container to the + defined network due to a missing network name. Please validate + your configuration and try again. + + Container: %{container} + Network Number: %{index} + network_name_undefined: |- + The Docker provider was unable to configure networking using the + provided network name `%{network_name}`. Please ensure the network + name is correct and exists, then try again. + network_no_interfaces: |- + The Docker provider was unable to list any available interfaces to bridge + the public network with. + network_subnet_invalid: |- + The configured network subnet is not valid for the defined network. + Please update the network settings and try again. + + Configured subnet: %{subnet} + Network name: %{network_name} package_not_supported: |- The "package" command is not supported with the Docker provider. If you'd like to commit or push your Docker container, please SSH diff -Nru vagrant-2.2.3+dfsg/templates/nfs/exports_bsd.erb vagrant-2.2.6+dfsg/templates/nfs/exports_bsd.erb --- vagrant-2.2.3+dfsg/templates/nfs/exports_bsd.erb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/templates/nfs/exports_bsd.erb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,5 @@ +# VAGRANT-BEGIN: <%= user %> <%= uuid %> +<% folders.each do |dirs, opts| %> +<%= dirs.map { |d| "#{d}" }.join(" ") %> <%=opts[:bsd__compiled_nfs_options] %> <%= ips.join(" ") %> +<% end %> +# VAGRANT-END: <%= user %> <%= uuid %> diff -Nru vagrant-2.2.3+dfsg/templates/nfs/exports.erb vagrant-2.2.6+dfsg/templates/nfs/exports.erb --- vagrant-2.2.3+dfsg/templates/nfs/exports.erb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/templates/nfs/exports.erb 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -# VAGRANT-BEGIN: <%= user %> <%= uuid %> -<% folders.each do |dirs, opts| %> -<%= dirs.map { |d| "\"#{d}\"" }.join(" ") %> <%= ips.join(" ") %> <%=opts[:bsd__compiled_nfs_options] %> -<% end %> -# VAGRANT-END: <%= user %> <%= uuid %> diff -Nru vagrant-2.2.3+dfsg/templates/nfs/exports_freebsd.erb vagrant-2.2.6+dfsg/templates/nfs/exports_freebsd.erb --- vagrant-2.2.3+dfsg/templates/nfs/exports_freebsd.erb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/templates/nfs/exports_freebsd.erb 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -# VAGRANT-BEGIN: <%= user %> <%= uuid %> -<% folders.each do |dirs, opts| %> -<%= dirs.map { |d| "#{d}" }.join(" ") %> <%=opts[:bsd__compiled_nfs_options] %> <%= ips.join(" ") %> -<% end %> -# VAGRANT-END: <%= user %> <%= uuid %> diff -Nru vagrant-2.2.3+dfsg/test/unit/base.rb vagrant-2.2.6+dfsg/test/unit/base.rb --- vagrant-2.2.3+dfsg/test/unit/base.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/base.rb 2019-10-14 16:38:13.000000000 +0000 @@ -41,6 +41,10 @@ c.filter_run_excluding :windows end + if !Vagrant::Util::Which.which("bsdtar") + c.filter_run_excluding :bsdtar + end + c.after(:suite) do FileUtils.rm_rf(VAGRANT_TEST_CWD) end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/commands/box/command/update_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/commands/box/command/update_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/commands/box/command/update_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/commands/box/command/update_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -67,7 +67,7 @@ expect(called).to be(false) end - it "does update if there is an update" do + it "does the correct update if there is an update" do metadata_url.open("w") do |f| f.write(<<-RAW) { @@ -77,7 +77,25 @@ "version": "1.0" }, { - "version": "1.1", + "version": "1.8", + "providers": [ + { + "name": "virtualbox", + "url": "bar" + } + ] + }, + { + "version": "1.10", + "providers": [ + { + "name": "virtualbox", + "url": "bar" + } + ] + }, + { + "version": "1.11", "providers": [ { "name": "virtualbox", @@ -97,10 +115,10 @@ expect(opts[:box_force]).to eq(nil) expect(opts[:box_url]).to eq(metadata_url.to_s) expect(opts[:box_provider]).to eq("virtualbox") - expect(opts[:box_version]).to eq("1.1") + expect(opts[:box_version]).to eq("1.11") expect(opts[:box_download_ca_path]).to be_nil expect(opts[:box_download_ca_cert]).to be_nil - expect(opts[:box_client_cert]).to be_nil + expect(opts[:box_download_client_cert]).to be_nil expect(opts[:box_download_insecure]).to be_nil end @@ -206,7 +224,7 @@ action_called = true expect(opts[:box_download_ca_cert]).to eq("foo") expect(opts[:box_download_ca_path]).to eq("bar") - expect(opts[:box_client_cert]).to eq("baz") + expect(opts[:box_download_client_cert]).to eq("baz") expect(opts[:box_download_insecure]).to be(true) end @@ -355,7 +373,7 @@ expect(action_runner).to receive(:run).with(any_args) { |action, opts| expect(opts[:box_download_ca_cert]).to eq("oof") expect(opts[:box_download_ca_path]).to eq("rab") - expect(opts[:box_client_cert]).to eq("zab") + expect(opts[:box_download_client_cert]).to eq("zab") expect(opts[:box_download_insecure]).to be(false) true } @@ -378,7 +396,7 @@ expect(action_runner).to receive(:run).with(any_args) { |action, opts| expect(opts[:box_download_ca_cert]).to eq("foo") expect(opts[:box_download_ca_path]).to eq("bar") - expect(opts[:box_client_cert]).to eq("baz") + expect(opts[:box_download_client_cert]).to eq("baz") expect(opts[:box_download_insecure]).to be(true) true } @@ -386,6 +404,24 @@ subject.execute end end + + context "ignoring boxes with no metadata" do + before do + allow(subject).to receive(:with_target_vms) { |&block| block.call machine } + end + + let(:box) do + box_dir = test_iso_env.box3("foo", "1.0", :virtualbox) + box = Vagrant::Box.new( + "foo", :virtualbox, "1.0", box_dir, metadata_url: "foo") + allow(box).to receive(:has_update?).and_raise(Vagrant::Errors::BoxUpdateNoMetadata, name: "foo") + box + end + + it "continues to update the rest of the boxes in the environment" do + subject.execute + end + end context "force flag is specified on the command line" do let(:argv) { ["--force"].concat(download_options) } diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/commands/cloud/auth/middleware/add_authentication_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/commands/cloud/auth/middleware/add_authentication_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/commands/cloud/auth/middleware/add_authentication_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/commands/cloud/auth/middleware/add_authentication_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,169 @@ +require File.expand_path("../../../../../../base", __FILE__) + +require Vagrant.source_root.join("plugins/commands/cloud/auth/middleware/add_authentication") + +describe VagrantPlugins::CloudCommand::AddAuthentication do + include_context "unit" + + let(:app) { lambda { |env| } } + let(:ui) { double("ui") } + let(:env) { { + env: iso_env, + ui: ui + } } + + let(:iso_env) { isolated_environment.create_vagrant_env } + let(:server_url) { "http://vagrantcloud.com" } + + subject { described_class.new(app, env) } + + before do + allow(Vagrant).to receive(:server_url).and_return(server_url) + allow(ui).to receive(:warn) + stub_env("ATLAS_TOKEN" => nil) + end + + describe "#call" do + it "does nothing if we have no server set" do + allow(Vagrant).to receive(:server_url).and_return(nil) + VagrantPlugins::CloudCommand::Client.new(iso_env).store_token("foo") + + original = ["foo", "#{server_url}/bar"] + env[:box_urls] = original.dup + + subject.call(env) + + expect(env[:box_urls]).to eq(original) + end + + it "does nothing if we aren't logged in" do + original = ["foo", "#{server_url}/bar"] + env[:box_urls] = original.dup + + subject.call(env) + + expect(env[:box_urls]).to eq(original) + end + + it "appends the access token to the URL of server URLs" do + token = "foobarbaz" + VagrantPlugins::CloudCommand::Client.new(iso_env).store_token(token) + + original = [ + "http://google.com/box.box", + "#{server_url}/foo.box", + "#{server_url}/bar.box?arg=true", + ] + + expected = original.dup + expected[1] = "#{original[1]}?access_token=#{token}" + expected[2] = "#{original[2]}&access_token=#{token}" + + env[:box_urls] = original.dup + subject.call(env) + + expect(env[:box_urls]).to eq(expected) + end + + it "does not append the access token to vagrantcloud.com URLs if Atlas" do + server_url = "https://atlas.hashicorp.com" + allow(Vagrant).to receive(:server_url).and_return(server_url) + allow(subject).to receive(:sleep) + + token = "foobarbaz" + VagrantPlugins::CloudCommand::Client.new(iso_env).store_token(token) + + original = [ + "http://google.com/box.box", + "http://vagrantcloud.com/foo.box", + "http://vagrantcloud.com/bar.box?arg=true", + ] + + expected = original.dup + + env[:box_urls] = original.dup + subject.call(env) + + expect(env[:box_urls]).to eq(expected) + end + + it "warns when adding token to custom server" do + server_url = "https://example.com" + allow(Vagrant).to receive(:server_url).and_return(server_url) + + token = "foobarbaz" + VagrantPlugins::CloudCommand::Client.new(iso_env).store_token(token) + + original = [ + "http://google.com/box.box", + "http://vagrantcloud.com/foo.box", + "http://example.com/bar.box", + "http://example.com/foo.box" + ] + + expected = original.dup + expected[2] = expected[2] + "?access_token=#{token}" + expected[3] = expected[3] + "?access_token=#{token}" + + expect(subject).to receive(:sleep).once + expect(ui).to receive(:warn).once + + env[:box_urls] = original.dup + subject.call(env) + + expect(env[:box_urls]).to eq(expected) + end + + it "modifies host URL to target if authorized host" do + originals = VagrantPlugins::CloudCommand::AddAuthentication:: + REPLACEMENT_HOSTS.map{ |h| "http://#{h}/box.box" } + expected = "http://#{VagrantPlugins::CloudCommand::AddAuthentication::TARGET_HOST}/box.box" + env[:box_urls] = originals + subject.call(env) + env[:box_urls].each do |url| + expect(url).to eq(expected) + end + end + + it "ignores urls that it cannot parse" do + bad_url = "this is not a valid url" + # Ensure the bad URL does cause an exception + expect{ URI.parse(bad_url) }.to raise_error URI::Error + env[:box_urls] = [bad_url] + subject.call(env) + expect(env[:box_urls].first).to eq(bad_url) + end + + it "returns original urls when not modified" do + to_persist = "file:////path/to/box.box" + to_change = VagrantPlugins::CloudCommand::AddAuthentication:: + REPLACEMENT_HOSTS.map{ |h| "http://#{h}/box.box" }.first + expected = "http://#{VagrantPlugins::CloudCommand::AddAuthentication::TARGET_HOST}/box.box" + env[:box_urls] = [to_persist, to_change] + subject.call(env) + check_persist, check_change = env[:box_urls] + expect(check_change).to eq(expected) + expect(check_persist).to eq(to_persist) + # NOTE: The behavior of URI.parse changes on Ruby 2.5 to produce + # the same string value. To make the test worthwhile in checking + # for the same value, check that the object IDs are still the same. + expect(check_persist.object_id).to eq(to_persist.object_id) + end + + it "does not append multiple access_tokens" do + token = "foobarbaz" + VagrantPlugins::CloudCommand::Client.new(iso_env).store_token(token) + + original = [ + "#{server_url}/foo.box?access_token=existing", + "#{server_url}/bar.box?arg=true", + ] + + env[:box_urls] = original.dup + subject.call(env) + + expect(env[:box_urls][0]).to eq("#{server_url}/foo.box?access_token=existing") + expect(env[:box_urls][1]).to eq("#{server_url}/bar.box?arg=true&access_token=foobarbaz") + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/commands/cloud/provider/create_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/commands/cloud/provider/create_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/commands/cloud/provider/create_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/commands/cloud/provider/create_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -48,7 +48,7 @@ it "creates a provider" do allow(VagrantCloud::Provider).to receive(:new). - with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token). + with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token, nil, nil, nil). and_return(provider) expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results) @@ -59,7 +59,7 @@ it "displays an error if encoutering a problem with the request" do allow(VagrantCloud::Provider).to receive(:new). - with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token). + with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token, nil, nil, nil). and_return(provider) allow(provider).to receive(:create_provider). @@ -73,7 +73,7 @@ it "creates a provider" do allow(VagrantCloud::Provider).to receive(:new). - with(version, "virtualbox", nil, "https://box.com/box", "vagrant", "box-name", client.token). + with(version, "virtualbox", nil, "https://box.com/box", "vagrant", "box-name", client.token, nil, nil, nil). and_return(provider) expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results) diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/commands/cloud/provider/update_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/commands/cloud/provider/update_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/commands/cloud/provider/update_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/commands/cloud/provider/update_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -48,7 +48,7 @@ it "updates a provider" do allow(VagrantCloud::Provider).to receive(:new). - with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token). + with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token, nil, nil, nil). and_return(provider) expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results) @@ -59,7 +59,7 @@ it "displays an error if encoutering a problem with the request" do allow(VagrantCloud::Provider).to receive(:new). - with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token). + with(version, "virtualbox", nil, nil, "vagrant", "box-name", client.token, nil, nil, nil). and_return(provider) allow(provider).to receive(:update). @@ -73,7 +73,7 @@ it "creates a provider" do allow(VagrantCloud::Provider).to receive(:new). - with(version, "virtualbox", nil, "https://box.com/box", "vagrant", "box-name", client.token). + with(version, "virtualbox", nil, "https://box.com/box", "vagrant", "box-name", client.token, nil, nil, nil). and_return(provider) expect(VagrantPlugins::CloudCommand::Util).to receive(:format_box_results) diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/commands/global-status/command_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/commands/global-status/command_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/commands/global-status/command_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/commands/global-status/command_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -10,9 +10,11 @@ # We have to create a Vagrantfile so there is a root path env = isolated_environment env.vagrantfile("") - env.create_vagrant_env + env.create_vagrant_env(env_opts) end + let(:env_opts) { {} } + let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } let(:argv) { [] } @@ -37,6 +39,38 @@ end end + describe "with --machine-readable" do + let(:env_opts) { {ui_class: Vagrant::UI::MachineReadable} } + + before do + iso_env.machine_index.set(new_entry("foo")) + iso_env.machine_index.set(new_entry("bar")) + allow($stdout).to receive(:puts) + end + + after { subject.execute } + + it "should include the machine id" do + expect($stdout).to receive(:puts).with(/,machine-id,/).twice + end + + it "should include the machine state" do + expect($stdout).to receive(:puts).with(/,state,/).twice + end + + it "should include the machine count" do + expect($stdout).to receive(:puts).with(/machine-count,2/) + end + + it "should include the machine home path" do + expect($stdout).to receive(:puts).with(/,machine-home,/).twice + end + + it "should include the provider name" do + expect($stdout).to receive(:puts).with(/,provider-name,/).twice + end + end + describe "execute with --prune" do let(:argv) { ["--prune"] } diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/commands/login/middleware/add_authentication_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/commands/login/middleware/add_authentication_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/commands/login/middleware/add_authentication_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/commands/login/middleware/add_authentication_test.rb 1970-01-01 00:00:00.000000000 +0000 @@ -1,169 +0,0 @@ -require File.expand_path("../../../../../base", __FILE__) - -require Vagrant.source_root.join("plugins/commands/login/middleware/add_authentication") - -describe VagrantPlugins::LoginCommand::AddAuthentication do - include_context "unit" - - let(:app) { lambda { |env| } } - let(:ui) { double("ui") } - let(:env) { { - env: iso_env, - ui: ui - } } - - let(:iso_env) { isolated_environment.create_vagrant_env } - let(:server_url) { "http://vagrantcloud.com" } - - subject { described_class.new(app, env) } - - before do - allow(Vagrant).to receive(:server_url).and_return(server_url) - allow(ui).to receive(:warn) - stub_env("ATLAS_TOKEN" => nil) - end - - describe "#call" do - it "does nothing if we have no server set" do - allow(Vagrant).to receive(:server_url).and_return(nil) - VagrantPlugins::LoginCommand::Client.new(iso_env).store_token("foo") - - original = ["foo", "#{server_url}/bar"] - env[:box_urls] = original.dup - - subject.call(env) - - expect(env[:box_urls]).to eq(original) - end - - it "does nothing if we aren't logged in" do - original = ["foo", "#{server_url}/bar"] - env[:box_urls] = original.dup - - subject.call(env) - - expect(env[:box_urls]).to eq(original) - end - - it "appends the access token to the URL of server URLs" do - token = "foobarbaz" - VagrantPlugins::LoginCommand::Client.new(iso_env).store_token(token) - - original = [ - "http://google.com/box.box", - "#{server_url}/foo.box", - "#{server_url}/bar.box?arg=true", - ] - - expected = original.dup - expected[1] = "#{original[1]}?access_token=#{token}" - expected[2] = "#{original[2]}&access_token=#{token}" - - env[:box_urls] = original.dup - subject.call(env) - - expect(env[:box_urls]).to eq(expected) - end - - it "does not append the access token to vagrantcloud.com URLs if Atlas" do - server_url = "https://atlas.hashicorp.com" - allow(Vagrant).to receive(:server_url).and_return(server_url) - allow(subject).to receive(:sleep) - - token = "foobarbaz" - VagrantPlugins::LoginCommand::Client.new(iso_env).store_token(token) - - original = [ - "http://google.com/box.box", - "http://vagrantcloud.com/foo.box", - "http://vagrantcloud.com/bar.box?arg=true", - ] - - expected = original.dup - - env[:box_urls] = original.dup - subject.call(env) - - expect(env[:box_urls]).to eq(expected) - end - - it "warns when adding token to custom server" do - server_url = "https://example.com" - allow(Vagrant).to receive(:server_url).and_return(server_url) - - token = "foobarbaz" - VagrantPlugins::LoginCommand::Client.new(iso_env).store_token(token) - - original = [ - "http://google.com/box.box", - "http://vagrantcloud.com/foo.box", - "http://example.com/bar.box", - "http://example.com/foo.box" - ] - - expected = original.dup - expected[2] = expected[2] + "?access_token=#{token}" - expected[3] = expected[3] + "?access_token=#{token}" - - expect(subject).to receive(:sleep).once - expect(ui).to receive(:warn).once - - env[:box_urls] = original.dup - subject.call(env) - - expect(env[:box_urls]).to eq(expected) - end - - it "modifies host URL to target if authorized host" do - originals = VagrantPlugins::LoginCommand::AddAuthentication:: - REPLACEMENT_HOSTS.map{ |h| "http://#{h}/box.box" } - expected = "http://#{VagrantPlugins::LoginCommand::AddAuthentication::TARGET_HOST}/box.box" - env[:box_urls] = originals - subject.call(env) - env[:box_urls].each do |url| - expect(url).to eq(expected) - end - end - - it "ignores urls that it cannot parse" do - bad_url = "this is not a valid url" - # Ensure the bad URL does cause an exception - expect{ URI.parse(bad_url) }.to raise_error URI::Error - env[:box_urls] = [bad_url] - subject.call(env) - expect(env[:box_urls].first).to eq(bad_url) - end - - it "returns original urls when not modified" do - to_persist = "file:////path/to/box.box" - to_change = VagrantPlugins::LoginCommand::AddAuthentication:: - REPLACEMENT_HOSTS.map{ |h| "http://#{h}/box.box" }.first - expected = "http://#{VagrantPlugins::LoginCommand::AddAuthentication::TARGET_HOST}/box.box" - env[:box_urls] = [to_persist, to_change] - subject.call(env) - check_persist, check_change = env[:box_urls] - expect(check_change).to eq(expected) - expect(check_persist).to eq(to_persist) - # NOTE: The behavior of URI.parse changes on Ruby 2.5 to produce - # the same string value. To make the test worthwhile in checking - # for the same value, check that the object IDs are still the same. - expect(check_persist.object_id).to eq(to_persist.object_id) - end - - it "does not append multiple access_tokens" do - token = "foobarbaz" - VagrantPlugins::LoginCommand::Client.new(iso_env).store_token(token) - - original = [ - "#{server_url}/foo.box?access_token=existing", - "#{server_url}/bar.box?arg=true", - ] - - env[:box_urls] = original.dup - subject.call(env) - - expect(env[:box_urls][0]).to eq("#{server_url}/foo.box?access_token=existing") - expect(env[:box_urls][1]).to eq("#{server_url}/bar.box?arg=true&access_token=foobarbaz") - end - end -end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/commands/snapshot/command/list_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/commands/snapshot/command/list_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/commands/snapshot/command/list_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/commands/snapshot/command/list_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -72,9 +72,10 @@ allow(machine.provider).to receive(:capability).with(:snapshot_list). and_return(["foo", "bar", "baz"]) - expect(iso_env.ui).to receive(:output).with(/foo/, anything) - expect(iso_env.ui).to receive(:output).with(/bar/, anything) - expect(iso_env.ui).to receive(:output).with(/baz/, anything) + expect(iso_env.ui).to receive(:output).with(/default/, anything) + expect(iso_env.ui).to receive(:detail).with(/foo/, anything) + expect(iso_env.ui).to receive(:detail).with(/bar/, anything) + expect(iso_env.ui).to receive(:detail).with(/baz/, anything) expect(subject.execute).to eq(0) end end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/commands/snapshot/command/save_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/commands/snapshot/command/save_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/commands/snapshot/command/save_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/commands/snapshot/command/save_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -76,6 +76,29 @@ end end + context "with a snapshot guest and name given" do + let(:argv) { ["foo", "backup"] } + it "calls snapshot_save with a snapshot name" do + machine.id = "foo" + + expect(machine).to receive(:action) do |name, opts| + expect(name).to eq(:snapshot_save) + expect(opts[:snapshot_name]).to eq("backup") + end + + expect(subject.execute).to eq(0) + end + + it "doesn't snapshot a non-existent machine" do + machine.id = nil + + expect(subject).to receive(:with_target_vms){} + + expect(machine).to_not receive(:action) + expect(subject.execute).to eq(0) + end + end + context "with a duplicate snapshot name given and no force flag" do let(:argv) { ["test"] } diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/communicators/ssh/communicator_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/communicators/ssh/communicator_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/communicators/ssh/communicator_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/communicators/ssh/communicator_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -497,12 +497,39 @@ describe ".upload" do before do expect(communicator).to receive(:scp_connect).and_yield(scp) + allow(communicator).to receive(:create_remote_directory) end - it "uploads a directory if local path is a directory" do - Dir.mktmpdir('vagrant-test') do |dir| - expect(scp).to receive(:upload!).with(dir, '/destination', recursive: true) - communicator.upload(dir, '/destination') + context "directory uploads" do + let(:test_dir) { @dir } + let(:test_file) { File.join(test_dir, "test-file") } + let(:dir_name) { File.basename(test_dir) } + let(:file_name) { File.basename(test_file) } + + before do + @dir = Dir.mktmpdir("vagrant-test") + FileUtils.touch(test_file) + end + + after { FileUtils.rm_rf(test_dir) } + + it "uploads directory when directory path provided" do + expect(scp).to receive(:upload!).with(instance_of(File), + File.join("", "destination", dir_name, file_name)) + communicator.upload(test_dir, "/destination") + end + + it "uploads contents of directory when dot suffix provided on directory" do + expect(scp).to receive(:upload!).with(instance_of(File), + File.join("", "destination", file_name)) + communicator.upload(File.join(test_dir, "."), "/destination") + end + + it "creates directories before upload" do + expect(communicator).to receive(:create_remote_directory).with( + /#{Regexp.escape(File.join("", "destination", dir_name))}/) + allow(scp).to receive(:upload!) + communicator.upload(test_dir, "/destination") end end @@ -516,6 +543,28 @@ end end + it "uploads file to directory if destination ends with file separator" do + file = Tempfile.new('vagrant-test') + begin + expect(scp).to receive(:upload!).with(instance_of(File), "/destination/dir/#{File.basename(file.path)}") + expect(communicator).to receive(:create_remote_directory).with("/destination/dir") + communicator.upload(file.path, "/destination/dir/") + ensure + file.delete + end + end + + it "creates remote directory path to destination on upload" do + file = Tempfile.new('vagrant-test') + begin + expect(scp).to receive(:upload!).with(instance_of(File), "/destination/dir/file.txt") + expect(communicator).to receive(:create_remote_directory).with("/destination/dir") + communicator.upload(file.path, "/destination/dir/file.txt") + ensure + file.delete + end + end + it "raises custom error on permission errors" do file = Tempfile.new('vagrant-test') begin @@ -609,7 +658,7 @@ end it "includes the default cipher array for encryption" do - cipher_array = %w(aes256-ctr aes192-ctr aes128-ctr + cipher_array = %w(aes256-ctr aes192-ctr aes128-ctr aes256-cbc aes192-cbc aes128-cbc rijndael-cbc@lysator.liu.se blowfish-ctr blowfish-cbc cast128-ctr cast128-cbc diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/communicators/winrm/shell_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/communicators/winrm/shell_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/communicators/winrm/shell_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/communicators/winrm/shell_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -33,13 +33,17 @@ describe "#upload" do let(:fm) { double("file_manager") } + + before do + allow(WinRM::FS::FileManager).to receive(:new).with(connection) + .and_return(fm) + end + it "should call file_manager.upload for each passed in path" do from = ["/path", "/path/folder", "/path/folder/file.py"] to = "/destination" size = 80 - allow(WinRM::FS::FileManager).to receive(:new).with(connection) - .and_return(fm) allow(fm).to receive(:upload).and_return(size) expect(fm).to receive(:upload).exactly(from.size).times @@ -51,13 +55,34 @@ to = "/destination" size = 80 - allow(WinRM::FS::FileManager).to receive(:new).with(connection) - .and_return(fm) allow(fm).to receive(:upload).and_return(size) expect(fm).to receive(:upload).exactly(1).times expect(subject.upload(from, to)).to eq(size) end + + context "when source is a directory" do + let(:source) { "path/sourcedir" } + + before do + allow(File).to receive(:directory?).with(/#{Regexp.escape(source)}/).and_return(true) + end + + it "should add source directory name to destination" do + expect(fm).to receive(:upload) do |from, to| + expect(to).to include("sourcedir") + end + subject.upload(source, "/dest") + end + + it "should not add source directory name to destination when source ends with '.'" do + source << "/." + expect(fm).to receive(:upload) do |from, to| + expect(to).to eq("/dest") + end + subject.upload(source, "/dest") + end + end end describe ".powershell" do diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/communicators/winssh/communicator_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/communicators/winssh/communicator_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/communicators/winssh/communicator_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/communicators/winssh/communicator_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -226,12 +226,14 @@ describe ".upload" do before do + allow(communicator).to receive(:create_remote_directory) expect(communicator).to receive(:scp_connect).and_yield(scp) end it "uploads a directory if local path is a directory" do Dir.mktmpdir('vagrant-test') do |dir| - expect(scp).to receive(:upload!).with(dir, 'C:\destination', recursive: true) + FileUtils.touch(File.join(dir, "test-file")) + expect(scp).to receive(:upload!).with(an_instance_of(File), /test-file/) communicator.upload(dir, 'C:\destination') end end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/alpine/cap/change_host_name_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/alpine/cap/change_host_name_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/alpine/cap/change_host_name_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/alpine/cap/change_host_name_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,127 @@ +require_relative "../../../../base" + +describe 'VagrantPlugins::GuestAlpine::Cap::ChangeHostname' do + let(:described_class) do + VagrantPlugins::GuestAlpine::Plugin.components.guest_capabilities[:alpine].get(:change_host_name) + end + let(:machine) { double('machine') } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + let(:old_hostname) { 'oldhostname.olddomain.tld' } + + before do + allow(machine).to receive(:communicate).and_return(communicator) + communicator.stub_command('hostname -f', stdout: old_hostname) + end + + after do + communicator.verify_expectations! + end + + describe '.change_host_name' do + it 'updates /etc/hostname on the machine' do + communicator.expect_command("echo 'newhostname' > /etc/hostname") + described_class.change_host_name(machine, 'newhostname.newdomain.tld') + end + + it 'does nothing when the provided hostname is not different' do + described_class.change_host_name(machine, 'oldhostname.olddomain.tld') + expect(communicator.received_commands).to eq(['hostname -f']) + end + + it 'refreshes the hostname service with the hostname command' do + communicator.expect_command('hostname -F /etc/hostname') + described_class.change_host_name(machine, 'newhostname.newdomain.tld') + end + + it 'renews dhcp on the system with the new hostname' do + communicator.expect_command('ifdown -a; ifup -a; ifup eth0') + described_class.change_host_name(machine, 'newhostname.newdomain.tld') + end + + describe 'flipping out the old hostname in /etc/hosts' do + let(:sed_command) do + # Here we run the change_host_name through and extract the recorded sed + # command from the dummy communicator + described_class.change_host_name(machine, 'newhostname.newdomain.tld') + communicator.received_commands.find { |cmd| cmd =~ /^sed/ } + end + + # Now we extract the regexp from that sed command so we can do some + # verification on it + let(:expression) { sed_command.sub(%r{^sed -ri '\(.*\)' /etc/hosts$}, "\1") } + let(:search) { Regexp.new(expression.split('@')[1], Regexp::EXTENDED) } + let(:replace) { expression.split('@')[2] } + + let(:grep_command) { "grep '#{old_hostname}' /etc/hosts" } + + before do + communicator.stub_command(grep_command, exit_code: 0) + end + + it 'works on an simple /etc/hosts file' do + original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') + 127.0.0.1 localhost + 127.0.1.1 oldhostname.olddomain.tld oldhostname + ETC_HOSTS + + modified_etc_hosts = original_etc_hosts.gsub(search, replace) + + expect(modified_etc_hosts).to eq <<-RESULT.gsub(/^ */, '') + 127.0.0.1 localhost + 127.0.1.1 newhostname.newdomain.tld newhostname + RESULT + end + + it 'does not modify lines which contain similar hostnames' do + original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') + 127.0.0.1 localhost + 127.0.1.1 oldhostname.olddomain.tld oldhostname + # common prefix, but different fqdn + 192.168.12.34 oldhostname.olddomain.tld.different + # different characters at the dot + 192.168.34.56 oldhostname-olddomain.tld + ETC_HOSTS + + modified_etc_hosts = original_etc_hosts.gsub(search, replace) + + expect(modified_etc_hosts).to eq <<-RESULT.gsub(/^ */, '') + 127.0.0.1 localhost + 127.0.1.1 newhostname.newdomain.tld newhostname + # common prefix, but different fqdn + 192.168.12.34 oldhostname.olddomain.tld.different + # different characters at the dot + 192.168.34.56 oldhostname-olddomain.tld + RESULT + end + + it "appends 127.0.1.1 if it isn't there" do + communicator.stub_command(grep_command, exit_code: 1) + described_class.change_host_name(machine, 'newhostname.newdomain.tld') + + sed = communicator.received_commands.find { |cmd| cmd =~ /^sed/ } + expect(sed).to be_nil + + echo = communicator.received_commands.find { |cmd| cmd =~ /^echo/ } + expect(echo).to_not be_nil + end + + context 'when the old fqdn has a trailing dot' do + let(:old_hostname) { 'oldhostname.withtrailing.dot.' } + + it 'modifies /etc/hosts properly' do + original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') + 127.0.0.1 localhost + 127.0.1.1 oldhostname.withtrailing.dot. oldhostname + ETC_HOSTS + + modified_etc_hosts = original_etc_hosts.gsub(search, replace) + + expect(modified_etc_hosts).to eq <<-RESULT.gsub(/^ */, '') + 127.0.0.1 localhost + 127.0.1.1 newhostname.newdomain.tld newhostname + RESULT + end + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/alpine/cap/configure_networks_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/alpine/cap/configure_networks_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/alpine/cap/configure_networks_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/alpine/cap/configure_networks_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,39 @@ +require_relative "../../../../base" + +describe 'VagrantPlugins::GuestAlpine::Cap::ConfigureNetworks' do + let(:described_class) do + VagrantPlugins::GuestAlpine::Plugin.components.guest_capabilities[:alpine].get(:configure_networks) + end + let(:machine) { double('machine') } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + + before do + allow(machine).to receive(:communicate).and_return(communicator) + end + + after do + communicator.verify_expectations! + end + + it 'should configure networks' do + networks = [ + { type: :static, ip: '192.168.10.10', netmask: '255.255.255.0', interface: 0, name: 'eth0' }, + { type: :dhcp, interface: 1, name: 'eth1' } + ] + + expect(communicator).to receive(:sudo).with("sed -e '/^#VAGRANT-BEGIN/,$ d' /etc/network/interfaces > /tmp/vagrant-network-interfaces.pre") + expect(communicator).to receive(:sudo).with("sed -ne '/^#VAGRANT-END/,$ p' /etc/network/interfaces | tail -n +2 > /tmp/vagrant-network-interfaces.post") + expect(communicator).to receive(:sudo).with('/sbin/ifdown eth0 2> /dev/null') + expect(communicator).to receive(:sudo).with('/sbin/ip addr flush dev eth0 2> /dev/null') + expect(communicator).to receive(:sudo).with('/sbin/ifdown eth1 2> /dev/null') + expect(communicator).to receive(:sudo).with('/sbin/ip addr flush dev eth1 2> /dev/null') + expect(communicator).to receive(:sudo).with('cat /tmp/vagrant-network-interfaces.pre /tmp/vagrant-network-entry /tmp/vagrant-network-interfaces.post > /etc/network/interfaces') + expect(communicator).to receive(:sudo).with('rm -f /tmp/vagrant-network-interfaces.pre /tmp/vagrant-network-entry /tmp/vagrant-network-interfaces.post') + expect(communicator).to receive(:sudo).with('/sbin/ifup eth0') + expect(communicator).to receive(:sudo).with('/sbin/ifup eth1') + + allow_message_expectations_on_nil + + described_class.configure_networks(machine, networks) + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/alpine/cap/halt_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/alpine/cap/halt_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/alpine/cap/halt_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/alpine/cap/halt_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,23 @@ +require_relative "../../../../base" + +describe 'VagrantPlugins::GuestAlpine::Cap::Halt' do + let(:described_class) do + VagrantPlugins::GuestAlpine::Plugin.components.guest_capabilities[:alpine].get(:halt) + end + let(:machine) { double('machine') } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + + before do + allow(machine).to receive(:communicate).and_return(communicator) + end + + after do + communicator.verify_expectations! + end + + it 'should halt guest' do + expect(communicator).to receive(:sudo).with('poweroff') + allow_message_expectations_on_nil + described_class.halt(machine) + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/alpine/cap/nfs_client_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/alpine/cap/nfs_client_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/alpine/cap/nfs_client_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/alpine/cap/nfs_client_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,27 @@ +require_relative "../../../../base" + +describe 'VagrantPlugins::GuestAlpine::Cap::NFSClient' do + let(:described_class) do + VagrantPlugins::GuestAlpine::Plugin.components.guest_capabilities[:alpine].get(:nfs_client_install) + end + + let(:machine) { double('machine') } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + + before do + allow(machine).to receive(:communicate).and_return(communicator) + end + + after do + communicator.verify_expectations! + end + + it 'should install nfs client' do + described_class.nfs_client_install(machine) + + expect(communicator.received_commands[0]).to match(/apk update/) + expect(communicator.received_commands[1]).to match(/apk add --upgrade nfs-utils/) + expect(communicator.received_commands[2]).to match(/rc-update add rpc.statd/) + expect(communicator.received_commands[3]).to match(/rc-service rpc.statd start/) + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/alpine/cap/rsync_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/alpine/cap/rsync_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/alpine/cap/rsync_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/alpine/cap/rsync_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,36 @@ +require_relative "../../../../base" + +describe 'VagrantPlugins::GuestAlpine::Cap::RSync' do + let(:machine) { double('machine') } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + + before do + allow(machine).to receive(:communicate).and_return(communicator) + end + + after do + communicator.verify_expectations! + end + + let(:described_class) do + VagrantPlugins::GuestAlpine::Plugin.components.guest_capabilities[:alpine].get(:rsync_install) + end + + it 'should install rsync' do + # communicator.should_receive(:sudo).with('apk add rsync') + expect(communicator).to receive(:sudo).with('apk add rsync') + allow_message_expectations_on_nil + described_class.rsync_install(machine) + end + + let(:described_class) do + VagrantPlugins::GuestAlpine::Plugin.components.guest_capabilities[:alpine].get(:rsync_installed) + end + + it 'should verify rsync installed' do + # communicator.should_receive(:test).with('test -f /usr/bin/rsync') + expect(communicator).to receive(:test).with('test -f /usr/bin/rsync') + allow_message_expectations_on_nil + described_class.rsync_installed(machine) + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/alt/cap/change_host_name_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/alt/cap/change_host_name_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/alt/cap/change_host_name_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/alt/cap/change_host_name_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -20,17 +20,87 @@ describe ".change_host_name" do let(:cap) { caps.get(:change_host_name) } + let(:name) { 'banana-rama.example.com' } + let(:systemd) { true } + let(:hostnamectl) { true } + let(:networkd) { true } + let(:service) { true } + let(:network_manager) { false } - let(:name) { "banana-rama.example.com" } + before do + allow(cap).to receive(:systemd?).and_return(systemd) + allow(cap).to receive(:service?).and_return(service) + allow(cap).to receive(:hostnamectl?).and_return(hostnamectl) + allow(cap).to receive(:systemd_networkd?).and_return(networkd) + allow(cap).to receive(:systemd_controlled?).with(anything, /NetworkManager/).and_return(network_manager) + end - it "sets the hostname" do + it "sets the hostname if not set" do comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 1) - cap.change_host_name(machine, name) - expect(comm.received_commands[1]).to match(/\/etc\/sysconfig\/network/) - expect(comm.received_commands[1]).to match(/hostnamectl set-hostname --static '#{name}'/) - expect(comm.received_commands[1]).to match(/hostnamectl set-hostname --transient '#{name}'/) - expect(comm.received_commands[1]).to match(/service network restart/) + comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 0) + end + + context "when hostnamectl is in use" do + let(:hostnamectl) { true } + + it "sets hostname with hostnamectl" do + cap.change_host_name(machine, name) + comm.received_commands.find { |cmd| cmd =~ /^hostnamectl/ } + end + end + + context "when hostnamectl is not in use" do + let(:hostnamectl) { false } + + it "sets hostname with hostname command" do + cap.change_host_name(machine, name) + comm.received_commands.find { |cmd| cmd =~ /^hostname -F/ } + end + end + + context "restarts the network" do + context "when networkd is in use" do + let(:networkd) { true } + + it "restarts networkd with systemctl" do + cap.change_host_name(machine, name) + comm.received_commands.find { |cmd| cmd =~ /systemctl restart systemd-networkd/ } + end + end + + context "when NetworkManager is in use with systemctl" do + let(:networkd) { false } + let(:network_manager) { true } + + it "restarts NetworkManager with systemctl" do + cap.change_host_name(machine, name) + comm.received_commands.find { |cmd| cmd =~ /systemctl restart NetworkManager/ } + end + end + + context "when NetworkManager is in use without systemctl" do + let(:networkd) { false } + let(:network_manager) { true } + let(:systemd) { false } + + it "restarts NetworkManager without systemctl" do + cap.change_host_name(machine, name) + comm.received_commands.find { |cmd| cmd =~ /service NetworkManager restart/ } + end + end + + context "when systemd is not in use" do + let(:networkd) { false } + let(:network_manager) { false } + let(:systemd) { false } + + it "restarts networking with networking init script" do + cap.change_host_name(machine, name) + comm.received_commands.find { |cmd| cmd =~ /service networking restart/ } + end + end + end it "does not change the hostname if already set" do diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/bsd/cap/mount_virtual_box_shared_folder_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/bsd/cap/mount_virtual_box_shared_folder_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/bsd/cap/mount_virtual_box_shared_folder_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/bsd/cap/mount_virtual_box_shared_folder_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,41 @@ +require_relative "../../../../base" + +describe "VagrantPlugins::GuestBSD::Cap::MountVirtualBoxSharedFolder" do + let(:caps) do + VagrantPlugins::GuestBSD::Plugin + .components + .guest_capabilities[:bsd] + end + + let(:machine) { double("machine") } + let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + let(:mount_owner){ "vagrant" } + let(:mount_group){ "vagrant" } + let(:mount_uid){ "1000" } + let(:mount_gid){ "1000" } + let(:mount_name){ "vagrant" } + let(:mount_guest_path){ "/vagrant" } + let(:folder_options) do + { + owner: mount_owner, + group: mount_group, + hostpath: "/host/directory/path" + } + end + let(:cap){ caps.get(:mount_virtualbox_shared_folder) } + + before do + allow(machine).to receive(:communicate).and_return(comm) + end + + after do + comm.verify_expectations! + end + + describe ".mount_virtualbox_shared_folder" do + it "raises an error as unsupported" do + expect {cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options) }. + to raise_error(Vagrant::Errors::VirtualBoxMountNotSupportedBSD) + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/coreos/cap/change_host_name_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/coreos/cap/change_host_name_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/coreos/cap/change_host_name_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/coreos/cap/change_host_name_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -21,17 +21,47 @@ describe ".change_host_name" do let(:name) { "banana-rama.example.com" } + let(:has_cloudinit) { false } - it "sets the hostname" do - comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 1) - comm.expect_command("hostname 'banana-rama'") - described_class.change_host_name(machine, name) + before do + allow(described_class).to receive(:systemd_unit_file?). + with(anything, /cloudinit/).and_return(has_cloudinit) end - it "does not change the hostname if already set" do - comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 0) - described_class.change_host_name(machine, name) - expect(comm.received_commands.size).to eq(1) + context "with systemd cloud-init" do + let(:has_cloudinit) { true } + + it "should upload cloudinit configuration file" do + expect(comm).to receive(:upload) + described_class.change_host_name(machine, name) + end + + it "should set hostname in configuration file" do + expect(comm).to receive(:upload) do |src, dst| + contents = File.read(src) + expect(contents).to include(name) + end + described_class.change_host_name(machine, name) + end + + it "should start the cloudinit service" do + expect(comm).to receive(:sudo).with(/systemctl start system-cloudinit/) + described_class.change_host_name(machine, name) + end + end + + context "without systemd cloud-init" do + it "sets the hostname" do + comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 1) + comm.expect_command("hostname 'banana-rama'") + described_class.change_host_name(machine, name) + end + + it "does not change the hostname if already set" do + comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 0) + described_class.change_host_name(machine, name) + expect(comm.received_commands.size).to eq(1) + end end end end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/coreos/cap/configure_networks_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/coreos/cap/configure_networks_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/coreos/cap/configure_networks_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/coreos/cap/configure_networks_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -8,25 +8,20 @@ .get(:configure_networks) end - let(:machine) { double("machine") } - let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } - + let(:machine) { double("machine", config: config, guest: guest) } + let(:guest) { double("guest") } + let(:config) { double("config", vm: vm) } + let(:vm) { double("vm") } + # let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + let(:comm) { double("comm") } let(:env) do double("env", machine: machine, active_machines: [machine]) end + let(:interfaces) { ["eth0", "eth1", "lo"] } before do allow(machine).to receive(:communicate).and_return(comm) allow(machine).to receive(:env).and_return(env) - - allow(described_class).to receive(:get_ip).and_return("1.2.3.4") - - comm.stub_command("ifconfig | grep -E '(e[n,t][h,s,p][[:digit:]]([a-z][[:digit:]])?)' | cut -f1 -d:", - stdout: "eth1\neth2") - end - - after do - comm.verify_expectations! end describe ".configure_networks" do @@ -36,7 +31,9 @@ type: "dhcp", } end - + let(:netconfig_1) do + [:public_interface, {}] + end let(:network_2) do { interface: 1, @@ -46,14 +43,145 @@ gateway: "33.33.0.1", } end + let(:netconfig_2) do + [:public_network, {ip: "33.33.33.10", netmask: 16}] + end + let(:network_3) do + { + interface: 2, + type: "static", + ip: "192.168.120.22", + netmask: "255.255.255.0", + gateway: "192.168.120.1" + } + end + let(:netconfig_3) do + [:private_network, {ip: "192.168.120.22", netmask: 24}] + end + let(:networks) { [network_1, network_2, network_3] } + let(:network_configs) { [netconfig_1, netconfig_2, netconfig_3] } + let(:vm) { double("vm") } + let(:default_env_ip) { described_class.const_get(:DEFAULT_ENVIRONMENT_IP) } + + before do + allow(guest).to receive(:capability).with(:network_interfaces). + and_return(interfaces) + allow(vm).to receive(:networks).and_return(network_configs) + allow(comm).to receive(:upload) + allow(comm).to receive(:sudo) + end + + it "should upload network configuration file" do + expect(comm).to receive(:upload) + described_class.configure_networks(machine, networks) + end + + it "should configure public ipv4 address" do + expect(comm).to receive(:upload) do |src, dst| + content = File.read(src) + expect(content).to include("COREOS_PUBLIC_IPV4=#{netconfig_2.last[:ip]}") + end + described_class.configure_networks(machine, networks) + end + + it "should configure the private ipv4 address" do + expect(comm).to receive(:upload) do |src, dst| + content = File.read(src) + expect(content).to include("COREOS_PRIVATE_IPV4=#{netconfig_3.last[:ip]}") + end + described_class.configure_networks(machine, networks) + end + + it "should configure network interfaces" do + expect(comm).to receive(:upload) do |src, dst| + content = File.read(src) + interfaces.each { |i| expect(content).to include("Name=#{i}") } + end + described_class.configure_networks(machine, networks) + end + + it "should configure DHCP interface" do + expect(comm).to receive(:upload) do |src, dst| + content = File.read(src) + expect(content).to include("DHCP=yes") + end + described_class.configure_networks(machine, networks) + end + + it "should configure static IP addresses" do + expect(comm).to receive(:upload) do |src, dst| + content = File.read(src) + network_configs.map(&:last).find_all { |c| c[:ip] }.each { |c| + expect(content).to include("Address=#{c[:ip]}") + } + end + described_class.configure_networks(machine, networks) + end + + context "when no public network is defined" do + let(:networks) { [network_1, network_3] } + let(:network_configs) { [netconfig_1, netconfig_3] } + + + it "should set public IP to the default environment IP" do + expect(comm).to receive(:upload) do |src, dst| + content = File.read(src) + expect(content).to include("COREOS_PUBLIC_IPV4=#{default_env_ip}") + end + described_class.configure_networks(machine, networks) + end + + it "should set the private IP to the private network" do + expect(comm).to receive(:upload) do |src, dst| + content = File.read(src) + expect(content).to include("COREOS_PRIVATE_IPV4=#{netconfig_3.last[:ip]}") + end + described_class.configure_networks(machine, networks) + end + end + + context "when no private network is defined" do + let(:networks) { [network_1, network_2] } + let(:network_configs) { [netconfig_1, netconfig_2] } + + + it "should set public IP to the public network" do + expect(comm).to receive(:upload) do |src, dst| + content = File.read(src) + expect(content).to include("COREOS_PUBLIC_IPV4=#{netconfig_2.last[:ip]}") + end + described_class.configure_networks(machine, networks) + end + + it "should set the private IP to the public IP" do + expect(comm).to receive(:upload) do |src, dst| + content = File.read(src) + expect(content).to include("COREOS_PRIVATE_IPV4=#{netconfig_2.last[:ip]}") + end + described_class.configure_networks(machine, networks) + end + end - it "creates and starts the networks" do - described_class.configure_networks(machine, [network_1, network_2]) - expect(comm.received_commands[1]).to match(/systemctl stop etcd/) - expect(comm.received_commands[1]).to match(/ifconfig eth1 netmask/) - expect(comm.received_commands[1]).to match(/ifconfig eth2 33.33.33.10 netmask 255.255.0.0/) - expect(comm.received_commands[1]).to match(/systemctl restart local-enable.service/) - expect(comm.received_commands[1]).to match(/systemctl start etcd/) + context "when no public or private network is defined" do + let(:networks) { [network_1] } + let(:network_configs) { [netconfig_1] } + + + it "should set public IP to the default environment IP" do + expect(comm).to receive(:upload) do |src, dst| + content = File.read(src) + expect(content).to include("COREOS_PUBLIC_IPV4=#{default_env_ip}") + end + described_class.configure_networks(machine, networks) + end + + it "should set the private IP to the default environment IP" do + expect(comm).to receive(:upload) do |src, dst| + content = File.read(src) + expect(content).to include("COREOS_PRIVATE_IPV4=#{default_env_ip}") + end + described_class.configure_networks(machine, networks) + end end end end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/debian/cap/configure_networks_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/debian/cap/configure_networks_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/debian/cap/configure_networks_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/debian/cap/configure_networks_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -101,6 +101,9 @@ end context "with systemd-networkd" do + let(:net_conf_dhcp) { "[Match]\nName=eth1\n[Network]\nDHCP=yes" } + let(:net_conf_static) { "[Match]\nName=eth2\n[Network]\nDHCP=no\nAddress=33.33.33.10/16\nGateway=33.33.0.1" } + before do expect(comm).to receive(:test).with("systemctl -q is-active systemd-networkd.service", anything).and_return(true) end @@ -113,6 +116,19 @@ expect(comm.received_commands[0]).to match("chmod") expect(comm.received_commands[2]).to match("systemctl restart") end + + it "properly configures DHCP and static IPs if defined" do + expect(cap).to receive(:upload_tmp_file).with(comm, net_conf_dhcp) + expect(cap).to receive(:upload_tmp_file).with(comm, net_conf_static) + + cap.configure_networks(machine, [network_0, network_1]) + + expect(comm.received_commands[0]).to match("mkdir -p /etc/systemd/network") + expect(comm.received_commands[0]).to match("mv -f '' '/etc/systemd/network/50-vagrant-eth1.network'") + expect(comm.received_commands[0]).to match("chown root:root '/etc/systemd/network/50-vagrant-eth1.network'") + expect(comm.received_commands[0]).to match("chmod 0644 '/etc/systemd/network/50-vagrant-eth1.network'") + expect(comm.received_commands[2]).to match("systemctl restart") + end end context "with netplan" do diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/freebsd/cap/configure_networks_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/freebsd/cap/configure_networks_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/freebsd/cap/configure_networks_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/freebsd/cap/configure_networks_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -13,8 +13,7 @@ before do allow(machine).to receive(:communicate).and_return(comm) - comm.stub_command("ifconfig -a | grep -o '^[0-9a-z]*' | grep -v '^lo'", - stdout: "em1\nem2") + comm.stub_command("ifconfig -l ether", stdout: "em1 em2") end after do diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/freebsd/cap/mount_virtual_box_shared_folder_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/freebsd/cap/mount_virtual_box_shared_folder_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/freebsd/cap/mount_virtual_box_shared_folder_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/freebsd/cap/mount_virtual_box_shared_folder_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,225 @@ +require_relative "../../../../base" + +describe "VagrantPlugins::GuestFreeBSD::Cap::MountVirtualBoxSharedFolder" do + let(:caps) do + VagrantPlugins::GuestFreeBSD::Plugin + .components + .guest_capabilities[:freebsd] + end + + let(:machine) { double("machine") } + let(:comm) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + let(:mount_owner){ "vagrant" } + let(:mount_group){ "vagrant" } + let(:mount_uid){ "1000" } + let(:mount_gid){ "1000" } + let(:mount_name){ "vagrant" } + let(:mount_guest_path){ "/vagrant" } + let(:folder_options) do + { + owner: mount_owner, + group: mount_group, + hostpath: "/host/directory/path" + } + end + let(:cap){ caps.get(:mount_virtualbox_shared_folder) } + + before do + allow(machine).to receive(:communicate).and_return(comm) + end + + after do + comm.verify_expectations! + end + + describe ".mount_virtualbox_shared_folder" do + + before do + allow(comm).to receive(:sudo).with(any_args) + allow(comm).to receive(:execute).with(any_args) + end + + it "generates the expected default mount command" do + expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid) + expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:") + expect(comm).to receive(:sudo).with("mount -t vboxvfs -o uid=#{mount_uid},gid=#{mount_gid} #{mount_name} #{mount_guest_path}", anything) + cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options) + end + + it "automatically chown's the mounted directory on guest" do + expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid) + expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:") + expect(comm).to receive(:sudo).with("mount -t vboxvfs -o uid=#{mount_uid},gid=#{mount_gid} #{mount_name} #{mount_guest_path}", anything) + expect(comm).to receive(:sudo).with("chown #{mount_uid}:#{mount_gid} #{mount_guest_path}") + cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options) + end + + context "with owner user ID explicitly defined" do + + before do + expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:") + end + + context "with user ID provided as Integer" do + let(:mount_owner){ 2000 } + + it "generates the expected mount command using mount_owner directly" do + expect(comm).to receive(:sudo).with("mount -t vboxvfs -o uid=#{mount_owner},gid=#{mount_gid} #{mount_name} #{mount_guest_path}", anything) + expect(comm).to receive(:sudo).with("chown #{mount_owner}:#{mount_gid} #{mount_guest_path}") + cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options) + end + end + + context "with user ID provided as String" do + let(:mount_owner){ "2000" } + + it "generates the expected mount command using mount_owner directly" do + expect(comm).to receive(:sudo).with("mount -t vboxvfs -o uid=#{mount_owner},gid=#{mount_gid} #{mount_name} #{mount_guest_path}", anything) + expect(comm).to receive(:sudo).with("chown #{mount_owner}:#{mount_gid} #{mount_guest_path}") + cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options) + end + end + + end + + context "with owner group ID explicitly defined" do + + before do + expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid) + end + + context "with owner group ID provided as Integer" do + let(:mount_group){ 2000 } + + it "generates the expected mount command using mount_group directly" do + expect(comm).to receive(:sudo).with("mount -t vboxvfs -o uid=#{mount_uid},gid=#{mount_group} #{mount_name} #{mount_guest_path}", anything) + expect(comm).to receive(:sudo).with("chown #{mount_uid}:#{mount_group} #{mount_guest_path}") + cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options) + end + end + + context "with owner group ID provided as String" do + let(:mount_group){ "2000" } + + it "generates the expected mount command using mount_group directly" do + expect(comm).to receive(:sudo).with("mount -t vboxvfs -o uid=#{mount_uid},gid=#{mount_group} #{mount_name} #{mount_guest_path}", anything) + expect(comm).to receive(:sudo).with("chown #{mount_uid}:#{mount_group} #{mount_guest_path}") + cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options) + end + end + + end + + context "with non-existent default owner group" do + + it "fetches the effective group ID of the user" do + expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid) + expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_raise(Vagrant::Errors::VirtualBoxMountFailed, {command: '', output: ''}) + expect(comm).to receive(:execute).with("id -g #{mount_owner}", anything).and_yield(:stdout, "1").and_return(0) + cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options) + end + end + + context "with non-existent owner group" do + + it "raises an error" do + expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid) + expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_raise(Vagrant::Errors::VirtualBoxMountFailed, {command: '', output: ''}) + expect do + cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options) + end.to raise_error Vagrant::Errors::VirtualBoxMountFailed + end + end + + context "with read-only option defined" do + + it "does not chown mounted guest directory" do + expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid) + expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:") + expect(comm).to receive(:sudo).with("mount -t vboxvfs -o ro,uid=#{mount_uid},gid=#{mount_gid} #{mount_name} #{mount_guest_path}", anything) + expect(comm).not_to receive(:sudo).with("chown #{mount_uid}:#{mount_gid} #{mount_guest_path}") + cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options.merge(mount_options: ["ro"])) + end + end + + context "with upstart init" do + + it "emits mount event" do + expect(comm).to receive(:sudo).with(/initctl emit/) + cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options) + end + end + + context "with custom mount options" do + + let(:ui){ double(:ui) } + before do + allow(ui).to receive(:warn) + allow(machine).to receive(:ui).and_return(ui) + end + + context "with uid defined" do + let(:options_uid){ '1234' } + + it "should only include uid defined within mount options" do + expect(comm).not_to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid) + expect(comm).to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:") + expect(comm).to receive(:sudo).with("mount -t vboxvfs -o uid=#{options_uid},gid=#{mount_gid} #{mount_name} #{mount_guest_path}", anything) + cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options.merge(mount_options: ["uid=#{options_uid}"])) + end + end + + context "with gid defined" do + let(:options_gid){ '1234' } + + it "should only include gid defined within mount options" do + expect(comm).to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid) + expect(comm).not_to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{mount_gid}:") + expect(comm).to receive(:sudo).with("mount -t vboxvfs -o uid=#{mount_uid},gid=#{options_gid} #{mount_name} #{mount_guest_path}", anything) + cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options.merge(mount_options: ["gid=#{options_gid}"])) + end + end + + context "with uid and gid defined" do + let(:options_gid){ '1234' } + let(:options_uid){ '1234' } + + it "should only include uid and gid defined within mount options" do + expect(comm).not_to receive(:execute).with("id -u #{mount_owner}", anything).and_yield(:stdout, mount_uid) + expect(comm).not_to receive(:execute).with("getent group #{mount_group}", anything).and_yield(:stdout, "vagrant:x:#{options_gid}:") + expect(comm).to receive(:sudo).with("mount -t vboxvfs -o uid=#{options_uid},gid=#{options_gid} #{mount_name} #{mount_guest_path}", anything) + cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options.merge(mount_options: ["gid=#{options_gid}", "uid=#{options_uid}"])) + end + end + end + + context "with guest builtin vboxvfs module" do + let(:vbox_stderr){ <<-EOF +mount.vboxvfs cannot be used with mainline vboxvfs; instead use: + + mount -cit vboxvfs NAME MOUNTPOINT +EOF + } + it "should perform guest mount using builtin module" do + expect(comm).to receive(:sudo).with(/mount -t vboxvfs/, any_args).and_yield(:stderr, vbox_stderr).and_return(1) + expect(comm).to receive(:sudo).with(/mount -cit/, any_args) + cap.mount_virtualbox_shared_folder(machine, mount_name, mount_guest_path, folder_options) + end + end + end + + describe ".unmount_virtualbox_shared_folder" do + + after { cap.unmount_virtualbox_shared_folder(machine, mount_guest_path, folder_options) } + + it "unmounts shared directory and deletes directory on guest" do + expect(comm).to receive(:sudo).with("umount #{mount_guest_path}", anything).and_return(0) + expect(comm).to receive(:sudo).with("rmdir #{mount_guest_path}", anything) + end + + it "does not delete guest directory if unmount fails" do + expect(comm).to receive(:sudo).with("umount #{mount_guest_path}", anything).and_return(1) + expect(comm).not_to receive(:sudo).with("rmdir #{mount_guest_path}", anything) + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/suse/cap/change_host_name_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/suse/cap/change_host_name_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/suse/cap/change_host_name_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/suse/cap/change_host_name_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -25,15 +25,15 @@ let(:basename) { "banana-rama" } it "sets the hostname" do - comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 1) + comm.stub_command("getent hosts '#{name}'", exit_code: 1) cap.change_host_name(machine, name) - expect(comm.received_commands[1]).to match(/echo '#{basename}' > \/etc\/HOSTNAME/) - expect(comm.received_commands[1]).to match(/hostname '#{basename}'/) + expect(comm.received_commands[1]).to match(/hostnamectl set-hostname '#{basename}'/) end it "does not change the hostname if already set" do - comm.stub_command("hostname -f | grep '^#{name}$'", exit_code: 0) + comm.stub_command("getent hosts '#{name}'", exit_code: 0) + cap.change_host_name(machine, name) expect(comm.received_commands.size).to eq(1) end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/suse/cap/nfs_client_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/suse/cap/nfs_client_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/suse/cap/nfs_client_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/suse/cap/nfs_client_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -24,8 +24,8 @@ it "installs nfs client utilities" do cap.nfs_client_install(machine) expect(comm.received_commands[0]).to match(/zypper -n install nfs-client/) - expect(comm.received_commands[0]).to match(/service rpcbind restart/) - expect(comm.received_commands[0]).to match(/service nfs restart/) + expect(comm.received_commands[0]).to match(/systemctl restart rpcbind/) + expect(comm.received_commands[0]).to match(/systemctl restart nfs-client.target/) end end end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/guests/windows/cap/reboot_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/guests/windows/cap/reboot_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/guests/windows/cap/reboot_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/guests/windows/cap/reboot_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -8,9 +8,10 @@ end let(:vm) { double("vm") } let(:config) { double("config") } - let(:machine) { double("machine") } + let(:machine) { double("machine", ui: ui) } let(:guest) { double("guest") } let(:communicator) { double("communicator") } + let(:ui) { double("ui") } before do allow(machine).to receive(:communicate).and_return(communicator) @@ -18,6 +19,7 @@ allow(machine.guest).to receive(:ready?).and_return(true) allow(machine).to receive(:config).and_return(config) allow(config).to receive(:vm).and_return(vm) + allow(ui).to receive(:info) end describe ".reboot" do @@ -26,13 +28,27 @@ end it "reboots the vm" do + allow(communicator).to receive(:execute) + + expect(communicator).to receive(:test).with(/# Function/, { error_check: false, shell: :powershell }).and_return(0) + expect(communicator).to receive(:execute).with(/shutdown/, { shell: :powershell }).and_return(0) + expect(described_class).to receive(:wait_for_reboot) + + described_class.reboot(machine) + end + + context "user output" do + before do allow(communicator).to receive(:execute) + allow(described_class).to receive(:wait_for_reboot) + end - expect(communicator).to receive(:test).with(/# Function/, { error_check: false, shell: :powershell }).and_return(0) - expect(communicator).to receive(:execute).with(/shutdown/, { shell: :powershell }).and_return(0) - expect(described_class).to receive(:wait_for_reboot) + after { described_class.reboot(machine) } - described_class.reboot(machine) + it "sends message to user that guest is rebooting" do + expect(communicator).to receive(:test).and_return(true) + expect(ui).to receive(:info) + end end end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/hosts/bsd/cap/nfs_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/hosts/bsd/cap/nfs_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/hosts/bsd/cap/nfs_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/hosts/bsd/cap/nfs_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,48 @@ +require_relative "../../../../base" +require_relative "../../../../../../plugins/hosts/bsd/cap/nfs" + +describe VagrantPlugins::HostBSD::Cap::NFS do + + include_context "unit" + + describe ".nfs_export" do + let(:environment) { double("environment", host: host) } + let(:host) { double("host") } + let(:ui) { double("ui") } + let(:id) { "UUID" } + let(:ips) { [] } + let(:folders) { {} } + + before do + allow(host).to receive(:capability).and_return("") + allow(Vagrant::Util::TemplateRenderer).to receive(:render).and_return("") + allow(described_class).to receive(:sleep) + allow(described_class).to receive(:nfs_cleanup) + allow(described_class).to receive(:system) + allow(File).to receive(:writable?).with("/etc/exports") + allow(ui).to receive(:info) + end + + it "should execute successfully when no folders are defined" do + expect { described_class.nfs_export(environment, ui, id, ips, folders) }. + not_to raise_error + end + + context "with single folder defined" do + let(:folders) { + {"/vagrant" => { + type: :nfs, guestpath: "/vagrant", hostpath: "/Users/vagrant/paths", disabled: false}} + } + + it "should execute successfully" do + expect { described_class.nfs_export(environment, ui, id, ips, folders) }. + not_to raise_error + end + + it "should resolve the host path" do + expect(host).to receive(:capability).with(:resolve_host_path, folders["/vagrant"][:hostpath]).and_return("") + described_class.nfs_export(environment, ui, id, ips, folders) + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/hosts/bsd/cap/path_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/hosts/bsd/cap/path_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/hosts/bsd/cap/path_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/hosts/bsd/cap/path_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,13 @@ +require_relative "../../../../base" +require_relative "../../../../../../plugins/hosts/bsd/cap/path" + +describe VagrantPlugins::HostBSD::Cap::Path do + describe ".resolve_host_path" do + let(:env) { double("environment") } + let(:path) { double("path") } + + it "should return the path object provided" do + expect(described_class.resolve_host_path(env, path)).to eq(path) + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/hosts/darwin/cap/path_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/hosts/darwin/cap/path_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/hosts/darwin/cap/path_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/hosts/darwin/cap/path_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,89 @@ +require_relative "../../../../base" +require_relative "../../../../../../plugins/hosts/darwin/cap/path" + +describe VagrantPlugins::HostDarwin::Cap::Path do + describe ".resolve_host_path" do + let(:env) { double("environment") } + let(:path) { "/test/vagrant/path" } + let(:firmlink_map) { {} } + + before { allow(described_class).to receive(:firmlink_map).and_return(firmlink_map) } + + it "should not change the path when no firmlinks are defined" do + expect(described_class.resolve_host_path(env, path)).to eq(path) + end + + context "when firmlink map contains non-matching values" do + let(:firmlink_map) { {"/users" => "users", "/system" => "system"} } + + it "should not change the path" do + expect(described_class.resolve_host_path(env, path)).to eq(path) + end + end + + context "when firmlink map contains matching value" do + let(:firmlink_map) { {"/users" => "users", "/test" => "test"} } + + it "should update the path" do + expect(described_class.resolve_host_path(env, path)).not_to eq(path) + end + + it "should prefix the path with the defined data path" do + expect(described_class.resolve_host_path(env, path)).to start_with(described_class.const_get(:FIRMLINK_DATA_PATH)) + end + end + + context "when firmlink map match points to different named target" do + let(:firmlink_map) { {"/users" => "users", "/test" => "other"} } + + it "should update the path" do + expect(described_class.resolve_host_path(env, path)).not_to eq(path) + end + + it "should prefix the path with the defined data path" do + expect(described_class.resolve_host_path(env, path)).to start_with(described_class.const_get(:FIRMLINK_DATA_PATH)) + end + + it "should include the updated path name" do + expect(described_class.resolve_host_path(env, path)).to include("other") + end + end + end + + describe ".firmlink_map" do + before { described_class.reset! } + + context "when firmlink definition file does not exist" do + before { expect(File).to receive(:exist?).with(described_class.const_get(:FIRMLINK_DEFS)).and_return(false) } + + it "should return an empty hash" do + expect(described_class.firmlink_map).to eq({}) + end + end + + context "when firmlink definition file exists with values" do + before do + expect(File).to receive(:exist?).with(described_class.const_get(:FIRMLINK_DEFS)).and_return(true) + expect(File).to receive(:readlines).with.(described_class.const_get(:FIRMLINK_DEFS)). + and_return(["/System\tSystem\n", "/Users\tUsers\n", "/Library/Something\tLibrary/Somethingelse"]) + + it "should generate a non-empty hash" do + expect(described_class.firmlink_map).not_to be_empty + end + + it "should properly create entries" do + result = described_class.firmlink_map + expect(result["/System"]).to eq("System") + expect(result["/Users"]).to eq("Users") + expect(result["/Library/Something"]).to eq("Library/Somethingelse") + end + + it "should only load values once" do + result = describe_class.firmlink_app + expect(File).not_to receive(:readlines) + result = describe_class.firmlink_app + end + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/hosts/void/cap/nfs_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/hosts/void/cap/nfs_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/hosts/void/cap/nfs_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/hosts/void/cap/nfs_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -39,7 +39,7 @@ let(:result) { Vagrant::Util::Subprocess::Result.new(exit_code, "", "") } before { allow(Vagrant::Util::Subprocess).to receive(:execute). - with(/xbps-query nfs-utils/).and_return(result) } + with("/usr/bin/xbps-query", "nfs-utils").and_return(result) } it "should provide nfs_installed capability" do expect(caps.get(:nfs_installed)).to eq(described_class) diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/kernel_v2/config/vagrant_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/kernel_v2/config/vagrant_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/kernel_v2/config/vagrant_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/kernel_v2/config/vagrant_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -56,4 +56,54 @@ subject.finalize! end end + + describe "#plugins" do + it "converts string into hash of plugins" do + subject.plugins = "vagrant-plugin" + subject.finalize! + expect(subject.plugins).to be_a(Hash) + end + + it "converts array of strings into hash of plugins" do + subject.plugins = ["vagrant-plugin", "vagrant-other-plugin"] + subject.finalize! + expect(subject.plugins).to be_a(Hash) + expect(subject.plugins.keys).to eq(["vagrant-plugin", "vagrant-other-plugin"]) + end + + it "does not convert hash" do + plugins = {"vagrant-plugin" => {}} + subject.plugins = plugins + subject.finalize + expect(subject.plugins).to eq(plugins) + end + + it "converts array of mixed strings and hashes" do + subject.plugins = ["vagrant-plugin", {"vagrant-other-plugin" => {:version => "1"}}] + subject.finalize! + expect(subject.plugins["vagrant-plugin"]).to eq({}) + expect(subject.plugins["vagrant-other-plugin"]).to eq({"version" => "1"}) + end + + it "generates a validation error when incorrect type is provided" do + subject.plugins = 0 + subject.finalize! + result = subject.validate(machine) + expect(result.values).not_to be_empty + end + + it "generates a validation error when invalid option is provided" do + subject.plugins = {"vagrant-plugin" => {"badkey" => true}} + subject.finalize! + result = subject.validate(machine) + expect(result.values).not_to be_empty + end + + it "generates a validation error when options are incorrect type" do + subject.plugins = {"vagrant-plugin" => 1} + subject.finalize! + result = subject.validate(machine) + expect(result.values).not_to be_empty + end + end end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/kernel_v2/config/vm_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/kernel_v2/config/vm_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/kernel_v2/config/vm_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/kernel_v2/config/vm_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -52,6 +52,13 @@ end end + describe "#base_address" do + it "defaults properly" do + subject.finalize! + expect(subject.base_address).to be_nil + end + end + describe "#box" do it "is required" do subject.box = nil diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/kernel_v2/config/vm_trigger_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/kernel_v2/config/vm_trigger_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/kernel_v2/config/vm_trigger_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/kernel_v2/config/vm_trigger_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -71,6 +71,7 @@ cfg.only_on = :guest cfg.ignore = "up" cfg.abort = true + cfg.type = "action" cfg.ruby do var = 1+1 end @@ -112,6 +113,11 @@ expect(cfg.abort).to eq(1) end + + it "converts types to symbols" do + cfg.finalize! + expect(cfg.type).to eq(:action) + end end describe "defining a basic trigger config" do diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/action/compare_synced_folders_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/action/compare_synced_folders_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/action/compare_synced_folders_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/action/compare_synced_folders_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,89 @@ +require_relative "../../../../base" +require_relative "../../../../../../plugins/providers/docker/action/compare_synced_folders" + +describe VagrantPlugins::DockerProvider::Action::CompareSyncedFolders do + include_context "unit" + include_context "virtualbox" + + let(:sandbox) { isolated_environment } + + let(:iso_env) do + # We have to create a Vagrantfile so there is a root path + sandbox.vagrantfile("") + sandbox.create_vagrant_env + end + + let(:machine) do + iso_env.machine(iso_env.machine_names[0], :virtualbox).tap do |m| + allow(m.provider).to receive(:driver).and_return(driver) + end + end + + let(:env) {{ machine: machine, ui: machine.ui, root_path: Pathname.new(".") }} + let(:app) { lambda { |*args| }} + let(:driver) { double("driver") } + + subject { described_class.new(app, env) } + + after do + sandbox.close + end + + describe "#call" do + let(:cached) { {:docker=>{"/vagrant"=>{:guestpath=>"/vagrant", :hostpath=>"/home/hashicorp/code/vagrant-sandbox", :disabled=>false, :__vagrantfile=>true}}} } + let(:fresh) { {:docker=>{"/vagrant"=>{:guestpath=>"/vagrant", :hostpath=>".", :disabled=>false, :__vagrantfile=>true}}} } + + let(:existing) { {"/vagrant"=>"/home/hashicorp/code/vagrant-sandbox"} } + + + it "calls the next action in the chain" do + allow(machine.provider).to receive(:host_vm?).and_return(false) + called = false + app = ->(*args) { called = true } + + action = described_class.new(app, env) + action.call(env) + + expect(called).to eq(true) + end + + context "invalid or existing entries" do + let(:cached) { {:docker=>{"/vagrant"=>{:guestpath=>"/not-real", :hostpath=>"/home/hashicorp/code/vagrant-sandbox", :disabled=>false, :__vagrantfile=>true}}} } + let(:fresh) { {:docker=>{"/vagrant"=>{:guestpath=>"/vagrant", :hostpath=>".", :disabled=>false, :__vagrantfile=>true}}} } + it "shows a warning" do + allow(machine.provider).to receive(:host_vm?).and_return(false) + + called = false + app = ->(*args) { called = true } + action = described_class.new(app, env) + + expect(action).to receive(:synced_folders). + with(machine, cached: true).and_return(cached) + expect(action).to receive(:synced_folders). + with(machine).and_return(fresh) + + expect(machine.ui).to receive(:warn) + + action.call(env) + expect(called).to eq(true) + end + end + + it "shows no warning comparing synced folders" do + allow(machine.provider).to receive(:host_vm?).and_return(false) + + called = false + app = ->(*args) { called = true } + action = described_class.new(app, env) + + expect(action).to receive(:synced_folders). + with(machine, cached: true).and_return(cached) + expect(action).to receive(:synced_folders). + with(machine).and_return(fresh) + + action.call(env) + expect(machine.ui).not_to receive(:warn) + expect(called).to eq(true) + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/action/connect_networks_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/action/connect_networks_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/action/connect_networks_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/action/connect_networks_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,151 @@ +require_relative "../../../../base" +require_relative "../../../../../../plugins/providers/docker/action/connect_networks" + + +describe VagrantPlugins::DockerProvider::Action::ConnectNetworks do + include_context "unit" + + let(:sandbox) { isolated_environment } + + let(:iso_env) do + # We have to create a Vagrantfile so there is a root path + sandbox.vagrantfile("") + sandbox.create_vagrant_env + end + + let(:vm_config) { double("machine_vm_config") } + + let(:machine_config) do + double("machine_config").tap do |top_config| + allow(top_config).to receive(:vm).and_return(vm_config) + end + end + + let(:machine) do + iso_env.machine(iso_env.machine_names[0], :docker).tap do |m| + allow(m).to receive(:vagrantfile).and_return(vagrantfile) + allow(m).to receive(:config).and_return(machine_config) + allow(m).to receive(:id).and_return("12345") + allow(m.provider).to receive(:driver).and_return(driver) + allow(m.provider).to receive(:host_vm?).and_return(false) + allow(m.config.vm).to receive(:networks).and_return(networks) + end + end + + let(:docker_connects) { {0=>"vagrant_network_172.20.0.0/16", 1=>"vagrant_network_public_wlp4s0", 2=>"vagrant_network_2a02:6b8:b010:9020:1::/80"} } + + let(:vagrantfile) { double("vagrantfile") } + + let(:env) {{ machine: machine, ui: machine.ui, root_path: Pathname.new("."), + docker_connects: docker_connects, vagrantfile: vagrantfile }} + let(:app) { lambda { |*args| }} + let(:driver) { double("driver", create: "abcd1234") } + + let(:networks) { [[:private_network, + {:ip=>"172.20.128.2", + :subnet=>"172.20.0.0/16", + :driver=>"bridge", + :internal=>"true", + :alias=>"mynetwork", + :protocol=>"tcp", + :id=>"80e017d5-388f-4a2f-a3de-f8dce8156a58"}], + [:public_network, + {:ip=>"172.30.130.2", + :subnet=>"172.30.0.0/16", + :driver=>"bridge", + :id=>"30e017d5-488f-5a2f-a3ke-k8dce8246b60"}], + [:private_network, + {:type=>"dhcp", + :ipv6=>"true", + :subnet=>"2a02:6b8:b010:9020:1::/80", + :protocol=>"tcp", + :id=>"b8f23054-38d5-45c3-99ea-d33fc5d1b9f2"}], + [:forwarded_port, + {:guest=>22, :host=>2200, :host_ip=>"127.0.0.1", :id=>"ssh", :auto_correct=>true, :protocol=>"tcp"}]] + } + + subject { described_class.new(app, env) } + + let(:subprocess_result) do + double("subprocess_result").tap do |result| + allow(result).to receive(:exit_code).and_return(0) + allow(result).to receive(:stdout).and_return("") + allow(result).to receive(:stderr).and_return("") + end + end + + before do + allow(Vagrant::Util::Subprocess).to receive(:execute).with("docker", "version", an_instance_of(Hash)).and_return(subprocess_result) + end + + after do + sandbox.close + end + + describe "#call" do + it "calls the next action in the chain" do + allow(driver).to receive(:host_vm?).and_return(false) + allow(driver).to receive(:connect_network).and_return(true) + + called = false + app = ->(*args) { called = true } + + action = described_class.new(app, env) + + action.call(env) + + expect(called).to eq(true) + end + + it "connects all of the avaiable networks to a container" do + expect(driver).to receive(:connect_network).with("vagrant_network_172.20.0.0/16", "12345", ["--ip", "172.20.128.2", "--alias", "mynetwork"]) + expect(driver).to receive(:connect_network).with("vagrant_network_public_wlp4s0", "12345", ["--ip", "172.30.130.2"]) + expect(driver).to receive(:connect_network).with("vagrant_network_2a02:6b8:b010:9020:1::/80", "12345", []) + + subject.call(env) + end + + context "with missing env values" do + it "raises an error if the network name is missing" do + env[:docker_connects] = {} + + expect{subject.call(env)}.to raise_error(VagrantPlugins::DockerProvider::Errors::NetworkNameMissing) + end + end + end + + describe "#generate_connect_cli_arguments" do + let(:network_options) { + {:ip=>"172.20.128.2", + :subnet=>"172.20.0.0/16", + :driver=>"bridge", + :internal=>"true", + :alias=>"mynetwork", + :protocol=>"tcp", + :id=>"80e017d5-388f-4a2f-a3de-f8dce8156a58"} } + + let(:false_network_options) { + {:ip=>"172.20.128.2", + :subnet=>"172.20.0.0/16", + :driver=>"bridge", + :internal=>"false", + :alias=>"mynetwork", + :protocol=>"tcp", + :id=>"80e017d5-388f-4a2f-a3de-f8dce8156a58"} } + + it "removes false values" do + cli_args = subject.generate_connect_cli_arguments(false_network_options) + expect(cli_args).to eq(["--ip", "172.20.128.2", "--subnet", "172.20.0.0/16", "--driver", "bridge", "--alias", "mynetwork", "--protocol", "tcp", "--id", "80e017d5-388f-4a2f-a3de-f8dce8156a58"]) + end + + it "removes true and leaves flag value in arguments" do + cli_args = subject.generate_connect_cli_arguments(network_options) + expect(cli_args).to eq(["--ip", "172.20.128.2", "--subnet", "172.20.0.0/16", "--driver", "bridge", "--internal", "--alias", "mynetwork", "--protocol", "tcp", "--id", "80e017d5-388f-4a2f-a3de-f8dce8156a58"]) + end + + it "takes options and generates cli flags" do + cli_args = subject.generate_connect_cli_arguments(network_options) + expect(cli_args).to eq(["--ip", "172.20.128.2", "--subnet", "172.20.0.0/16", "--driver", "bridge", "--internal", "--alias", "mynetwork", "--protocol", "tcp", "--id", "80e017d5-388f-4a2f-a3de-f8dce8156a58"]) + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/action/destroy_network_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/action/destroy_network_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/action/destroy_network_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/action/destroy_network_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,128 @@ +require_relative "../../../../base" +require_relative "../../../../../../plugins/providers/docker/action/destroy_network" + +describe VagrantPlugins::DockerProvider::Action::DestroyNetwork do + include_context "unit" + + let(:sandbox) { isolated_environment } + + let(:iso_env) do + # We have to create a Vagrantfile so there is a root path + sandbox.vagrantfile("") + sandbox.create_vagrant_env + end + + let(:vm_config) { double("machine_vm_config") } + + let(:machine_config) do + double("machine_config").tap do |top_config| + allow(top_config).to receive(:vm).and_return(vm_config) + end + end + + let(:machine) do + iso_env.machine(iso_env.machine_names[0], :docker).tap do |m| + allow(m).to receive(:vagrantfile).and_return(vagrantfile) + allow(m).to receive(:config).and_return(machine_config) + allow(m.provider).to receive(:driver).and_return(driver) + allow(m.config.vm).to receive(:networks).and_return(networks) + end + end + + let(:vagrantfile) { double("vagrantfile") } + + let(:env) {{ machine: machine, ui: machine.ui, root_path: Pathname.new("."), vagrantfile: vagrantfile }} + let(:app) { lambda { |*args| }} + let(:driver) { double("driver", create: "abcd1234") } + + let(:networks) { [[:private_network, + {:ip=>"172.20.128.2", + :subnet=>"172.20.0.0/16", + :driver=>"bridge", + :internal=>"true", + :alias=>"mynetwork", + :protocol=>"tcp", + :id=>"80e017d5-388f-4a2f-a3de-f8dce8156a58"}], + [:private_network, + {:type=>"dhcp", + :ipv6=>"true", + :subnet=>"2a02:6b8:b010:9020:1::/80", + :protocol=>"tcp", + :id=>"b8f23054-38d5-45c3-99ea-d33fc5d1b9f2"}], + [:forwarded_port, + {:guest=>22, :host=>2200, :host_ip=>"127.0.0.1", :id=>"ssh", :auto_correct=>true, :protocol=>"tcp"}]] + } + + subject { described_class.new(app, env) } + + let(:subprocess_result) do + double("subprocess_result").tap do |result| + allow(result).to receive(:exit_code).and_return(0) + allow(result).to receive(:stdout).and_return("") + allow(result).to receive(:stderr).and_return("") + end + end + + before do + allow(Vagrant::Util::Subprocess).to receive(:execute).with("docker", "version", an_instance_of(Hash)).and_return(subprocess_result) + end + + after do + sandbox.close + end + + describe "#call" do + let(:network_names) { ["vagrant_network_172.20.0.0/16", "vagrant_network_2a02:6b8:b010:9020:1::/80"] } + + it "calls the next action in the chain" do + allow(driver).to receive(:host_vm?).and_return(false) + allow(driver).to receive(:existing_network?).and_return(true) + allow(driver).to receive(:network_used?).and_return(true) + allow(driver).to receive(:list_network_names).and_return([]) + + called = false + app = ->(*args) { called = true } + + action = described_class.new(app, env) + action.call(env) + + expect(called).to eq(true) + end + + it "calls the proper driver method to destroy the network" do + allow(driver).to receive(:list_network_names).and_return(network_names) + allow(driver).to receive(:host_vm?).and_return(false) + allow(driver).to receive(:existing_named_network?).with("vagrant_network_172.20.0.0/16"). + and_return(true) + allow(driver).to receive(:network_used?).with("vagrant_network_172.20.0.0/16"). + and_return(false) + allow(driver).to receive(:existing_named_network?).with("vagrant_network_2a02:6b8:b010:9020:1::/80"). + and_return(true) + allow(driver).to receive(:network_used?).with("vagrant_network_2a02:6b8:b010:9020:1::/80"). + and_return(false) + + expect(driver).to receive(:rm_network).with("vagrant_network_172.20.0.0/16").twice + expect(driver).to receive(:rm_network).with("vagrant_network_2a02:6b8:b010:9020:1::/80").twice + + subject.call(env) + end + + it "doesn't destroy the network if another container is still using it" do + allow(driver).to receive(:host_vm?).and_return(false) + allow(driver).to receive(:list_network_names).and_return(network_names) + allow(driver).to receive(:existing_named_network?).with("vagrant_network_172.20.0.0/16"). + and_return(true) + allow(driver).to receive(:network_used?).with("vagrant_network_172.20.0.0/16"). + and_return(true) + allow(driver).to receive(:existing_named_network?).with("vagrant_network_2a02:6b8:b010:9020:1::/80"). + and_return(true) + allow(driver).to receive(:network_used?).with("vagrant_network_2a02:6b8:b010:9020:1::/80"). + and_return(true) + + expect(driver).not_to receive(:rm_network).with("vagrant_network_172.20.0.0/16") + expect(driver).not_to receive(:rm_network).with("vagrant_network_2a02:6b8:b010:9020:1::/80") + + subject.call(env) + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/action/login_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/action/login_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/action/login_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/action/login_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,119 @@ +require_relative "../../../../base" +require_relative "../../../../../../plugins/providers/docker/action/login" + + +describe VagrantPlugins::DockerProvider::Action::Login do + include_context "unit" + + let(:sandbox) { isolated_environment } + + let(:iso_env) do + # We have to create a Vagrantfile so there is a root path + sandbox.vagrantfile("") + sandbox.create_vagrant_env + end + + let(:provider_config) { double("provider_config", username: "docker", password: "") } + + let(:vm_config) { double("machine_vm_config") } + + let(:machine_config) do + double("machine_config").tap do |top_config| + allow(top_config).to receive(:vm).and_return(vm_config) + end + end + + let(:machine) do + iso_env.machine(iso_env.machine_names[0], :docker).tap do |m| + allow(m).to receive(:id).and_return("12345") + allow(m).to receive(:config).and_return(machine_config) + allow(m).to receive(:provider_config).and_return(provider_config) + allow(m).to receive(:vagrantfile).and_return(vagrantfile) + allow(m.provider).to receive(:driver).and_return(driver) + allow(m.provider).to receive(:host_vm?).and_return(false) + end + end + + let(:vagrantfile) { double("vagrantfile") } + + let(:env) {{ machine: machine, ui: machine.ui, root_path: Pathname.new("."), vagrantfile: vagrantfile }} + let(:app) { lambda { |*args| }} + let(:driver) { double("driver", create: "abcd1234") } + + + subject { described_class.new(app, env) } + + let(:subprocess_result) do + double("subprocess_result").tap do |result| + allow(result).to receive(:exit_code).and_return(0) + allow(result).to receive(:stdout).and_return("") + allow(result).to receive(:stderr).and_return("") + end + end + + before do + allow(Vagrant::Util::Subprocess).to receive(:execute).with("docker", "version", an_instance_of(Hash)).and_return(subprocess_result) + end + + after do + sandbox.close + end + + describe "#call" do + it "calls the next action in the chain" do + allow(driver).to receive(:host_vm?).and_return(false) + + called = false + app = ->(*args) { called = true } + + action = described_class.new(app, env) + + action.call(env) + + expect(called).to eq(true) + end + + it "uses a host vm lock if host_vm is true and password is set" do + allow(driver).to receive(:host_vm?).and_return(true) + allow(driver).to receive(:login).and_return(true) + allow(driver).to receive(:logout).and_return(true) + + allow(machine.provider).to receive(:host_vm?).and_return(true) + allow(machine.provider).to receive(:host_vm_lock) { |&block| block.call } + + allow(provider_config).to receive(:password).and_return("docker") + allow(provider_config).to receive(:email).and_return("docker") + allow(provider_config).to receive(:auth_server).and_return("docker") + + called = false + app = ->(*args) { called = true } + + action = described_class.new(app, env) + + action.call(env) + + expect(called).to eq(true) + end + + it "doesn't use the host vm if not set" do + allow(driver).to receive(:host_vm?).and_return(false) + allow(driver).to receive(:login).and_return(true) + allow(driver).to receive(:logout).and_return(true) + + allow(machine.provider).to receive(:host_vm?).and_return(false) + + allow(provider_config).to receive(:password).and_return("docker") + allow(provider_config).to receive(:email).and_return("docker") + allow(provider_config).to receive(:auth_server).and_return("docker") + + called = false + app = ->(*args) { called = true } + + action = described_class.new(app, env) + + action.call(env) + + expect(called).to eq(true) + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/action/prepare_networks_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/action/prepare_networks_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/action/prepare_networks_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/action/prepare_networks_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,390 @@ +require_relative "../../../../base" +require_relative "../../../../../../plugins/providers/docker/action/prepare_networks" + +describe VagrantPlugins::DockerProvider::Action::PrepareNetworks do + include_context "unit" + + let(:sandbox) { isolated_environment } + + let(:iso_env) do + # We have to create a Vagrantfile so there is a root path + sandbox.vagrantfile("") + sandbox.create_vagrant_env + end + + let(:vm_config) { double("machine_vm_config") } + + let(:machine_config) do + double("machine_config").tap do |top_config| + allow(top_config).to receive(:vm).and_return(vm_config) + end + end + + let(:machine) do + iso_env.machine(iso_env.machine_names[0], :docker).tap do |m| + allow(m).to receive(:vagrantfile).and_return(vagrantfile) + allow(m).to receive(:config).and_return(machine_config) + allow(m.provider).to receive(:driver).and_return(driver) + allow(m.config.vm).to receive(:networks).and_return(networks) + end + end + + let(:vagrantfile) { double("vagrantfile") } + + let(:env) {{ machine: machine, ui: machine.ui, root_path: Pathname.new("."), vagrantfile: vagrantfile }} + let(:app) { lambda { |*args| }} + let(:driver) { double("driver", create: "abcd1234") } + + let(:networks) { [[:private_network, + {:ip=>"172.20.128.2", + :subnet=>"172.20.0.0/16", + :driver=>"bridge", + :internal=>"true", + :alias=>"mynetwork", + :protocol=>"tcp", + :id=>"80e017d5-388f-4a2f-a3de-f8dce8156a58"}], + [:public_network, + {:ip=>"172.30.130.2", + :subnet=>"172.30.0.0/16", + :driver=>"bridge", + :id=>"30e017d5-488f-5a2f-a3ke-k8dce8246b60"}], + [:private_network, + {:type=>"dhcp", + :ipv6=>"true", + :subnet=>"2a02:6b8:b010:9020:1::/80", + :protocol=>"tcp", + :id=>"b8f23054-38d5-45c3-99ea-d33fc5d1b9f2"}], + [:forwarded_port, + {:guest=>22, :host=>2200, :host_ip=>"127.0.0.1", :id=>"ssh", :auto_correct=>true, :protocol=>"tcp"}]] + } + + let(:invalid_network) { + [[:private_network, + {:ipv6=>"true", + :protocol=>"tcp", + :id=>"b8f23054-38d5-45c3-99ea-d33fc5d1b9f2"}]] + } + + subject { described_class.new(app, env) } + + let(:subprocess_result) do + double("subprocess_result").tap do |result| + allow(result).to receive(:exit_code).and_return(0) + allow(result).to receive(:stdout).and_return("") + allow(result).to receive(:stderr).and_return("") + end + end + + before do + allow(Vagrant::Util::Subprocess).to receive(:execute).with("docker", "version", an_instance_of(Hash)).and_return(subprocess_result) + end + + after do + sandbox.close + end + + describe "#call" do + it "calls the next action in the chain" do + allow(driver).to receive(:host_vm?).and_return(false) + allow(driver).to receive(:existing_named_network?).and_return(false) + allow(driver).to receive(:create_network).and_return(true) + + called = false + app = ->(*args) { called = true } + + action = described_class.new(app, env) + + allow(action).to receive(:process_public_network).and_return(["name", {}]) + allow(action).to receive(:process_private_network).and_return(["name", {}]) + + action.call(env) + + expect(called).to eq(true) + end + + it "calls the proper driver methods to setup a network" do + allow(driver).to receive(:host_vm?).and_return(false) + allow(driver).to receive(:existing_named_network?).and_return(false) + allow(driver).to receive(:network_containing_address). + with("172.20.128.2").and_return(nil) + allow(driver).to receive(:network_containing_address). + with("192.168.1.1").and_return(nil) + allow(driver).to receive(:network_defined?).with("172.20.128.0/24"). + and_return(false) + allow(driver).to receive(:network_defined?).with("172.30.128.0/24"). + and_return(false) + allow(driver).to receive(:network_defined?).with("2a02:6b8:b010:9020:1::/80"). + and_return(false) + + allow(subject).to receive(:request_public_gateway).and_return("1234") + allow(subject).to receive(:request_public_iprange).and_return("1234") + + expect(subject).to receive(:process_private_network).with(networks[0][1], {}, env). + and_return(["vagrant_network_172.20.128.0/24", {:ipv6=>false, :subnet=>"172.20.128.0/24"}]) + + expect(subject).to receive(:process_public_network).with(networks[1][1], {}, env). + and_return(["vagrant_network_public_wlp4s0", {"opt"=>"parent=wlp4s0", "subnet"=>"192.168.1.0/24", "driver"=>"macvlan", "gateway"=>"1234", "ipv6"=>false, "ip_range"=>"1234"}]) + + expect(subject).to receive(:process_private_network).with(networks[2][1], {}, env). + and_return(["vagrant_network_2a02:6b8:b010:9020:1::/80", {:ipv6=>true, :subnet=>"2a02:6b8:b010:9020:1::/80"}]) + + allow(machine.ui).to receive(:ask).and_return("1") + + expect(driver).to receive(:create_network). + with("vagrant_network_172.20.128.0/24", ["--subnet", "172.20.128.0/24"]) + expect(driver).to receive(:create_network). + with("vagrant_network_public_wlp4s0", ["--opt", "parent=wlp4s0", "--subnet", "192.168.1.0/24", "--driver", "macvlan", "--gateway", "1234", "--ip-range", "1234"]) + expect(driver).to receive(:create_network). + with("vagrant_network_2a02:6b8:b010:9020:1::/80", ["--ipv6", "--subnet", "2a02:6b8:b010:9020:1::/80"]) + + subject.call(env) + + expect(env[:docker_connects]).to eq({0=>"vagrant_network_172.20.128.0/24", 1=>"vagrant_network_public_wlp4s0", 2=>"vagrant_network_2a02:6b8:b010:9020:1::/80"}) + end + + it "uses an existing network if a matching subnet is found" do + allow(driver).to receive(:host_vm?).and_return(false) + allow(driver).to receive(:network_containing_address). + with("172.20.128.2").and_return(nil) + allow(driver).to receive(:network_containing_address). + with("192.168.1.1").and_return(nil) + allow(driver).to receive(:network_defined?).with("172.20.128.0/24"). + and_return("vagrant_network_172.20.128.0/24") + allow(driver).to receive(:network_defined?).with("172.30.128.0/24"). + and_return("vagrant_network_public_wlp4s0") + allow(driver).to receive(:network_defined?).with("2a02:6b8:b010:9020:1::/80"). + and_return("vagrant_network_2a02:6b8:b010:9020:1::/80") + allow(machine.ui).to receive(:ask).and_return("1") + + expect(driver).to receive(:existing_named_network?). + with("vagrant_network_172.20.128.0/24").and_return(true) + expect(driver).to receive(:existing_named_network?). + with("vagrant_network_public_wlp4s0").and_return(true) + expect(driver).to receive(:existing_named_network?). + with("vagrant_network_2a02:6b8:b010:9020:1::/80").and_return(true) + + expect(subject).to receive(:process_private_network).with(networks[0][1], {}, env). + and_return(["vagrant_network_172.20.128.0/24", {:ipv6=>false, :subnet=>"172.20.128.0/24"}]) + + expect(subject).to receive(:process_public_network).with(networks[1][1], {}, env). + and_return(["vagrant_network_public_wlp4s0", {"opt"=>"parent=wlp4s0", "subnet"=>"192.168.1.0/24", "driver"=>"macvlan", "gateway"=>"1234", "ipv6"=>false, "ip_range"=>"1234"}]) + + expect(subject).to receive(:process_private_network).with(networks[2][1], {}, env). + and_return(["vagrant_network_2a02:6b8:b010:9020:1::/80", {:ipv6=>true, :subnet=>"2a02:6b8:b010:9020:1::/80"}]) + expect(driver).not_to receive(:create_network) + + expect(subject).to receive(:validate_network_configuration!). + with("vagrant_network_172.20.128.0/24", networks[0][1], + {:ipv6=>false, :subnet=>"172.20.128.0/24"}, driver) + + expect(subject).to receive(:validate_network_configuration!). + with("vagrant_network_public_wlp4s0", networks[1][1], + {"opt"=>"parent=wlp4s0", "subnet"=>"192.168.1.0/24", "driver"=>"macvlan", "gateway"=>"1234", "ipv6"=>false, "ip_range"=>"1234"}, driver) + + expect(subject).to receive(:validate_network_configuration!). + with("vagrant_network_2a02:6b8:b010:9020:1::/80", networks[2][1], + {:ipv6=>true, :subnet=>"2a02:6b8:b010:9020:1::/80"}, driver) + + subject.call(env) + end + + it "raises an error if an inproper network configuration is given" do + allow(machine.config.vm).to receive(:networks).and_return(invalid_network) + allow(driver).to receive(:host_vm?).and_return(false) + allow(driver).to receive(:existing_network?).and_return(false) + + expect{ subject.call(env) }.to raise_error(VagrantPlugins::DockerProvider::Errors::NetworkIPAddressRequired) + end + end + + describe "#list_interfaces" do + let(:interfaces){ ["192.168.1.2", "192.168.10.10"] } + + it "returns an array of interfaces to use" do + allow(Socket).to receive(:getifaddrs). + and_return(interfaces.map{|i| double(:socket, addr: Addrinfo.ip(i))}) + interfaces = subject.list_interfaces + + expect(subject.list_interfaces.size).to eq(2) + end + + it "does not include an interface with the address is nil" do + allow(Socket).to receive(:getifaddrs). + and_return(interfaces.map{|i| double(:socket, addr: nil)}) + + expect(subject.list_interfaces.size).to eq(0) + end + end + + describe "#generate_create_cli_arguments" do + let(:network_options) { + {:ip=>"172.20.128.2", + :subnet=>"172.20.0.0/16", + :driver=>"bridge", + :internal=>"true", + :alias=>"mynetwork", + :protocol=>"tcp", + :id=>"80e017d5-388f-4a2f-a3de-f8dce8156a58"} } + + let(:false_network_options) { + {:ip=>"172.20.128.2", + :subnet=>"172.20.0.0/16", + :driver=>"bridge", + :internal=>"false", + :alias=>"mynetwork", + :protocol=>"tcp", + :id=>"80e017d5-388f-4a2f-a3de-f8dce8156a58"} } + + it "returns an array of cli arguments" do + cli_args = subject.generate_create_cli_arguments(network_options) + expect(cli_args).to eq( ["--ip", "172.20.128.2", "--subnet", "172.20.0.0/16", "--driver", "bridge", "--internal", "--alias", "mynetwork", "--protocol", "tcp", "--id", "80e017d5-388f-4a2f-a3de-f8dce8156a58"]) + end + + it "removes option if set to false" do + cli_args = subject.generate_create_cli_arguments(false_network_options) + expect(cli_args).to eq( ["--ip", "172.20.128.2", "--subnet", "172.20.0.0/16", "--driver", "bridge", "--alias", "mynetwork", "--protocol", "tcp", "--id", "80e017d5-388f-4a2f-a3de-f8dce8156a58"]) + end + end + + describe "#validate_network_name!" do + let(:netname) { "vagrant_network" } + + it "returns true if name exists" do + allow(driver).to receive(:existing_named_network?).with(netname). + and_return(true) + + expect(subject.validate_network_name!(netname, env)).to be_truthy + end + + it "raises an error if name does not exist" do + allow(driver).to receive(:existing_named_network?).with(netname). + and_return(false) + + expect{subject.validate_network_name!(netname, env)}.to raise_error(VagrantPlugins::DockerProvider::Errors::NetworkNameUndefined) + end + end + + describe "#validate_network_configuration!" do + let(:netname) { "vagrant_network_172.20.128.0/24" } + let(:options) { {:ip=>"172.20.128.2", :subnet=>"172.20.0.0/16", :driver=>"bridge", :internal=>"true", :alias=>"mynetwork", :protocol=>"tcp", :id=>"80e017d5-388f-4a2f-a3de-f8dce8156a58", :netmask=>24} } + let(:network_options) { {:ipv6=>false, :subnet=>"172.20.128.0/24"} } + + it "returns true if all options are valid" do + allow(driver).to receive(:network_containing_address).with(options[:ip]). + and_return(netname) + allow(driver).to receive(:network_containing_address).with(network_options[:subnet]). + and_return(netname) + + expect(subject.validate_network_configuration!(netname, options, network_options, driver)). + to be_truthy + end + + it "raises an error of the address is invalid" do + allow(driver).to receive(:network_containing_address).with(options[:ip]). + and_return("fakename") + expect{subject.validate_network_configuration!(netname, options, network_options, driver)}. + to raise_error(VagrantPlugins::DockerProvider::Errors::NetworkAddressInvalid) + end + + it "raises an error of the subnet is invalid" do + allow(driver).to receive(:network_containing_address).with(options[:ip]). + and_return(netname) + allow(driver).to receive(:network_containing_address).with(network_options[:subnet]). + and_return("fakename") + + expect{subject.validate_network_configuration!(netname, options, network_options, driver)}. + to raise_error(VagrantPlugins::DockerProvider::Errors::NetworkSubnetInvalid) + end + end + + describe "#process_private_network" do + let(:options) { {:ip=>"172.20.128.2", :subnet=>"172.20.0.0/16", :driver=>"bridge", :internal=>"true", :alias=>"mynetwork", :protocol=>"tcp", :id=>"80e017d5-388f-4a2f-a3de-f8dce8156a58", :netmask=>24} } + let(:dhcp_options) { {type: "dhcp"} } + let(:bad_options) { {driver: "bridge"} } + + it "generates a network name and config for a dhcp private network" do + network_name, network_options = subject.process_private_network(dhcp_options, {}, env) + + expect(network_name).to eq("vagrant_network") + expect(network_options).to eq({}) + end + + it "generates a network name and options for a static ip" do + allow(driver).to receive(:network_defined?).and_return(nil) + network_name, network_options = subject.process_private_network(options, {}, env) + expect(network_name).to eq("vagrant_network_172.20.0.0/16") + expect(network_options).to eq({:ipv6=>false, :subnet=>"172.20.0.0/16"}) + end + + it "raises an error if no ip address or type `dhcp` was given" do + expect{subject.process_private_network(bad_options, {}, env)}. + to raise_error(VagrantPlugins::DockerProvider::Errors::NetworkIPAddressRequired) + end + end + + describe "#process_public_network" do + let(:options) { {:ip=>"172.30.130.2", :subnet=>"172.30.0.0/16", :driver=>"bridge", :id=>"30e017d5-488f-5a2f-a3ke-k8dce8246b60"} } + let(:ipaddr) { double("ipaddr", prefix: 22, succ: "10.1.10.2", ipv4?: true, + ipv6?: false, to_i: 4294967040) } + + it "raises an error if there are no network interfaces" do + expect(subject).to receive(:list_interfaces).and_return([]) + + expect{subject.process_public_network(options, {}, env)}. + to raise_error(VagrantPlugins::DockerProvider::Errors::NetworkNoInterfaces) + end + + it "generates a network name and configuration" do + allow(machine.ui).to receive(:ask).and_return("1") + allow(subject).to receive(:request_public_gateway).and_return("1234") + allow(subject).to receive(:request_public_iprange).and_return("1234") + allow(IPAddr).to receive(:new).and_return(ipaddr) + allow(driver).to receive(:existing_named_network?).and_return(false) + allow(driver).to receive(:network_containing_address). + with("10.1.10.2").and_return("vagrant_network_public") + + network_name, network_options = subject.process_public_network(options, {}, env) + expect(network_name).to eq("vagrant_network_public") + end + end + + describe "#request_public_gateway" do + let(:options) { {:ip=>"172.30.130.2", :subnet=>"172.30.0.0/16", :driver=>"bridge", :id=>"30e017d5-488f-5a2f-a3ke-k8dce8246b60"} } + let(:ipaddr) { double("ipaddr", to_s: "172.30.130.2", prefix: 22, succ: "172.30.130.3", + ipv4?: true, ipv6?: false) } + + it "requests a gateway" do + allow(IPAddr).to receive(:new).and_return(ipaddr) + allow(ipaddr).to receive(:include?).and_return(false) + allow(machine.ui).to receive(:ask).and_return("1") + + addr = subject.request_public_gateway(options, "bridge", env) + + expect(addr).to eq("172.30.130.2") + end + end + + describe "#request_public_iprange" do + let(:options) { {:ip=>"172.30.130.2", :subnet=>"172.30.0.0/16", :driver=>"bridge", :id=>"30e017d5-488f-5a2f-a3ke-k8dce8246b60"} } + let(:ipaddr) { double("ipaddr", to_s: "172.30.100.2", prefix: 22, succ: "172.30.100.3", + ipv4?: true, ipv6?: false) } + let(:subnet) { double("ipaddr", to_s: "172.30.130.2", prefix: 22, succ: "172.30.130.3", + ipv6?: false) } + + let(:ipaddr_prefix) { double("ipaddr_prefix", to_s: "255.255.255.255/255.255.255.0", + to_i: 4294967040 ) } + + let(:netmask) { double("netmask", ip_unpack: ["255.255.255.0", 0]) } + let(:interface) { double("interface", name: "bridge", netmask: netmask) } + + it "requests a public ip range" do + allow(IPAddr).to receive(:new).with(options[:subnet]).and_return(subnet) + allow(IPAddr).to receive(:new).with("172.30.130.2").and_return(ipaddr) + allow(IPAddr).to receive(:new).with("255.255.255.255/255.255.255.0").and_return(ipaddr_prefix) + allow(subnet).to receive(:include?).and_return(true) + allow(machine.ui).to receive(:ask).and_return(options[:ip]) + + addr = subject.request_public_iprange(options, interface, env) + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/driver_compose_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/driver_compose_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/driver_compose_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/driver_compose_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -54,6 +54,23 @@ end end + describe '#build' do + it 'creates a compose config with no extra options' do + expect(subject).to receive(:update_composition) + subject.build(composition_path) + end + + it 'creates a compose config when given an array for build-arg' do + expect(subject).to receive(:update_composition) + subject.build(composition_path, extra_args: ["foo", "bar"]) + end + + it 'creates a compose config when given a hash for build-arg' do + expect(subject).to receive(:update_composition) + subject.build(composition_path, extra_args: {"foo"=>"bar"}) + end + end + describe '#create' do let(:params) { { image: 'jimi/hendrix:electric-ladyland', @@ -106,6 +123,22 @@ end end + context 'with a volumes key in use for mounting' do + let(:compose_config) { {"volumes"=>{"my_volume_key"=>"data"}} } + + before do + params[:volumes] = 'my_volume_key:my/guest/path' + allow(Pathname).to receive(:new).with('./path').and_call_original + allow(Pathname).to receive(:new).with('my_volume_key').and_call_original + allow(Pathname).to receive(:new).with('/compose/cwd/my_volume_key').and_call_original + allow(subject).to receive(:get_composition).and_return(compose_config) + end + + it 'should not expand the relative host directory' do + expect(docker_yml).to receive(:write).with(%r{my_volume_key}) + end + end + it 'links containers' do params[:links].each do |link| expect(docker_yml).to receive(:write).with(/#{link}/) diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/driver_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/driver_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/driver_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/driver_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -10,6 +10,149 @@ allow(subject).to receive(:execute) { |*args| @cmd = args.join(' ') } end + let(:docker_network_struct) { +[ + { + "Name": "bridge", + "Id": "ae74f6cc18bbcde86326937797070b814cc71bfc4a6d8e3e8cf3b2cc5c7f4a7d", + "Created": "2019-03-20T14:10:06.313314662-07:00", + "Scope": "local", + "Driver": "bridge", + "EnableIPv6": false, + "IPAM": { + "Driver": "default", + "Options": nil, + "Config": [ + { + "Subnet": "172.17.0.0/16", + "Gateway": "172.17.0.1" + } + ] + }, + "Internal": false, + "Attachable": false, + "Ingress": false, + "ConfigFrom": { + "Network": "" + }, + "ConfigOnly": false, + "Containers": { + "a1ee9b12bcea8268495b1f43e8d1285df1925b7174a695075f6140adb9415d87": { + "Name": "vagrant-sandbox_docker-1_1553116237", + "EndpointID": "fc1b0ed6e4f700cf88bb26a98a0722655191542e90df3e3492461f4d1f3c0cae", + "MacAddress": "02:42:ac:11:00:02", + "IPv4Address": "172.17.0.2/16", + "IPv6Address": "" + } + }, + "Options": { + "com.docker.network.bridge.default_bridge": "true", + "com.docker.network.bridge.enable_icc": "true", + "com.docker.network.bridge.enable_ip_masquerade": "true", + "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", + "com.docker.network.bridge.name": "docker0", + "com.docker.network.driver.mtu": "1500" + }, + "Labels": {} + }, + { + "Name": "host", + "Id": "2a2845e77550e33bf3e97bda8b71477ac7d3ccf78bc9102585fdb6056fb84cbf", + "Created": "2018-09-28T10:54:08.633543196-07:00", + "Scope": "local", + "Driver": "host", + "EnableIPv6": false, + "IPAM": { + "Driver": "default", + "Options": nil, + "Config": [] + }, + "Internal": false, + "Attachable": false, + "Ingress": false, + "ConfigFrom": { + "Network": "" + }, + "ConfigOnly": false, + "Containers": {}, + "Options": {}, + "Labels": {} + }, + { + "Name": "vagrant_network", + "Id": "93385d4fd3cf7083a36e62fa72a0ad0a21203d0ddf48409c32b550cd8462b3ba", + "Created": "2019-03-20T14:10:36.828235585-07:00", + "Scope": "local", + "Driver": "bridge", + "EnableIPv6": false, + "IPAM": { + "Driver": "default", + "Options": {}, + "Config": [ + { + "Subnet": "172.18.0.0/16", + "Gateway": "172.18.0.1" + } + ] + }, + "Internal": false, + "Attachable": false, + "Ingress": false, + "ConfigFrom": { + "Network": "" + }, + "ConfigOnly": false, + "Containers": { + "a1ee9b12bcea8268495b1f43e8d1285df1925b7174a695075f6140adb9415d87": { + "Name": "vagrant-sandbox_docker-1_1553116237", + "EndpointID": "9502cd9d37ae6815e3ffeb0bc2de9b84f79e7223e8a1f8f4ccc79459e96c7914", + "MacAddress": "02:42:ac:12:00:02", + "IPv4Address": "172.18.0.2/16", + "IPv6Address": "" + } + }, + "Options": {}, + "Labels": {} + }, + { + "Name": "vagrant_network_172.20.0.0/16", + "Id": "649f0ab3ef0eef6f2a025c0d0398bd7b9b4d05ec88b0d7bd573b44153d903cfb", + "Created": "2019-03-20T14:10:37.088885647-07:00", + "Scope": "local", + "Driver": "bridge", + "EnableIPv6": false, + "IPAM": { + "Driver": "default", + "Options": {}, + "Config": [ + { + "Subnet": "172.20.0.0/16" + } + ] + }, + "Internal": false, + "Attachable": false, + "Ingress": false, + "ConfigFrom": { + "Network": "" + }, + "ConfigOnly": false, + "Containers": { + "a1ee9b12bcea8268495b1f43e8d1285df1925b7174a695075f6140adb9415d87": { + "Name": "vagrant-sandbox_docker-1_1553116237", + "EndpointID": "e19156f8018f283468227fa97c145f4ea0eaba652fb7e977a0c759b1c3ec168a", + "MacAddress": "02:42:ac:14:80:02", + "IPv4Address": "172.20.0.2/16", + "IPv6Address": "" + } + }, + "Options": {}, + "Labels": {} + } +].to_json } + + + describe '#create' do let(:params) { { image: 'jimi/hendrix:electric-ladyland', @@ -251,4 +394,139 @@ expect(subject.docker_bridge_ip).to eq('123.456.789.012') end end + + describe '#docker_connect_network' do + let(:opts) { ["--ip", "172.20.128.2"] } + it 'connects a network to a container' do + expect(subject).to receive(:execute).with("docker", "network", "connect", "vagrant_network", cid, "--ip", "172.20.128.2") + subject.connect_network("vagrant_network", cid, opts) + end + end + + describe '#docker_create_network' do + let(:opts) { ["--subnet", "172.20.0.0/16"] } + it 'creates a network' do + expect(subject).to receive(:execute).with("docker", "network", "create", "vagrant_network", "--subnet", "172.20.0.0/16") + subject.create_network("vagrant_network", opts) + end + end + + describe '#docker_disconnet_network' do + it 'disconnects a network from a container' do + expect(subject).to receive(:execute).with("docker", "network", "disconnect", "vagrant_network", cid, "--force") + subject.disconnect_network("vagrant_network", cid) + end + end + + describe '#docker_inspect_network' do + it 'gets info about a network' do + expect(subject).to receive(:execute).with("docker", "network", "inspect", "vagrant_network") + subject.inspect_network("vagrant_network") + end + end + + describe '#docker_list_network' do + it 'lists docker networks' do + expect(subject).to receive(:execute).with("docker", "network", "ls") + subject.list_network() + end + end + + describe '#docker_rm_network' do + it 'deletes a docker network' do + expect(subject).to receive(:execute).with("docker", "network", "rm", "vagrant_network") + subject.rm_network("vagrant_network") + end + end + + describe '#network_defined?' do + let(:subnet_string) { "172.20.0.0/16" } + let(:network_names) { ["vagrant_network_172.20.0.0/16", "bridge", "null" ] } + + it "returns network name if defined" do + allow(subject).to receive(:list_network_names).and_return(network_names) + allow(subject).to receive(:inspect_network).and_return(JSON.load(docker_network_struct)) + + network_name = subject.network_defined?(subnet_string) + expect(network_name).to eq("vagrant_network_172.20.0.0/16") + end + + it "returns nil name if not defined" do + allow(subject).to receive(:list_network_names).and_return(network_names) + allow(subject).to receive(:inspect_network).and_return(JSON.load(docker_network_struct)) + + network_name = subject.network_defined?("120.20.0.0/24") + expect(network_name).to eq(nil) + end + end + + describe '#network_containing_address' do + let(:address) { "172.20.128.2" } + let(:network_names) { ["vagrant_network_172.20.0.0/16", "bridge", "null" ] } + + it "returns the network name if it contains the requested address" do + allow(subject).to receive(:list_network_names).and_return(network_names) + allow(subject).to receive(:inspect_network).and_return(JSON.load(docker_network_struct)) + + network_name = subject.network_containing_address(address) + expect(network_name).to eq("vagrant_network_172.20.0.0/16") + end + + it "returns nil if no networks contain the requested address" do + allow(subject).to receive(:list_network_names).and_return(network_names) + allow(subject).to receive(:inspect_network).and_return(JSON.load(docker_network_struct)) + + network_name = subject.network_containing_address("127.0.0.1") + expect(network_name).to eq(nil) + end + end + + describe '#existing_named_network?' do + let(:network_names) { ["vagrant_network_172.20.0.0/16", "bridge", "null" ] } + + it "returns true if the network exists" do + allow(subject).to receive(:list_network_names).and_return(network_names) + + expect(subject.existing_named_network?("vagrant_network_172.20.0.0/16")).to be_truthy + end + + it "returns false if the network does not exist" do + allow(subject).to receive(:list_network_names).and_return(network_names) + + expect(subject.existing_named_network?("vagrant_network_17.0.0/16")).to be_falsey + end + end + + describe '#list_network_names' do + let(:unparsed_network_names) { "vagrant_network_172.20.0.0/16\nbridge\nnull" } + let(:network_names) { ["vagrant_network_172.20.0.0/16", "bridge", "null" ] } + + it "lists the network names" do + allow(subject).to receive(:list_network).with("--format={{.Name}}"). + and_return(unparsed_network_names) + + expect(subject.list_network_names).to eq(network_names) + end + end + + describe '#network_used?' do + let(:network_name) { "vagrant_network_172.20.0.0/16" } + it "returns nil if no networks" do + allow(subject).to receive(:inspect_network).with(network_name).and_return(nil) + + expect(subject.network_used?(network_name)).to eq(nil) + end + + it "returns true if network has containers in use" do + allow(subject).to receive(:inspect_network).with(network_name).and_return([JSON.load(docker_network_struct).last]) + + expect(subject.network_used?(network_name)).to be_truthy + end + + it "returns false if network has containers in use" do + allow(subject).to receive(:inspect_network).with("host").and_return([JSON.load(docker_network_struct)[1]]) + + expect(subject.network_used?("host")).to be_falsey + end + end end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/provider_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/provider_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/providers/docker/provider_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/providers/docker/provider_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,131 @@ +require_relative "../../../base" + +require Vagrant.source_root.join("plugins/providers/docker/provider") + +describe VagrantPlugins::DockerProvider::Provider do + let(:driver_obj){ double("driver") } + let(:provider){ double("provider", driver: driver_obj) } + let(:provider_config){ double("provider_config", force_host_vm: false) } + let(:ssh) { double("ssh", guest_port: 22) } + let(:config) { double("config", ssh: ssh) } + let(:machine){ double("machine", provider: provider, provider_config: provider_config, config: config) } + + + let(:platform) { double("platform") } + + subject { described_class.new(machine) } + + before do + stub_const("Vagrant::Util::Platform", platform) + allow(machine).to receive(:id).and_return("foo") + end + + describe ".usable?" do + subject { described_class } + + it "returns true if usable" do + allow(VagrantPlugins::DockerProvider::Driver).to receive(:new).and_return(driver_obj) + allow(provider_config).to receive(:compose).and_return(false) + allow(driver_obj).to receive(:execute).with("docker", "version").and_return(true) + expect(subject).to be_usable + end + + it "raises an exception if docker is not available" do + allow(VagrantPlugins::DockerProvider::Driver).to receive(:new).and_return(driver_obj) + allow(provider_config).to receive(:compose).and_return(false) + allow(platform).to receive(:windows?).and_return(false) + allow(platform).to receive(:darwin?).and_return(false) + + allow(driver_obj).to receive(:execute).with("docker", "version"). + and_raise(Vagrant::Errors::CommandUnavailable, file: "docker") + + expect { subject.usable?(true) }. + to raise_error(Vagrant::Errors::CommandUnavailable) + end + end + + describe "#driver" do + it "is initialized" do + allow(provider_config).to receive(:compose).and_return(false) + allow(platform).to receive(:windows?).and_return(false) + allow(platform).to receive(:darwin?).and_return(false) + expect(subject.driver).to be_kind_of(VagrantPlugins::DockerProvider::Driver) + end + end + + describe "#state" do + before { allow(subject).to receive(:driver).and_return(driver_obj) } + + it "returns not_created if no ID" do + allow(machine).to receive(:id).and_return(nil) + expect(subject.state.id).to eq(:not_created) + end + + it "calls an action to determine the ID" do + allow(provider_config).to receive(:compose).and_return(false) + allow(platform).to receive(:windows?).and_return(false) + allow(platform).to receive(:darwin?).and_return(false) + expect(machine).to receive(:id).and_return("foo") + expect(driver_obj).to receive(:created?).with("foo").and_return(false) + + expect(subject.state.id).to eq(:not_created) + end + end + + describe "#host_vm" do + let(:host_env) { double("host_env", root_path: "/vagrant.d", default_provider: :virtualbox) } + + it "returns the host machine object" do + allow(machine.provider_config).to receive(:vagrant_vagrantfile).and_return("/path/to/Vagrantfile") + allow(machine.provider_config).to receive(:vagrant_machine).and_return(:default) + allow(machine).to receive(:env).and_return(true) + allow(machine.env).to receive(:root_path).and_return("/.vagrant.d") + allow(machine.env).to receive(:home_path).and_return("/path/to") + allow(machine.env).to receive(:ui_class).and_return(true) + + expect(Vagrant::Environment).to receive(:new).and_return(host_env) + + allow(host_env).to receive(:machine).and_return(true) + subject.host_vm + end + end + + describe "#ssh_info" do + let(:result) { "127.0.0.1" } + let(:exit_code) { 0 } + let(:ssh_info) {{:host=>result,:port=>22}} + + let(:network_settings) { {"NetworkSettings" => {"Bridge"=>"", "SandboxID"=>"randomid", "HairpinMode"=>false, "LinkLocalIPv6Address"=>"", "LinkLocalIPv6PrefixLen"=>0, "Ports"=>{"443/tcp"=>nil, "80/tcp"=>nil}, "SandboxKey"=>"/var/run/docker/netns/158b7024a9e4", "SecondaryIPAddresses"=>nil, "SecondaryIPv6Addresses"=>nil, "EndpointID"=>"randomEndpointID", "Gateway"=>"172.17.0.1", "GlobalIPv6Address"=>"", "GlobalIPv6PrefixLen"=>0, "IPAddress"=>"127.0.0.1", "IPPrefixLen"=>16, "IPv6Gateway"=>"", "MacAddress"=>"02:42:ac:11:00:02", "Networks"=>{"bridge"=>{"IPAMConfig"=>nil, "Links"=>nil, "Aliases"=>nil, "NetworkID"=>"networkIDVar", "EndpointID"=>"endpointIDVar", "Gateway"=>"127.0.0.1", "IPAddress"=>"127.0.0.1", "IPPrefixLen"=>16, "IPv6Gateway"=>"", "GlobalIPv6Address"=>"", "GlobalIPv6PrefixLen"=>0, "MacAddress"=>"02:42:ac:11:00:02", "DriverOpts"=>nil}}}} } + + let(:empty_network_settings) { {"NetworkSettings" => {"Bridge"=>"", "SandboxID"=>"randomid", "HairpinMode"=>false, "LinkLocalIPv6Address"=>"", "LinkLocalIPv6PrefixLen"=>0, "Ports"=>"", "SandboxKey"=>"/var/run/docker/netns/158b7024a9e4", "SecondaryIPAddresses"=>nil, "SecondaryIPv6Addresses"=>nil, "EndpointID"=>"randomEndpointID", "Gateway"=>"172.17.0.1", "GlobalIPv6Address"=>"", "GlobalIPv6PrefixLen"=>0, "IPAddress"=>"", "IPPrefixLen"=>16, "IPv6Gateway"=>"", "MacAddress"=>"02:42:ac:11:00:02", "Networks"=>{"bridge"=>{"IPAMConfig"=>nil, "Links"=>nil, "Aliases"=>nil, "NetworkID"=>"networkIDVar", "EndpointID"=>"endpointIDVar", "Gateway"=>"127.0.0.1", "IPAddress"=>"127.0.0.1", "IPPrefixLen"=>16, "IPv6Gateway"=>"", "GlobalIPv6Address"=>"", "GlobalIPv6PrefixLen"=>0, "MacAddress"=>"02:42:ac:11:00:02", "DriverOpts"=>nil}}}} } + + before do + allow(VagrantPlugins::DockerProvider::Driver).to receive(:new).and_return(driver_obj) + allow(machine).to receive(:action).with(:read_state).and_return(machine_state_id: :running) + end + + it "returns nil if a port info is nil from the driver" do + allow(provider_config).to receive(:compose).and_return(false) + allow(platform).to receive(:windows?).and_return(false) + allow(platform).to receive(:darwin?).and_return(false) + allow(driver_obj).to receive(:created?).and_return(true) + allow(driver_obj).to receive(:state).and_return(:running) + + allow(driver_obj).to receive(:inspect_container).and_return(empty_network_settings) + + expect(subject.ssh_info).to eq(nil) + end + + it "should receive a valid address" do + allow(provider_config).to receive(:compose).and_return(false) + allow(platform).to receive(:windows?).and_return(false) + allow(platform).to receive(:darwin?).and_return(false) + allow(driver_obj).to receive(:created?).and_return(true) + allow(driver_obj).to receive(:state).and_return(:running) + allow(driver_obj).to receive(:execute).with(:get_network_config).and_return(result) + allow(driver_obj).to receive(:inspect_container).and_return(network_settings) + + expect(subject.ssh_info).to eq(ssh_info) + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/providers/virtualbox/cap_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/providers/virtualbox/cap_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/providers/virtualbox/cap_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/providers/virtualbox/cap_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -40,4 +40,19 @@ expect(described_class.forwarded_ports(machine)).to be(nil) end end + + describe "#snapshot_list" do + it "returns all the snapshots" do + allow(machine).to receive(:id).and_return("1234") + allow(driver).to receive(:list_snapshots).with(machine.id). + and_return(["backup", "old"]) + + expect(described_class.snapshot_list(machine)).to eq(["backup", "old"]) + end + + it "returns empty array when the machine is does not exist" do + allow(machine).to receive(:id).and_return(nil) + expect(described_class.snapshot_list(machine)).to eq([]) + end + end end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/providers/virtualbox/driver/version_6_0_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/providers/virtualbox/driver/version_6_0_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/providers/virtualbox/driver/version_6_0_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/providers/virtualbox/driver/version_6_0_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -95,5 +95,68 @@ expect { subject.import(ovf) }.to raise_error(Vagrant::Errors::VirtualBoxNoName) end end + + context "when within windows" do + before do + allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true) + end + + let(:output) {<<-OUTPUT +0%...10%...20%...30%...40%...50%...60%...70%...80%...90%...100% +Interpreting C:\\home\\user\\.vagrant.d\\boxes\\hashicorp-VAGRANTSLASH-precise64\\1.1.0\\virtualbox\\box.ovf... +OK. +Disks: + vmdisk1 85899345920 -1 http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized box-disk1.vmdk -1 -1 + +Virtual system 0: + 0: Suggested OS type: "Ubuntu_64" + (change with "--vsys 0 --ostype "; use "list ostypes" to list all possible values) + 1: Suggested VM name "precise64" + (change with "--vsys 0 --vmname ") + 2: Suggested VM group "/" + (change with "--vsys 0 --group ") + 3: Suggested VM settings file name "C:\\home\\user\\VirtualBox VMs\\precise64\\precise64.vbox" + (change with "--vsys 0 --settingsfile ") + 4: Suggested VM base folder "C:\\home\\vagrant\\VirtualBox VMs" + (change with "--vsys 0 --basefolder ") + 5: Number of CPUs: 2 + (change with "--vsys 0 --cpus ") + 6: Guest memory: 384 MB + (change with "--vsys 0 --memory ") + 7: Network adapter: orig NAT, config 3, extra slot=0;type=NAT + 8: CD-ROM + (disable with "--vsys 0 --unit 8 --ignore") + 9: IDE controller, type PIIX4 + (disable with "--vsys 0 --unit 9 --ignore") +10: IDE controller, type PIIX4 + (disable with "--vsys 0 --unit 10 --ignore") +11: SATA controller, type AHCI + (disable with "--vsys 0 --unit 11 --ignore") +12: Hard disk image: source image=box-disk1.vmdk, target path=box-disk1.vmdk, controller=11;channel=0 + (change target path with "--vsys 0 --unit 12 --disk path"; + disable with "--vsys 0 --unit 12 --ignore") +OUTPUT + } + + it "should include disk image on import" do + expect(subject).to receive(:execute).with("import", "-n", ovf).and_return(output) + expect(subject).to receive(:execute) do |*args| + match = args[3, args.size].detect { |a| a.include?("disk1.vmdk") } + expect(match).to include("disk1.vmdk") + end + expect(subject.import(ovf)).to eq(machine_id) + end + + it "should update the suggested VM path from default box name" do + expect(subject).to receive(:execute).with("import", "-n", ovf).and_return(output) + expect(subject).to receive(:execute) do |*args| + match = args[3, args.size].detect { |a| a.include?("box-disk1.vmdk") } + expect(match).not_to include("/precise64/box-disk1.vmdk") + expect(match).to match(/.+precise64_.+?\/box-disk1.vmdk/) + end + expect(subject.import(ovf)).to eq(machine_id) + end + + end end end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/providers/virtualbox/provider_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/providers/virtualbox/provider_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/providers/virtualbox/provider_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/providers/virtualbox/provider_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,113 @@ +require_relative "../../../base" + +require Vagrant.source_root.join("plugins/providers/virtualbox/provider") + +describe VagrantPlugins::ProviderVirtualBox::Provider do + let(:driver){ double("driver") } + let(:provider){ double("provider", driver: driver) } + let(:provider_config){ double("provider_config") } + let(:uid) { "1000" } + let(:machine){ double("machine", uid: uid, provider: provider, provider_config: provider_config) } + + let(:platform) { double("platform") } + + subject { described_class.new(machine) } + + before do + stub_const("Vagrant::Util::Platform", platform) + allow(platform).to receive(:windows?).and_return(false) + allow(platform).to receive(:cygwin?).and_return(false) + allow(platform).to receive(:wsl?).and_return(false) + allow(platform).to receive(:wsl_windows_access_bypass?).and_return(false) + allow(machine).to receive(:id).and_return("foo") + + allow(Process).to receive(:uid).and_return(uid) + end + + describe ".usable?" do + subject { described_class } + + it "returns true if usable" do + allow(VagrantPlugins::ProviderVirtualBox::Driver::Meta).to receive(:new).and_return(driver) + expect(subject).to be_usable + end + + it "raises an exception if virtualbox is not available" do + allow(VagrantPlugins::ProviderVirtualBox::Driver::Meta).to receive(:new). + and_raise(Vagrant::Errors::VirtualBoxNotDetected) + + expect { subject.usable?(true) }. + to raise_error(Vagrant::Errors::VirtualBoxNotDetected) + end + + it "raises an exception if virtualbox is the wrong version" do + allow(VagrantPlugins::ProviderVirtualBox::Driver::Meta).to receive(:new). + and_raise(Vagrant::Errors::VirtualBoxInvalidVersion, supported_versions: "1,2,3") + + expect { subject.usable?(true) }. + to raise_error(Vagrant::Errors::VirtualBoxInvalidVersion) + end + + it "raises an exception if virtualbox kernel module is not loaded" do + allow(VagrantPlugins::ProviderVirtualBox::Driver::Meta).to receive(:new). + and_raise(Vagrant::Errors::VirtualBoxKernelModuleNotLoaded) + + expect { subject.usable?(true) }. + to raise_error(Vagrant::Errors::VirtualBoxKernelModuleNotLoaded) + end + + it "raises an exception if virtualbox installation is incomplete" do + allow(VagrantPlugins::ProviderVirtualBox::Driver::Meta).to receive(:new). + and_raise(Vagrant::Errors::VirtualBoxInstallIncomplete) + + expect { subject.usable?(true) }. + to raise_error(Vagrant::Errors::VirtualBoxInstallIncomplete) + end + end + + describe "#driver" do + it "is initialized" do + allow(VagrantPlugins::ProviderVirtualBox::Driver::Meta).to receive(:new).and_return(driver) + expect(subject.driver).to be(driver) + end + end + + describe "#state" do + it "returns not_created if no ID" do + allow(machine).to receive(:id).and_return(nil) + allow(machine).to receive(:data_dir).and_return(".vagrant") + + expect(subject.state.id).to eq(:not_created) + end + end + + describe "#ssh_info" do + let(:result) { "127.0.0.1" } + let(:exit_code) { 0 } + let(:ssh_info) {{:host=>result,:port=>22}} + let(:ssh) { double("ssh", guest_port: 22) } + let(:config) { double("config", ssh: ssh) } + + before do + allow(VagrantPlugins::ProviderVirtualBox::Driver::Meta).to receive(:new).and_return(driver) + allow(machine).to receive(:action).with(:read_state).and_return(machine_state_id: :running) + allow(machine).to receive(:data_dir).and_return(".vagrant") + allow(driver).to receive(:uuid).and_return("1234") + allow(driver).to receive(:read_state).and_return(:running) + allow(driver).to receive(:ssh_port).and_return(22) + allow(machine).to receive(:config).and_return(config) + end + + it "returns nil if machine state is not running" do + allow(driver).to receive(:read_state).and_return(:not_created) + expect(subject.ssh_info).to eq(nil) + end + + it "should receive a valid address" do + allow(driver).to receive(:execute).with(:get_network_config).and_return(result) + + allow(driver).to receive(:read_guest_ip).and_return({"ip" => "127.0.0.1"}) + expect(subject.ssh_info).to eq(ssh_info) + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/ansible/cap/guest/pip/pip_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/ansible/cap/guest/pip/pip_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/ansible/cap/guest/pip/pip_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/ansible/cap/guest/pip/pip_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,52 @@ +require_relative "../../../../../../base" + +require Vagrant.source_root.join("plugins/provisioners/ansible/cap/guest/pip/pip") + +describe VagrantPlugins::Ansible::Cap::Guest::Pip do + include_context "unit" + + subject { VagrantPlugins::Ansible::Cap::Guest::Pip } + + let(:iso_env) do + # We have to create a Vagrantfile so there is a root path + env = isolated_environment + env.vagrantfile("") + env.create_vagrant_env + end + + let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } + let(:communicator) { double("comm") } + + before do + allow(machine).to receive(:communicate).and_return(communicator) + allow(communicator).to receive(:execute).and_return(true) + end + + describe "#get_pip" do + describe 'when no pip_install_command argument is provided' do + it "installs pip using the default command" do + expect(communicator).to receive(:execute).with("curl https://bootstrap.pypa.io/get-pip.py | sudo python") + subject.get_pip(machine) + end + end + + describe 'when pip_install_command argument is provided' do + it "runs the supplied argument instead of default" do + pip_install_command = "foo" + expect(communicator).to receive(:execute).with(pip_install_command) + subject.get_pip(machine,pip_install_command) + end + + it "installs pip using the default command if the argument is empty" do + pip_install_command = "" + expect(communicator).to receive(:execute).with("curl https://bootstrap.pypa.io/get-pip.py | sudo python") + subject.get_pip(machine,pip_install_command) + end + + it "installs pip using the default command if the argument is nil" do + expect(communicator).to receive(:execute).with("curl https://bootstrap.pypa.io/get-pip.py | sudo python") + subject.get_pip(machine, nil) + end + end + end +end \ No newline at end of file diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/ansible/config/guest_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/ansible/config/guest_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/ansible/config/guest_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/ansible/config/guest_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -32,6 +32,7 @@ inventory_path limit pip_args + pip_install_cmd playbook playbook_command provisioning_path @@ -60,6 +61,7 @@ expect(subject.install_mode).to eql(:default) expect(subject.provisioning_path).to eql("/vagrant") expect(subject.tmp_path).to eql("/tmp/vagrant-ansible") + expect(subject.pip_install_cmd).to eql("") end end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/chef/cap/freebsd/chef_installed_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/chef/cap/freebsd/chef_installed_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/chef/cap/freebsd/chef_installed_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/chef/cap/freebsd/chef_installed_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,41 @@ +require_relative "../../../../../base" + +require Vagrant.source_root.join("plugins/provisioners/chef/cap/freebsd/chef_installed") + +describe VagrantPlugins::Chef::Cap::FreeBSD::ChefInstalled do + include_context "unit" + + let(:iso_env) do + # We have to create a Vagrantfile so there is a root path + env = isolated_environment + env.vagrantfile("") + env.create_vagrant_env + end + + let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + let(:config) { double("config") } + + subject { described_class } + + before do + allow(machine).to receive(:communicate).and_return(communicator) + end + + describe "#chef_installed" do + let(:version) { "15.0.0" } + let(:command) { "test -x /opt/chef_solo/bin/knife&& /opt/chef_solo/bin/knife --version | grep '15.0.0'" } + + it "returns true if installed" do + expect(machine.communicate).to receive(:test). + with(command, sudo: true).and_return(true) + subject.chef_installed(machine, "chef_solo", version) + end + + it "returns false if not installed" do + expect(machine.communicate).to receive(:test). + with(command, sudo: true).and_return(false) + expect(subject.chef_installed(machine, "chef_solo", version)).to be_falsey + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/chef/cap/linux/chef_installed_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/chef/cap/linux/chef_installed_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/chef/cap/linux/chef_installed_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/chef/cap/linux/chef_installed_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,41 @@ +require_relative "../../../../../base" + +require Vagrant.source_root.join("plugins/provisioners/chef/cap/linux/chef_installed") + +describe VagrantPlugins::Chef::Cap::Linux::ChefInstalled do + include_context "unit" + + let(:iso_env) do + # We have to create a Vagrantfile so there is a root path + env = isolated_environment + env.vagrantfile("") + env.create_vagrant_env + end + + let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + let(:config) { double("config") } + + subject { described_class } + + before do + allow(machine).to receive(:communicate).and_return(communicator) + end + + describe "#chef_installed" do + let(:version) { "15.0.0" } + let(:command) { "test -x /opt/chef_solo/bin/knife&& /opt/chef_solo/bin/knife --version | grep '15.0.0'" } + + it "returns true if installed" do + expect(machine.communicate).to receive(:test). + with(command, sudo: true).and_return(true) + subject.chef_installed(machine, "chef_solo", version) + end + + it "returns false if not installed" do + expect(machine.communicate).to receive(:test). + with(command, sudo: true).and_return(false) + expect(subject.chef_installed(machine, "chef_solo", version)).to be_falsey + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/chef/cap/omnios/chef_installed_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/chef/cap/omnios/chef_installed_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/chef/cap/omnios/chef_installed_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/chef/cap/omnios/chef_installed_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,41 @@ +require_relative "../../../../../base" + +require Vagrant.source_root.join("plugins/provisioners/chef/cap/omnios/chef_installed") + +describe VagrantPlugins::Chef::Cap::OmniOS::ChefInstalled do + include_context "unit" + + let(:iso_env) do + # We have to create a Vagrantfile so there is a root path + env = isolated_environment + env.vagrantfile("") + env.create_vagrant_env + end + + let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + let(:config) { double("config") } + + subject { described_class } + + before do + allow(machine).to receive(:communicate).and_return(communicator) + end + + describe "#chef_installed" do + let(:version) { "15.0.0" } + let(:command) { "test -x /opt/chef_solo/bin/knife&& /opt/chef_solo/bin/knife --version | grep '15.0.0'" } + + it "returns true if installed" do + expect(machine.communicate).to receive(:test). + with(command, sudo: true).and_return(true) + subject.chef_installed(machine, "chef_solo", version) + end + + it "returns false if not installed" do + expect(machine.communicate).to receive(:test). + with(command, sudo: true).and_return(false) + expect(subject.chef_installed(machine, "chef_solo", version)).to be_falsey + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/chef/cap/windows/chef_installed_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/chef/cap/windows/chef_installed_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/chef/cap/windows/chef_installed_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/chef/cap/windows/chef_installed_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,41 @@ +require_relative "../../../../../base" + +require Vagrant.source_root.join("plugins/provisioners/chef/cap/windows/chef_installed") + +describe VagrantPlugins::Chef::Cap::Windows::ChefInstalled do + include_context "unit" + + let(:iso_env) do + # We have to create a Vagrantfile so there is a root path + env = isolated_environment + env.vagrantfile("") + env.create_vagrant_env + end + + let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } + let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } + let(:config) { double("config") } + + subject { described_class } + + before do + allow(machine).to receive(:communicate).and_return(communicator) + end + + describe "#chef_installed" do + let(:version) { "15.0.0" } + let(:command) { "if ((&knife --version) -Match \"15.0.0\"){ exit 0 } else { exit 1 }" } + + it "returns true if installed" do + expect(machine.communicate).to receive(:test). + with(command, sudo: true).and_return(true) + subject.chef_installed(machine, "chef_solo", version) + end + + it "returns false if not installed" do + expect(machine.communicate).to receive(:test). + with(command, sudo: true).and_return(false) + expect(subject.chef_installed(machine, "chef_solo", version)).to be_falsey + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/file/provisioner_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/file/provisioner_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/file/provisioner_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/file/provisioner_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -34,8 +34,6 @@ allow(config).to receive(:source).and_return("/source") allow(config).to receive(:destination).and_return("/foo/bar") - expect(communicator).to receive(:execute).with("mkdir -p \"/foo\"") - subject.provision end @@ -43,8 +41,6 @@ allow(config).to receive(:source).and_return("/source") allow(config).to receive(:destination).and_return("/foo bar/bar") - expect(communicator).to receive(:execute).with("mkdir -p \"/foo bar\"") - subject.provision end @@ -52,8 +48,6 @@ allow(config).to receive(:source).and_return("/source/file.sh") allow(config).to receive(:destination).and_return("/foo/bar/file.sh") - expect(communicator).to receive(:execute).with("mkdir -p \"/foo/bar\"") - subject.provision end @@ -71,7 +65,7 @@ allow(config).to receive(:destination).and_return("/foo/bar") expect(communicator).to receive(:upload).with( - File.expand_path("source"), "/foo/bar") + File.expand_path("#{machine.env.cwd}/source"), "/foo/bar") subject.provision end @@ -105,21 +99,12 @@ subject.provision end - it "sends an array of files and folders if winrm and destination doesn't end with file separator" do - files = ["/source/file.py", "/source/folder"] - allow(Dir).to receive(:[]).and_return(files) - allow(config).to receive(:source).and_return("/source") - allow(config).to receive(:destination).and_return("/foo/bar") + it "appends a '/.' to expanded source if defined in original source" do + allow(config).to receive(:source).and_return("/source/.") allow(File).to receive(:directory?).with("/source").and_return(true) - allow(machine.config.vm).to receive(:communicator).and_return(:winrm) - - expect(guest).to receive(:capability?). - with(:shell_expand_guest_path).and_return(true) - expect(guest).to receive(:capability). - with(:shell_expand_guest_path, "/foo/bar").and_return("/foo/bar") + allow(config).to receive(:destination).and_return("/foo/bar") - expect(communicator).to receive(:upload) - .with(files, "/foo/bar") + expect(communicator).to receive(:upload).with("/source/.", "/foo/bar") subject.provision end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/shell/provisioner_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/shell/provisioner_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/provisioners/shell/provisioner_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/provisioners/shell/provisioner_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -175,8 +175,16 @@ end context "with remote script" do + let(:filechecksum) { double("filechecksum", checksum: checksum_value) } + let(:checksum_value) { double("checksum_value") } + + before do + allow(FileChecksum).to receive(:new).and_return(filechecksum) + allow_any_instance_of(Vagrant::Util::Downloader).to receive(:execute_curl).and_return(true) + end context "that does not have matching sha1 checksum" do + let(:checksum_value) { "INVALID_VALUE" } let(:config) { double( :config, @@ -188,18 +196,41 @@ :binary => false, :md5 => nil, :sha1 => 'EXPECTED_VALUE', + :sha256 => nil, + :sha384 => nil, + :sha512 => nil, :reset => false, :reboot => false ) } - let(:digest){ double("digest") } - before do - allow_any_instance_of(Vagrant::Util::Downloader).to receive(:execute_curl).and_return(true) - allow(digest).to receive(:file).and_return(digest) - expect(Digest::SHA1).to receive(:new).and_return(digest) - expect(digest).to receive(:hexdigest).and_return('INVALID_VALUE') + it "should raise an exception" do + vsp = VagrantPlugins::Shell::Provisioner.new(machine, config) + + expect{ vsp.provision }.to raise_error(Vagrant::Errors::DownloaderChecksumError) end + end + + context "that does not have matching sha256 checksum" do + let(:checksum_value) { "INVALID_VALUE" } + let(:config) { + double( + :config, + :args => "doesn't matter", + :env => {}, + :upload_path => "arbitrary", + :remote? => true, + :path => "http://example.com/script.sh", + :binary => false, + :md5 => nil, + :sha1 => nil, + :sha256 => 'EXPECTED_VALUE', + :sha384 => nil, + :sha512 => nil, + :reset => false, + :reboot => false + ) + } it "should raise an exception" do vsp = VagrantPlugins::Shell::Provisioner.new(machine, config) @@ -208,7 +239,8 @@ end end - context "that does not have matching md5 checksum" do + context "that does not have matching sha384 checksum" do + let(:checksum_value) { "INVALID_VALUE" } let(:config) { double( :config, @@ -218,20 +250,71 @@ :remote? => true, :path => "http://example.com/script.sh", :binary => false, - :md5 => 'EXPECTED_VALUE', + :md5 => nil, :sha1 => nil, + :sha256 => nil, + :sha384 => 'EXPECTED_VALUE', + :sha512 => nil, :reset => false, :reboot => false ) } - let(:digest){ double("digest") } - before do - allow_any_instance_of(Vagrant::Util::Downloader).to receive(:execute_curl).and_return(true) - allow(digest).to receive(:file).and_return(digest) - expect(Digest::MD5).to receive(:new).and_return(digest) - expect(digest).to receive(:hexdigest).and_return('INVALID_VALUE') + it "should raise an exception" do + vsp = VagrantPlugins::Shell::Provisioner.new(machine, config) + + expect{ vsp.provision }.to raise_error(Vagrant::Errors::DownloaderChecksumError) end + end + + context "that does not have matching sha512 checksum" do + let(:checksum_value) { "INVALID_VALUE" } + let(:config) { + double( + :config, + :args => "doesn't matter", + :env => {}, + :upload_path => "arbitrary", + :remote? => true, + :path => "http://example.com/script.sh", + :binary => false, + :md5 => nil, + :sha1 => nil, + :sha256 => nil, + :sha384 => nil, + :sha512 => 'EXPECTED_VALUE', + :reset => false, + :reboot => false + ) + } + + it "should raise an exception" do + vsp = VagrantPlugins::Shell::Provisioner.new(machine, config) + + expect{ vsp.provision }.to raise_error(Vagrant::Errors::DownloaderChecksumError) + end + end + + context "that does not have matching md5 checksum" do + let(:checksum_value) { "INVALID_VALUE" } + let(:config) { + double( + :config, + :args => "doesn't matter", + :env => {}, + :upload_path => "arbitrary", + :remote? => true, + :path => "http://example.com/script.sh", + :binary => false, + :md5 => 'EXPECTED_VALUE', + :sha1 => nil, + :sha256 => nil, + :sha384 => nil, + :sha512 => nil, + :reset => false, + :reboot => false + ) + } it "should raise an exception" do vsp = VagrantPlugins::Shell::Provisioner.new(machine, config) diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/synced_folders/rsync/command/rsync_auto_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/synced_folders/rsync/command/rsync_auto_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/synced_folders/rsync/command/rsync_auto_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/synced_folders/rsync/command/rsync_auto_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -66,15 +66,12 @@ # https://github.com/hashicorp/vagrant/blob/9c1b014536e61b332cfaa00774a87a240cce8ed9/lib/vagrant/action/builtin/synced_folders.rb#L45-L46 let(:config_synced_folders) { {"/vagrant": {type: "rsync", - exclude: false, hostpath: "/Users/brian/code/vagrant-sandbox"}, "/vagrant/other-dir": {type: "rsync", - exclude: false, hostpath: "/Users/brian/code/vagrant-sandbox/other-dir"}, "/vagrant/relative-dir": {type: "rsync", - exclude: false, hostpath: "/Users/brian/code/relative-dir"}}} before do @@ -103,6 +100,11 @@ with("Watching: /Users/brian/code/relative-dir") expect(helper_class).to receive(:rsync_single) + expect(Listen).to receive(:to). + with("/Users/brian/code/vagrant-sandbox", + "/Users/brian/code/relative-dir", + {:ignore=>[/.vagrant\//], + :force_polling=>false}) subject.execute end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/synced_folders/rsync/default_unix_cap_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/synced_folders/rsync/default_unix_cap_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/synced_folders/rsync/default_unix_cap_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/synced_folders/rsync/default_unix_cap_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,87 @@ +require_relative "../../../base" + +require Vagrant.source_root.join("plugins/synced_folders/rsync/default_unix_cap") + +describe VagrantPlugins::SyncedFolderRSync::DefaultUnixCap do + include_context "unit" + + let(:iso_env) do + # We have to create a Vagrantfile so there is a root path + env = isolated_environment + env.vagrantfile("") + env.create_vagrant_env + end + + let(:guest) { double("guest") } + let(:machine) { iso_env.machine(iso_env.machine_names[0], :dummy) } + + let(:subject) { Class.new { extend VagrantPlugins::SyncedFolderRSync::DefaultUnixCap } } + + describe "#rsync_installed" do + it "tests if rsync is on the path" do + expect(machine.communicate).to receive(:test).with("which rsync"). + and_return(true) + + subject.rsync_installed(machine) + end + end + + + describe "#rsync_command" do + it "returns the rsync command" do + expect( subject.rsync_command(machine) ).to eq("sudo rsync") + end + end + + describe "#rsync_post" do + let(:opts) {{:type=>:rsync, + :guestpath=>"/vagrant", + :hostpath=>"/home/user/syncfolder", + :disabled=>false, + :__vagrantfile=>true, + :exclude=>[".vagrant"], + :owner=>"vagrant", + :group=>"vagrant"}} + + let(:cmd) { "find /vagrant -path /vagrant/.vagrant -prune -o '!' -type l -a '(' ! -user vagrant -or ! -group vagrant ')' -exec chown vagrant:vagrant '{}' +" } + + it "executes the rsync post command" do + expect(machine.communicate).to receive(:sudo). + with(cmd) + subject.rsync_post(machine, opts) + end + end + + describe "#build_rsync_chown" do + let(:opts) {{:type=>:rsync, + :guestpath=>"/vagrant", + :hostpath=>"/home/user/syncfolder", + :disabled=>false, + :__vagrantfile=>true, + :exclude=>[".vagrant"], + :owner=>"vagrant", + :group=>"vagrant"}} + + let(:cmd) { "find /vagrant -path /vagrant/.vagrant -prune -o '!' -type l -a '(' ! -user vagrant -or ! -group vagrant ')' -exec chown vagrant:vagrant '{}' +" } + let(:no_exclude_cmd) { "find /vagrant '!' -type l -a '(' ! -user vagrant -or ! -group vagrant ')' -exec chown vagrant:vagrant '{}' +" } + + let(:empty_opts) {{:type=>:rsync, + :guestpath=>"/vagrant", + :hostpath=>"/home/user/syncfolder", + :disabled=>false, + :__vagrantfile=>true, + :exclude=>[], + :owner=>"vagrant", + :group=>"vagrant"}} + + it "builds up a command to properly chown folders" do + command = subject.build_rsync_chown(opts) + expect(command).to eq(cmd) + end + + it "does not include any excludes if the array is empty" do + command = subject.build_rsync_chown(empty_opts) + expect(command).to eq(no_exclude_cmd) + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/synced_folders/rsync/helper_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/synced_folders/rsync/helper_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/synced_folders/rsync/helper_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/synced_folders/rsync/helper_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -32,23 +32,23 @@ let(:path) { "/foo/bar" } it "converts a directory match" do - expect(described_class.exclude_to_regexp(path, "foo/")). - to eq(/^#{Regexp.escape(path)}\/.*foo\//) + expect(described_class.exclude_to_regexp("foo/")). + to eq(/^foo\/.[^\/]*/) end it "converts the start anchor" do - expect(described_class.exclude_to_regexp(path, "/foo")). - to eq(/^\/foo\/bar\/foo/) + expect(described_class.exclude_to_regexp("/foo")). + to eq(/^foo\//) end it "converts the **" do - expect(described_class.exclude_to_regexp(path, "fo**o")). - to eq(/^#{Regexp.escape(path)}\/.*fo.*o/) + expect(described_class.exclude_to_regexp("fo**o")). + to eq(/^fo.*o\/.[^\/]*/) end it "converts the *" do - expect(described_class.exclude_to_regexp(path, "fo*o")). - to eq(/^#{Regexp.escape(path)}\/.*fo[^\/]*o/) + expect(described_class.exclude_to_regexp("fo*o")). + to eq(/^fo[^\/]*o\/.[^\/]*/) end end @@ -292,6 +292,20 @@ expect(args[9]).to include("ControlPath=/tmp/vagrant-rsync-12345") }.and_return(result) + expect(FileUtils).to receive(:remove_entry_secure).with("/tmp/vagrant-rsync-12345", true).and_return(true) + subject.rsync_single(machine, ssh_info, opts) + end + + it "does not create tmp dir on windows platforms" do + allow(Vagrant::Util::Platform).to receive(:windows?).and_return(true) + allow(Dir).to receive(:mktmpdir).with("vagrant-rsync-"). + and_return("/tmp/vagrant-rsync-12345") + + expect(Vagrant::Util::Subprocess).to receive(:execute).with(any_args) { |*args| + expect(args).not_to include("ControlPath=/tmp/vagrant-rsync-12345") + }.and_return(result) + + expect(FileUtils).not_to receive(:remove_entry_secure).with("/tmp/vagrant-rsync-12345", true) subject.rsync_single(machine, ssh_info, opts) end end diff -Nru vagrant-2.2.3+dfsg/test/unit/plugins/synced_folders/smb/synced_folder_test.rb vagrant-2.2.6+dfsg/test/unit/plugins/synced_folders/smb/synced_folder_test.rb --- vagrant-2.2.3+dfsg/test/unit/plugins/synced_folders/smb/synced_folder_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/plugins/synced_folders/smb/synced_folder_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -36,7 +36,7 @@ end end - describe ".usable?" do + describe "#usable?" do context "without supporting capabilities" do it "is not usable" do expect(subject.usable?(machine)).to be(false) @@ -66,7 +66,7 @@ end end - describe ".prepare" do + describe "#prepare" do let(:host_caps){ [:smb_start, :smb_prepare] } context "with username credentials provided" do @@ -181,7 +181,7 @@ end end - describe ".enable" do + describe "#enable" do it "fails when guest does not support capability" do expect{ subject.enable(machine, folders, options) @@ -229,6 +229,11 @@ expect(folders["/second/path"][:smb_host]).to eq("ADDR") end + it "should scrub folder configuration" do + expect(subject).to receive(:clean_folder_configuration).at_least(:once) + subject.enable(machine, folders, options) + end + context "with smb_host option set" do let(:folders){ {"/first/path" => {smb_host: "ADDR"}, "/second/path" => {}} } @@ -251,10 +256,63 @@ expect(folders["/second/path"][:group]).to eq("smbgroup") end end + + context "with smb_username and smb_password set" do + let(:folders){ { + "/first/path" => {owner: "smbowner", smb_username: "user", smb_password: "pass"}, + "/second/path" => {group: "smbgroup", smb_username: "user", smb_password: "pass"} + } } + + it "should retain non password configuration options" do + subject.enable(machine, folders, options) + folder1 = folders["/first/path"] + folder2 = folders["/second/path"] + expect(folder1.key?(:owner)).to be_truthy + expect(folder1.key?(:smb_username)).to be_truthy + expect(folder2.key?(:group)).to be_truthy + expect(folder2.key?(:smb_username)).to be_truthy + end + + it "should remove the smb_password option when set" do + subject.enable(machine, folders, options) + expect(folders["/first/path"].key?(:smb_password)).to be_falsey + expect(folders["/second/path"].key?(:smb_password)).to be_falsey + end + end + end + end + + describe "#disable" do + it "should scrub folder configuration" do + expect(subject).to receive(:clean_folder_configuration).at_least(:once) + subject.disable(machine, folders, options) + end + + context "with smb_username and smb_password set" do + let(:folders){ { + "/first/path" => {owner: "smbowner", smb_username: "user", smb_password: "pass"}, + "/second/path" => {group: "smbgroup", smb_username: "user", smb_password: "pass"} + } } + + it "should retain non password configuration options" do + subject.disable(machine, folders, options) + folder1 = folders["/first/path"] + folder2 = folders["/second/path"] + expect(folder1.key?(:owner)).to be_truthy + expect(folder1.key?(:smb_username)).to be_truthy + expect(folder2.key?(:group)).to be_truthy + expect(folder2.key?(:smb_username)).to be_truthy + end + + it "should remove the smb_password option when set" do + subject.disable(machine, folders, options) + expect(folders["/first/path"].key?(:smb_password)).to be_falsey + expect(folders["/second/path"].key?(:smb_password)).to be_falsey + end end end - describe ".cleanup" do + describe "#cleanup" do context "without supporting capability" do it "does nothing" do subject.cleanup(machine, options) @@ -270,4 +328,17 @@ end end end + + describe "#clean_folder_configuration" do + it "should remove smb_password if defined" do + data = {smb_password: "password"} + subject.send(:clean_folder_configuration, data) + expect(data.key?(:smb_password)).to be_falsey + end + + it "should not error if non-hash value provided" do + expect { subject.send(:clean_folder_configuration, nil) }. + not_to raise_error + end + end end diff -Nru vagrant-2.2.3+dfsg/test/unit/templates/guests/suse/network_static6_test.rb vagrant-2.2.6+dfsg/test/unit/templates/guests/suse/network_static6_test.rb --- vagrant-2.2.3+dfsg/test/unit/templates/guests/suse/network_static6_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/templates/guests/suse/network_static6_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,43 @@ +require_relative "../../../base" + +require "vagrant/util/template_renderer" + +describe "templates/guests/suse/network_static6" do + let(:template) { "guests/suse/network_static6" } + + it "renders the template" do + result = Vagrant::Util::TemplateRenderer.render(template, options: { + device: "eth2", + ip: "fde4:8dba:82e1::c4", + }) + expect(result).to eq <<-EOH.gsub(/^ {6}/, "") + #VAGRANT-BEGIN + # The contents below are automatically generated by Vagrant. Do not modify. + STARTMODE='auto' + BOOTPROTO='static' + IPADDR=fde4:8dba:82e1::c4 + DEVICE=eth2 + #VAGRANT-END + EOH + end + + it "includes the prefix-length and gateway" do + result = Vagrant::Util::TemplateRenderer.render(template, options: { + device: "eth1", + ip: "fde4:8dba:82e1::c4", + gateway: "fde4:8dba:82e1::01", + prefix_length: "64", + }) + expect(result).to eq <<-EOH.gsub(/^ {6}/, "") + #VAGRANT-BEGIN + # The contents below are automatically generated by Vagrant. Do not modify. + STARTMODE='auto' + BOOTPROTO='static' + IPADDR=fde4:8dba:82e1::c4 + DEVICE=eth1 + GATEWAY=fde4:8dba:82e1::01 + PREFIXLEN=64 + #VAGRANT-END + EOH + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/action/builder_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/action/builder_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/action/builder_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/action/builder_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -22,6 +22,10 @@ @app = app end + def self.name + "TestAction" + end + define_method(:call) do |env| env[:data] << "#{data}_in" @app.call(env) @@ -147,6 +151,10 @@ @app = app end + def self.name + "TestAction" + end + define_method(:call) do |env| env[:data] << "#{letter}1" @app.call(env) @@ -266,6 +274,10 @@ @app = app end + def self.name + "TestAction" + end + define_method(:call) do |env| inner = described_klass.new inner.use wrapper_proc[2] diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/action/builtin/box_add_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/action/builtin/box_add_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/action/builtin/box_add_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/action/builtin/box_add_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -10,7 +10,7 @@ require "vagrant/util/file_checksum" -describe Vagrant::Action::Builtin::BoxAdd, :skip_windows do +describe Vagrant::Action::Builtin::BoxAdd, :skip_windows, :bsdtar do include_context "unit" let(:app) { lambda { |env| } } diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/action/builtin/box_remove_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/action/builtin/box_remove_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/action/builtin/box_remove_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/action/builtin/box_remove_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -151,6 +151,37 @@ subject.call(env) end + + it "doesn't delete if the box is in use and keep_used_boxes is set" do + env[:keep_used_boxes] = true + machine_index << new_entry("foo", "virtualbox", "1.0") + + result = { result: true } + expect(action_runner).to receive(:run). + with(anything, env).and_return(result) + + expect(box_collection).to receive(:find).with( + "foo", :virtualbox, "1.0").and_return(box) + expect(box).to receive(:destroy!).never + + subject.call(env) + end + + it "deletes if the box is in use and force is used" do + machine_index << new_entry("foo", "virtualbox", "1.0") + + result = { result: true } + expect(action_runner).to receive(:run). + with(anything, env).and_return(result) + + expect(box_collection).to receive(:find).with( + "foo", :virtualbox, "1.0").and_return(box) + expect(box_collection).to receive(:clean).with(box.name) + .and_return(true) + expect(box).to receive(:destroy!) + + subject.call(env) + end end it "errors if the box doesn't exist" do diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/action/builtin/call_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/action/builtin/call_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/action/builtin/call_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/action/builtin/call_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -10,6 +10,10 @@ @app = app end + def self.name + "TestAction" + end + define_method(:call) do |env| env[:data] << "#{data}_in" @app.call(env) @@ -103,6 +107,10 @@ env[:arg] = arg end + def self.name + "TestAction" + end + def call(env); end end @@ -126,6 +134,10 @@ @env = env end + def self.name + "TestAction" + end + def call(env) @app.call(env) end @@ -137,6 +149,10 @@ super end + def self.name + "TestAction" + end + def recover(env) env[:steps] << :recover_A end @@ -148,6 +164,10 @@ super end + def self.name + "TestAction" + end + def recover(env) env[:steps] << :recover_B end diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/action/builtin/mixin_provisioners_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/action/builtin/mixin_provisioners_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/action/builtin/mixin_provisioners_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/action/builtin/mixin_provisioners_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,238 @@ +require File.expand_path("../../../../base", __FILE__) +require Vagrant.source_root.join("plugins/kernel_v2/config/vm") + +require "vagrant/action/builtin/mixin_provisioners" + +describe Vagrant::Action::Builtin::MixinProvisioners do + include_context "unit" + + let(:sandbox) { isolated_environment } + let(:iso_env) do + # We have to create a Vagrantfile so there is a root path + sandbox.vagrantfile("") + sandbox.create_vagrant_env + end + + let(:provisioner_config){ {} } + let(:provisioner_one) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("spec-test", :shell) + prov.config = provisioner_config + prov + end + let(:provisioner_two) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("spec-test", :shell) + prov.config = provisioner_config + prov + end + + let(:provisioner_instances) { [provisioner_one,provisioner_two] } + + let(:ui) { double("ui") } + let(:vm) { double("vm", provisioners: provisioner_instances) } + let(:config) { double("config", vm: vm) } + let(:machine) { double("machine", ui: ui, config: config) } + + let(:env) {{ machine: machine, ui: machine.ui, root_path: Pathname.new(".") }} + + subject do + Class.new do + extend Vagrant::Action::Builtin::MixinProvisioners + end + end + + after do + sandbox.close + described_class.reset! + end + + describe "#provisioner_instances" do + it "returns all the instances of configured provisioners" do + result = subject.provisioner_instances(env) + expect(result.size).to eq(provisioner_instances.size) + shell_one = result.first + expect(shell_one.first).to be_a(VagrantPlugins::Shell::Provisioner) + shell_two = result[1] + expect(shell_two.first).to be_a(VagrantPlugins::Shell::Provisioner) + end + end + + context "#sort_provisioner_instances" do + describe "with no dependency provisioners" do + it "returns the original array" do + result = subject.provisioner_instances(env) + expect(result.size).to eq(provisioner_instances.size) + shell_one = result.first + expect(shell_one.first).to be_a(VagrantPlugins::Shell::Provisioner) + shell_two = result[1] + expect(shell_two.first).to be_a(VagrantPlugins::Shell::Provisioner) + end + end + + describe "with before and after dependency provisioners" do + let(:provisioner_config){ {} } + let(:provisioner_root) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("root-test", :shell) + prov.config = provisioner_config + prov + end + let(:provisioner_before) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("before-test", :shell) + prov.config = provisioner_config + prov.before = "root-test" + prov + end + let(:provisioner_after) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("after-test", :shell) + prov.config = provisioner_config + prov.after = "root-test" + prov + end + let(:provisioner_instances) { [provisioner_root,provisioner_before,provisioner_after] } + + it "returns the array in the correct order" do + result = subject.provisioner_instances(env) + expect(result[0].last[:name]).to eq("before-test") + expect(result[1].last[:name]).to eq("root-test") + expect(result[2].last[:name]).to eq("after-test") + end + end + + describe "with before :each dependency provisioners" do + let(:provisioner_config){ {} } + let(:provisioner_root) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("root-test", :shell) + prov.config = provisioner_config + prov + end + let(:provisioner_root2) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("root2-test", :shell) + prov.config = provisioner_config + prov + end + let(:provisioner_before) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("before-test", :shell) + prov.config = provisioner_config + prov.before = :each + prov + end + + let(:provisioner_instances) { [provisioner_root,provisioner_root2,provisioner_before] } + + it "puts the each shortcut provisioners in place" do + result = subject.provisioner_instances(env) + + expect(result[0].last[:name]).to eq("before-test") + expect(result[1].last[:name]).to eq("root-test") + expect(result[2].last[:name]).to eq("before-test") + expect(result[3].last[:name]).to eq("root2-test") + end + end + + describe "with after :each dependency provisioners" do + let(:provisioner_config){ {} } + let(:provisioner_root) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("root-test", :shell) + prov.config = provisioner_config + prov + end + let(:provisioner_root2) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("root2-test", :shell) + prov.config = provisioner_config + prov + end + let(:provisioner_after) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("after-test", :shell) + prov.config = provisioner_config + prov.after = :each + prov + end + + let(:provisioner_instances) { [provisioner_root,provisioner_root2,provisioner_after] } + + it "puts the each shortcut provisioners in place" do + result = subject.provisioner_instances(env) + + expect(result[0].last[:name]).to eq("root-test") + expect(result[1].last[:name]).to eq("after-test") + expect(result[2].last[:name]).to eq("root2-test") + expect(result[3].last[:name]).to eq("after-test") + end + end + + describe "with before and after :each dependency provisioners" do + let(:provisioner_config){ {} } + let(:provisioner_root) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("root-test", :shell) + prov.config = provisioner_config + prov + end + let(:provisioner_root2) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("root2-test", :shell) + prov.config = provisioner_config + prov + end + let(:provisioner_after) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("after-test", :shell) + prov.config = provisioner_config + prov.after = :each + prov + end + let(:provisioner_before) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("before-test", :shell) + prov.config = provisioner_config + prov.before = :each + prov + end + + let(:provisioner_instances) { [provisioner_root,provisioner_root2,provisioner_before,provisioner_after] } + + it "puts the each shortcut provisioners in place" do + result = subject.provisioner_instances(env) + + expect(result[0].last[:name]).to eq("before-test") + expect(result[1].last[:name]).to eq("root-test") + expect(result[2].last[:name]).to eq("after-test") + expect(result[3].last[:name]).to eq("before-test") + expect(result[4].last[:name]).to eq("root2-test") + expect(result[5].last[:name]).to eq("after-test") + end + end + + describe "with before and after :all dependency provisioners" do + let(:provisioner_config){ {} } + let(:provisioner_root) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("root-test", :shell) + prov.config = provisioner_config + prov + end + let(:provisioner_root2) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("root2-test", :shell) + prov.config = provisioner_config + prov + end + let(:provisioner_after) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("after-test", :shell) + prov.config = provisioner_config + prov.after = :all + prov + end + let(:provisioner_before) do + prov = VagrantPlugins::Kernel_V2::VagrantConfigProvisioner.new("before-test", :shell) + prov.config = provisioner_config + prov.before = :all + prov + end + + let(:provisioner_instances) { [provisioner_root,provisioner_root2,provisioner_before,provisioner_after] } + + it "puts the each shortcut provisioners in place" do + result = subject.provisioner_instances(env) + + expect(result[0].last[:name]).to eq("before-test") + expect(result[1].last[:name]).to eq("root-test") + expect(result[2].last[:name]).to eq("root2-test") + expect(result[3].last[:name]).to eq("after-test") + end + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/action/builtin/mixin_synced_folders_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/action/builtin/mixin_synced_folders_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/action/builtin/mixin_synced_folders_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/action/builtin/mixin_synced_folders_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -256,6 +256,66 @@ end end + describe "#save_synced_folders" do + let(:folders) { {} } + let(:options) { {} } + let(:output_file) { double("output_file") } + + before do + allow(machine.data_dir).to receive(:join).with("synced_folders"). + and_return(output_file) + allow(output_file).to receive(:open).and_yield(output_file) + allow(output_file).to receive(:write) + end + + it "should write empty hash to file" do + expect(output_file).to receive(:write).with("{}") + subject.save_synced_folders(machine, folders, options) + end + + it "should call credential scrubber before writing file" do + expect(Vagrant::Util::CredentialScrubber).to receive(:desensitize).and_call_original + subject.save_synced_folders(machine, folders, options) + end + + context "when folder data is defined" do + let(:folders) { + {"root" => { + hostpath: "foo", type: "nfs", nfs__foo: "bar"}} + } + + it "should write folder information to file" do + expect(output_file).to receive(:write).with(JSON.dump(folders)) + subject.save_synced_folders(machine, folders, options) + end + + context "when folder data configuration includes sensitive data" do + let(:password) { "VAGRANT_TEST_PASSWORD" } + + before do + folders["root"][:folder_password] = password + Vagrant::Util::CredentialScrubber.sensitive(password) + end + + after { Vagrant::Util::CredentialScrubber.unsensitive(password) } + + it "should not include password when writing file" do + expect(output_file).to receive(:write) do |content| + expect(content).not_to include(password) + end + subject.save_synced_folders(machine, folders, options) + end + + it "should mask password content when writing file" do + expect(output_file).to receive(:write) do |content| + expect(content).to include(Vagrant::Util::CredentialScrubber::REPLACEMENT_TEXT) + end + subject.save_synced_folders(machine, folders, options) + end + end + end + end + describe "#synced_folders_diff" do it "sees two equal " do one = { diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/action/general/package_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/action/general/package_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/action/general/package_test.rb 1970-01-01 00:00:00.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/action/general/package_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -0,0 +1,389 @@ +require File.expand_path("../../../../base", __FILE__) + +describe Vagrant::Action::General::Package do + + let(:app) { double("app", call: nil) } + let(:env) { + {env: environment, + machine: machine, ui: ui} + } + let(:environment) { double("environment") } + let(:machine) { double("machine") } + let(:ui) { double("ui") } + + let(:subject) { described_class.new(app, env) } + + before do + allow_any_instance_of(Vagrant::Errors::VagrantError). + to receive(:translate_error) + end + + describe ".validate!" do + let(:output) { double("output", to_s: "output-path") } + let(:directory) { double("directory") } + + before do + allow(described_class).to receive(:fullpath).and_return(output) + allow(File).to receive(:directory?).with(output).and_return(false) + allow(File).to receive(:directory?).with(directory).and_return(true) + allow(File).to receive(:exist?).and_return(false) + allow(Vagrant::Util::Presence).to receive(:present?).with(directory).and_return(true) + end + + it "should not raise an error when options are valid" do + expect { described_class.validate!(output, directory) }.not_to raise_error + end + + it "should raise error when output directory exists" do + expect(File).to receive(:directory?).with(output).and_return(true) + expect { + described_class.validate!(output, directory) + }.to raise_error(Vagrant::Errors::PackageOutputDirectory) + end + + it "should raise error if output path exists" do + expect(File).to receive(:exist?).with(output).and_return(true) + expect { + described_class.validate!(output, directory) + }.to raise_error(Vagrant::Errors::PackageOutputExists) + end + + it "should raise error if directory value not provided" do + expect(Vagrant::Util::Presence).to receive(:present?).and_call_original + expect { + described_class.validate!(output, nil) + }.to raise_error(Vagrant::Errors::PackageRequiresDirectory) + end + + it "should raise error if directory path is not a directory" do + expect(File).to receive(:directory?).with(directory).and_return(false) + expect { + described_class.validate!(output, directory) + }.to raise_error(Vagrant::Errors::PackageRequiresDirectory) + end + end + + describe "#package_with_folder_path" do + let(:expanded_path) { double("expanded_path") } + + before do + allow(File).to receive(:expand_path).and_return(expanded_path) + end + + it "should create box folder if it does not exist" do + expect(File).to receive(:directory?).with(expanded_path).and_return(false) + expect(subject).to receive(:create_box_folder).with(expanded_path) + subject.package_with_folder_path + end + + it "should not create box folder if it already exists" do + expect(File).to receive(:directory?).with(expanded_path).and_return(true) + expect(subject).not_to receive(:create_box_folder) + subject.package_with_folder_path + end + end + + describe "#create_box_folder" do + let(:path) { double("path") } + + before do + allow(ui).to receive(:info) + allow(FileUtils).to receive(:mkdir_p) + subject.instance_variable_set(:@env, env) + end + + it "should notify user of new directory creation" do + expect(I18n).to receive(:t).with(an_instance_of(String), hash_including(folder_path: path)) + subject.create_box_folder(path) + end + + it "should create the directory" do + expect(FileUtils).to receive(:mkdir_p).with(path) + subject.create_box_folder(path) + end + end + + describe "#recover" do + let(:env) { {"vagrant.error" => error} } + let(:error) { nil } + let(:fullpath) { double("fullpath") } + + before { allow(described_class).to receive(:fullpath).and_return(fullpath) } + + it "should delete packaged files if they exist" do + expect(File).to receive(:exist?).with(fullpath).and_return(true) + expect(File).to receive(:delete).with(fullpath) + subject.recover(env) + end + + it "should not delete anything if package files do not exist" do + expect(File).to receive(:exist?).with(fullpath).and_return(false) + expect(File).not_to receive(:delete).with(fullpath) + subject.recover(env) + end + + context "when vagrant error is PackageOutputDirectory" do + let(:error) { Vagrant::Errors::PackageOutputDirectory.new } + + it "should not do anything" do + expect(File).not_to receive(:exist?) + expect(File).not_to receive(:delete) + subject.recover(env) + end + end + + context "when vagrant error is PackageOutputExists" do + let(:error) { Vagrant::Errors::PackageOutputExists.new } + + it "should not do anything" do + expect(File).not_to receive(:exist?) + expect(File).not_to receive(:delete) + subject.recover(env) + end + end + end + + describe "#copy_include_files" do + let(:package_directory) { @package_directory } + let(:package_files) { + Dir.glob(File.join(@package_files_directory, "*")).map {|i| + [i, File.basename(i)] + } + } + + before do + @package_directory = Dir.mktmpdir + @package_files_directory = Dir.mktmpdir + 3.times { |i| FileUtils.touch(File.join(@package_files_directory, "file.#{i}")) } + env["package.files"] = package_files + env["package.directory"] = package_directory + subject.instance_variable_set(:@env, env) + + allow(ui).to receive(:info) + end + + after do + FileUtils.rm_rf(@package_directory) + FileUtils.rm_rf(@package_files_directory) + end + + it "should copy all files to package directory" do + subject.copy_include_files + expected_files = package_files.map(&:last).map { |f| + File.join(package_directory, "include", f) + }.sort + expect( + Dir.glob(File.join(package_directory, "**", "*.*")).sort + ).to eq(expected_files) + end + + it "should notify user of copy" do + expect(ui).to receive(:info) + subject.copy_include_files + end + end + + describe "#compress" do + let(:package_directory) { @package_directory } + let(:fullpath) { "PATH" } + + before do + @package_directory = Dir.mktmpdir + FileUtils.touch(File.join(@package_directory, "test-file1")) + env["package.directory"] = package_directory + subject.instance_variable_set(:@env, env) + + allow(subject).to receive(:fullpath).and_return(fullpath) + end + + after do + FileUtils.rm_rf(package_directory) + end + + it "should change directory into package directory" do + expect(Vagrant::Util::SafeChdir).to receive(:safe_chdir).with(package_directory) + subject.compress + end + + it "should compress files using bsdtar" do + expect(Vagrant::Util::SafeChdir).to receive(:safe_chdir).with(package_directory).and_call_original + expect(Vagrant::Util::Subprocess).to receive(:execute).with("bsdtar", any_args, "./test-file1") + subject.compress + end + end + + describe "#write_metadata_json" do + let(:metadata_path) { File.join(package_directory, "metadata.json") } + let(:package_directory) { @package_directory } + let(:machine_provider) { "machine-provider" } + let(:default_provider) { "default-provider" } + + before do + @package_directory = Dir.mktmpdir + env["package.directory"] = @package_directory + subject.instance_variable_set(:@env, env) + + allow(machine).to receive(:provider_name).and_return(machine_provider) + allow(environment).to receive(:default_provider).and_return(default_provider) + end + + after { FileUtils.rm_rf(@package_directory) } + + it "should not create a metadata.json file if it already exists" do + expect(File).to receive(:exist?).with(metadata_path).and_return(true) + expect(File).not_to receive(:write) + subject.write_metadata_json + end + + it "should write a metadata file" do + expect(File).to receive(:write).with(metadata_path, any_args) + subject.write_metadata_json + end + + it "should write machine provider to metadata file" do + subject.write_metadata_json + content = JSON.load(File.read(metadata_path)) + expect(content["provider"]).to eq(machine_provider) + end + + context "when machine provider is unset" do + let(:machine_provider) { nil } + + it "should write default provider to metadata file" do + subject.write_metadata_json + content = JSON.load(File.read(metadata_path)) + expect(content["provider"]).to eq(default_provider) + end + end + + context "when machine provider and default provider are unset" do + let(:machine_provider) { nil } + let(:default_provider) { nil } + + it "should not write metadata file" do + subject.write_metadata_json + expect(File.exist?(metadata_path)).to be_falsey + end + end + end + + describe "#setup_private_key" do + let(:package_directory) { @package_directory } + let(:private_key_path) { File.join(package_directory, "datadir", "private_key") } + let(:new_key_path) { File.join(package_directory, "vagrant_private_key") } + let(:vagrantfile_path) { File.join(package_directory, "Vagrantfile") } + let(:data_dir) { Pathname.new(File.join(package_directory, "datadir")) } + let(:config) { + double("config", ssh: double("ssh", private_key_path: [private_key_path])) + } + + before do + @package_directory = Dir.mktmpdir + env["package.directory"] = @package_directory + FileUtils.mkdir(File.join(@package_directory, "datadir")) + File.write(private_key_path, "SSH KEY") + subject.instance_variable_set(:@env, env) + + allow(machine).to receive(:data_dir).and_return(data_dir) + allow(machine).to receive(:config).and_return(config) + end + + after { FileUtils.rm_rf(@package_directory) } + + it "should create a new Vagrantfile" do + subject.setup_private_key + expect(File.exist?(vagrantfile_path)).to be_truthy + end + + it "should create the new private ssh key" do + subject.setup_private_key + expect(File.exist?(new_key_path)).to be_truthy + end + + it "should copy the contents of the ssh key" do + subject.setup_private_key + expect(File.read(new_key_path)).to eq(File.read(private_key_path)) + end + + context "with no machine provided" do + before { env.delete(:machine) } + + it "should not create a private ssh key file" do + subject.setup_private_key + expect(File.exist?(new_key_path)).to be_falsey + end + end + + context "when vagrant_private_key exists" do + let(:private_key_path) { File.join(package_directory, "datadir", "vagrant_private_key") } + + it "should create the new private ssh key" do + subject.setup_private_key + expect(File.exist?(new_key_path)).to be_truthy + end + + it "should copy the contents of the ssh key" do + subject.setup_private_key + expect(File.read(new_key_path)).to eq(File.read(private_key_path)) + end + end + end + + describe "#call" do + let(:fullpath) { "FULLPATH" } + + before do + allow(ui).to receive(:info) + + allow(described_class).to receive(:validate!) + allow(subject).to receive(:fullpath).and_return(fullpath) + allow(subject).to receive(:package_with_folder_path) + allow(subject).to receive(:copy_include_files) + allow(subject).to receive(:setup_private_key) + allow(subject).to receive(:write_metadata_json) + allow(subject).to receive(:compress) + end + + it "should validate required arguments" do + expect(described_class).to receive(:validate!) + subject.call(env) + end + + it "should raise error if output path is a directory" do + expect(File).to receive(:directory?).with(fullpath).and_return(true) + expect { + subject.call(env) + }.to raise_error(Vagrant::Errors::PackageOutputDirectory) + end + + it "should call the next middleware" do + expect(app).to receive(:call) + subject.call(env) + end + + it "should notify of package compressing" do + expect(ui).to receive(:info) + subject.call(env) + end + + it "should copy include files" do + expect(subject).to receive(:copy_include_files) + subject.call(env) + end + + it "should setup private ssh key" do + expect(subject).to receive(:setup_private_key) + subject.call(env) + end + + it "should write metadata json file" do + expect(subject).to receive(:write_metadata_json) + subject.call(env) + end + + it "should compress the box" do + expect(subject).to receive(:compress) + subject.call(env) + end + end +end diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/action/runner_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/action/runner_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/action/runner_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/action/runner_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -1,7 +1,7 @@ require File.expand_path("../../../base", __FILE__) describe Vagrant::Action::Runner do - let(:instance) { described_class.new } + let(:instance) { described_class.new(action_name: "test") } it "should raise an error if an invalid callable is given" do expect { instance.run(7) }.to raise_error(ArgumentError, /must be a callable/) @@ -18,6 +18,7 @@ raise Exception, "BANG" end end + callable = klass.new.method(:action) expect { instance.run(callable) }.to raise_error(Exception, "BANG") end @@ -27,6 +28,23 @@ def initialize(app, env) end + def self.name + "TestAction" + end + + def call(env) + raise Exception, "BOOM" + end + end + + expect { instance.run(callable) }.to raise_error(Exception, "BOOM") + end + + it "should be able to use a Class as a callable with no name attribute" do + callable = Class.new do + def initialize(app, env) + end + def call(env) raise Exception, "BOOM" end @@ -63,7 +81,7 @@ result = env["data"] end - instance = described_class.new("data" => "bar") + instance = described_class.new("data" => "bar", action_name: "test") instance.run(callable) expect(result).to eq("bar") end @@ -74,7 +92,7 @@ result = env["data"] end - instance = described_class.new { { "data" => "bar" } } + instance = described_class.new { { "data" => "bar", action_name: "test" } } instance.run(callable) expect(result).to eq("bar") end diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/batch_action_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/batch_action_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/batch_action_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/batch_action_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -63,5 +63,24 @@ subject.action(machine, "up") subject.run end + + context "with provider supporting parallel actions" do + let(:provider_options) { {parallel: true} } + + it "should flag threads as being parallel actions" do + parallel = nil + subject.custom(machine) { |m| parallel = Thread.current[:batch_parallel_action] } + subject.custom(machine) { |*_| } + subject.run + expect(parallel).to eq(true) + end + + it "should exit the process if exit_code has been set" do + subject.custom(machine) { |m| Thread.current[:exit_code] = 1} + subject.custom(machine) { |*_| } + expect(Process).to receive(:exit!).with(1) + subject.run + end + end end end diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/box_collection_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/box_collection_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/box_collection_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/box_collection_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -16,6 +16,8 @@ end describe "#all" do + let(:ui) { double("ui", warn: true) } + it "should return an empty array when no boxes are there" do expect(subject.all).to eq([]) end @@ -35,6 +37,27 @@ expect(results.include?(["foo", "1.0", :vmware])).to be expect(results.include?(["bar", "0", :ec2])).to be expect(results.include?(["foo/bar", "1.0", :virtualbox])).to be + expect(results.include?(["foo:colon", "1.0", :virtualbox])).to be + end + + it "should return the boxes and their providers even if box has wrong version" do + allow(Vagrant::UI::Prefixed).to receive(:new).and_return(ui) + # Create some boxes + environment.box3("foo", "fake-invalid-version", :virtualbox) + environment.box3("foo", "1.0", :vmware) + environment.box3("bar", "0", :ec2) + environment.box3("foo-VAGRANTSLASH-bar", "1.0", :virtualbox) + environment.box3("foo-VAGRANTCOLON-colon", "1.0", :virtualbox) + + expect(ui).to receive(:warn).once + + # Verify some output + results = subject.all + expect(results.length).to eq(4) + expect(results.include?(["foo", "1.0", :virtualbox])).not_to be + expect(results.include?(["foo", "1.0", :vmware])).to be + expect(results.include?(["bar", "0", :ec2])).to be + expect(results.include?(["foo/bar", "1.0", :virtualbox])).to be expect(results.include?(["foo:colon", "1.0", :virtualbox])).to be end diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/box_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/box_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/box_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/box_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -320,7 +320,7 @@ FileUtils.rm_rf(scratch) end - it "should repackage the box" do + it "should repackage the box", :bsdtar do test_file_contents = "hello, world!" # Put a file in the box directory to verify it is packaged properly diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/bundler_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/bundler_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/bundler_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/bundler_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -28,6 +28,19 @@ expect(subject.env_plugin_gem_path).to be_nil end + describe "#initialize" do + let(:gemrc_location) { "C:\\My\\Config\\File" } + + it "should set up GEMRC through a flag instead of GEMRC" do + allow(ENV).to receive(:[]).with("VAGRANT_HOME") + allow(ENV).to receive(:[]).with("USERPROFILE") + + allow(ENV).to receive(:[]).with("GEMRC").and_return(gemrc_location) + expect(Gem::ConfigFile).to receive(:new).with(["--config-file", gemrc_location]) + init_subject = described_class.new + end + end + describe "#deinit" do it "should provide method for backwards compatibility" do subject.deinit diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/cli_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/cli_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/cli_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/cli_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -28,6 +28,8 @@ end describe "#execute" do + let(:triggers) { double("triggers") } + it "invokes help and exits with 1 if invalid command" do subject = described_class.new(["i-dont-exist"], env) expect(subject).to receive(:help).once @@ -54,6 +56,37 @@ expect(checkpoint).to receive(:display) described_class.new(["destroy"], env).execute end + + it "fires triggers, if enabled" do + allow(Vagrant::Util::Experimental).to receive(:feature_enabled?). + with("typed_triggers").and_return("true") + allow(triggers).to receive(:fire_triggers) + + commands[:destroy] = [command_lambda("destroy", 42), {}] + + allow(Vagrant::Plugin::V2::Trigger).to receive(:new).and_return(triggers) + + subject = described_class.new(["destroy"], env) + + expect(triggers).to receive(:fire_triggers).twice + + expect(subject).not_to receive(:help) + expect(subject.execute).to eql(42) + end + + it "does not fire triggers if disabled" do + allow(Vagrant::Util::Experimental).to receive(:feature_enabled?). + with("typed_triggers").and_return("false") + + commands[:destroy] = [command_lambda("destroy", 42), {}] + + subject = described_class.new(["destroy"], env) + + expect(triggers).not_to receive(:fire_triggers) + + expect(subject).not_to receive(:help) + expect(subject.execute).to eql(42) + end end describe "#help" do diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/environment_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/environment_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/environment_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/environment_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -165,7 +165,7 @@ collection = double("collection") expect(Vagrant::BoxCollection).to receive(:new).with( - env.homedir.join("boxes"), anything).and_return(collection) + env.homedir.join("boxes"), anything).twice.and_return(collection) expect(collection).to receive(:upgrade_v1_1_v1_5).once subject end @@ -761,6 +761,7 @@ before do m = Vagrant.plugin("2").manager allow(m).to receive(:providers).and_return(plugin_providers) + allow_any_instance_of(described_class).to receive(:process_configured_plugins) end it "is the highest matching usable provider" do @@ -1431,6 +1432,108 @@ end end + describe "guess_provider" do + before { allow_any_instance_of(described_class).to receive(:process_configured_plugins) } + + it "should return the default provider by default" do + expect(subject).to receive(:default_provider).and_return("default_provider") + expect(subject.send(:guess_provider)).to eq("default_provider") + end + + context "when provider is defined via command line argument" do + before { stub_const("ARGV", argv) } + + context "when provider is given as single argument" do + let(:argv) { ["--provider=single_arg"] } + + it "should return the provider name" do + expect(subject.send(:guess_provider)).to eq(:single_arg) + end + end + + context "when provider is given as two arguments" do + let(:argv) { ["--provider", "double_arg"] } + + it "should return the provider name" do + expect(subject.send(:guess_provider)).to eq(:double_arg) + end + end + end + + context "when no default provider is available" do + before { + expect(subject).to receive(:default_provider). + and_raise(Vagrant::Errors::NoDefaultProvider) } + + it "should return a nil value" do + expect(subject.send(:guess_provider)).to be_nil + end + end + end + + describe "#find_configured_plugins" do + before do + allow_any_instance_of(described_class).to receive(:guess_provider).and_return(:dummy) + allow_any_instance_of(described_class).to receive(:process_configured_plugins) + end + + it "should find no plugins when no plugins are configured" do + expect(subject.send(:find_configured_plugins)).to be_empty + end + + context "when plugins are defined in the Vagrantfile" do + before do + env.vagrantfile <<-VF + Vagrant.configure("2") do |config| + config.vagrant.plugins = "vagrant-plugin" + end + VF + end + + it "should return the vagrant-plugin" do + expect(subject.send(:find_configured_plugins).keys).to include("vagrant-plugin") + end + end + + context "when plugins are defined in the Vagrantfile of a box" do + before do + env.box3("foo", "1.0", :dummy, vagrantfile: <<-VF) + Vagrant.configure("2") do |config| + config.vagrant.plugins = "vagrant-plugin" + end + VF + env.vagrantfile <<-VF + Vagrant.configure("2") do |config| + config.vm.box = "foo" + end + VF + end + + it "should return the vagrant-plugin" do + expect(subject.send(:find_configured_plugins).keys).to include("vagrant-plugin") + end + end + + context "when the box does not match the provider" do + before do + env.box3("foo", "1.0", :other, vagrantfile: <<-VF) + Vagrant.configure("2") do |config| + config.vagrant.plugins = "vagrant-plugin" + end + VF + env.vagrantfile <<-VF + Vagrant.configure("2") do |config| + config.vm.box = "foo" + end + VF + end + + it "should not return the vagrant-plugin" do + expect(subject.send(:find_configured_plugins).keys).not_to include("vagrant-plugin") + end + end + end + describe "#process_configured_plugins" do let(:env) do isolated_environment.tap do |e| @@ -1458,10 +1561,18 @@ allow(plugin_manager).to receive(:load_plugins) end + context "when local data directory does not exist" do + let(:local_file) { nil } + + it "should properly return empty result" do + expect(instance.send(:process_configured_plugins)).to be_empty + end + end + context "plugins are disabled" do before{ allow(Vagrant).to receive(:plugins_enabled?).and_return(false) } - it "should return nil" do + it "should return empty result" do expect(instance.send(:process_configured_plugins)).to be_nil end end diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/machine_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/machine_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/machine_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/machine_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -411,38 +411,6 @@ expect(subject.ui).to_not have_received(:warn) end end - - context "with the vagrant-triggers community plugin" do - it "should not call the internal trigger functions if installed" do - action_name = :destroy - callable = lambda { |_env| } - - allow(provider).to receive(:action).with(action_name).and_return(callable) - - # The first call here is to allow the environment to setup with attempting - # to load a plugin that does not exist - expect(Vagrant::Plugin::Manager.instance).to receive(:installed_plugins) - .and_return({}) - - expect(Vagrant::Plugin::Manager.instance).to receive(:installed_plugins) - .and_return({"vagrant-triggers"=>"stuff"}) - - expect(instance.instance_variable_get(:@triggers)).not_to receive(:fire_triggers) - instance.action(action_name) - end - - it "should call the internal trigger functions if not installed" do - action_name = :destroy - callable = lambda { |_env| } - - allow(provider).to receive(:action).with(action_name).and_return(callable) - allow(Vagrant::Plugin::Manager.instance).to receive(:installed_plugins) - .and_return({}) - - expect(instance.instance_variable_get(:@triggers)).to receive(:fire_triggers).twice - instance.action(action_name) - end - end end describe "#action_raw" do @@ -944,4 +912,62 @@ expect(subject.ui).to_not equal(ui) end end + + describe "#reload" do + context "when ID is unset and id file does not exist" do + it "should remain nil" do + expect(subject.id).to be_nil + instance.reload + expect(subject.id).to be_nil + end + end + + context "when id file is set" do + let(:id_content) { "test-machine-id" } + + before do + id_file = subject.data_dir.join("id") + File.write(id_file.to_s, id_content) + end + + it "should update the machine id" do + expect(subject.id).to be_nil + instance.reload + expect(subject.id).to eq(id_content) + end + + it "should notify of the id change when provider is set" do + expect(provider).to receive(:machine_id_changed) + instance.reload + end + + context "when id file content includes whitespace" do + let(:id_content) { " test-machine-id\n" } + + it "should remove all whitespace" do + instance.reload + expect(instance.id).to eq("test-machine-id") + end + end + + context "when id file content is all whitespace" do + let(:id_content) { "\0\0\0\0\0\0" } + + it "should not update the id" do + expect(instance.id).to be_nil + instance.reload + expect(instance.id).to be_nil + end + end + + context "when id is already set to value in id file" do + it "should not notify of id change" do + instance.instance_variable_set(:@id, id_content) + expect(provider).not_to receive(:machine_id_changed) + instance.reload + expect(instance.id).to eq(id_content) + end + end + end + end end diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/plugin/v2/trigger_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/plugin/v2/trigger_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/plugin/v2/trigger_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/plugin/v2/trigger_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -17,9 +17,10 @@ allow(m).to receive(:state).and_return(state) end end + let(:ui) { Vagrant::UI::Silent.new } let(:env) { { machine: machine, - ui: Vagrant::UI::Silent.new, + ui: ui, } } let(:triggers) { VagrantPlugins::Kernel_V2::TriggerConfig.new } @@ -36,19 +37,33 @@ end - let(:subject) { described_class.new(env, triggers, machine) } + let(:subject) { described_class.new(env, triggers, machine, ui) } context "#fire_triggers" do it "raises an error if an inproper stage is given" do - expect{ subject.fire_triggers(:up, :not_real, "guest") }. + expect{ subject.fire_triggers(:up, :not_real, "guest", :action) }. to raise_error(Vagrant::Errors::TriggersNoStageGiven) end + + it "does not fire triggers if community plugin is detected" do + allow(subject).to receive(:community_plugin_detected?).and_return(true) + + expect(subject).not_to receive(:fire) + subject.fire_triggers(:up, :before, "guest", :action) + end + + it "does fire triggers if community plugin is not detected" do + allow(subject).to receive(:community_plugin_detected?).and_return(false) + + expect(subject).to receive(:fire) + subject.fire_triggers(:up, :before, "guest", :action) + end end context "#filter_triggers" do it "returns all triggers if no constraints" do before_triggers = triggers.before_triggers - filtered_triggers = subject.send(:filter_triggers, before_triggers, "guest") + filtered_triggers = subject.send(:filter_triggers, before_triggers, "guest", :action) expect(filtered_triggers).to eq(before_triggers) end @@ -59,7 +74,7 @@ after_triggers = triggers.after_triggers expect(after_triggers.size).to eq(3) - subject.send(:filter_triggers, after_triggers, "ubuntu") + subject.send(:filter_triggers, after_triggers, :ubuntu, :action) expect(after_triggers.size).to eq(2) end @@ -70,7 +85,7 @@ after_triggers = triggers.after_triggers expect(after_triggers.size).to eq(3) - subject.send(:filter_triggers, after_triggers, "ubuntu-guest") + subject.send(:filter_triggers, after_triggers, "ubuntu-guest", :action) expect(after_triggers.size).to eq(3) end @@ -81,7 +96,7 @@ after_triggers = triggers.after_triggers expect(after_triggers.size).to eq(3) - subject.send(:filter_triggers, after_triggers, "ubuntu-guest") + subject.send(:filter_triggers, after_triggers, "ubuntu-guest", :action) expect(after_triggers.size).to eq(3) end end @@ -101,7 +116,7 @@ it "prints messages at INFO" do output = "" - allow(machine.ui).to receive(:info) do |data| + allow(ui).to receive(:info) do |data| output << data end @@ -115,7 +130,7 @@ it "prints messages at WARN" do output = "" - allow(machine.ui).to receive(:warn) do |data| + allow(ui).to receive(:warn) do |data| output << data end @@ -304,6 +319,29 @@ trigger_run.finalize! end + context "with no machine existing" do + let(:machine) { nil } + + it "raises an error and halts if guest does not exist" do + trigger = trigger_run.after_triggers.first + shell_config = trigger.run_remote + on_error = trigger.on_error + exit_codes = trigger.exit_codes + + expect { subject.send(:run_remote, shell_config, on_error, exit_codes) }. + to raise_error(Vagrant::Errors::TriggersGuestNotExist) + end + + it "continues on if guest does not exist but is configured to continue on error" do + trigger = trigger_run.before_triggers.first + shell_config = trigger.run_remote + on_error = trigger.on_error + exit_codes = trigger.exit_codes + + subject.send(:run_remote, shell_config, on_error, exit_codes) + end + end + it "raises an error and halts if guest is not running" do allow(machine.state).to receive(:id).and_return(:not_running) @@ -376,12 +414,45 @@ context "#trigger_abort" do it "system exits when called" do + allow(Process).to receive(:exit!).and_return(true) output = "" allow(machine.ui).to receive(:warn) do |data| output << data end - expect { subject.send(:trigger_abort, 3) }.to raise_error(SystemExit) + expect(Process).to receive(:exit!).with(3) + subject.send(:trigger_abort, 3) + end + + context "when running in parallel" do + let(:thread) { + @t ||= Thread.new do + Thread.current[:batch_parallel_action] = true + Thread.stop + subject.send(:trigger_abort, exit_code) + end + } + let(:exit_code) { 22 } + + before do + expect(Process).not_to receive(:exit!) + sleep(0.1) until thread.stop? + end + + after { @t = nil } + + it "should terminate the thread" do + expect(thread).to receive(:terminate).and_call_original + thread.wakeup + thread.join(1) while thread.alive? + end + + it "should set the exit code into the thread data" do + expect(thread).to receive(:terminate).and_call_original + thread.wakeup + thread.join(1) while thread.alive? + expect(thread[:exit_code]).to eq(exit_code) + end end end diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/util/downloader_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/util/downloader_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/util/downloader_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/util/downloader_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -175,23 +175,19 @@ context "with checksum" do let(:checksum_expected_value){ 'MD5_CHECKSUM_VALUE' } let(:checksum_invalid_value){ 'INVALID_VALUE' } - let(:digest){ double("digest") } + let(:filechecksum) { double("filechecksum", checksum: checksum_value) } + let(:checksum_value) { double("checksum_value") } - before do - allow(digest).to receive(:file).and_return(digest) - end + before { allow(FileChecksum).to receive(:new).with(any_args).and_return(filechecksum) } - [Digest::MD5, Digest::SHA1].each do |klass| + [Digest::MD5, Digest::SHA1, Digest::SHA256, Digest::SHA384, Digest::SHA512].each do |klass| short_name = klass.to_s.split("::").last.downcase context "using #{short_name} digest" do subject { described_class.new(source, destination, short_name.to_sym => checksum_expected_value) } context "that matches expected value" do - before do - expect(klass).to receive(:new).and_return(digest) - expect(digest).to receive(:hexdigest).and_return(checksum_expected_value) - end + let(:checksum_value) { checksum_expected_value } it "should not raise an exception" do expect(subject.download!).to be(true) @@ -199,10 +195,7 @@ end context "that does not match expected value" do - before do - expect(klass).to receive(:new).and_return(digest) - expect(digest).to receive(:hexdigest).and_return(checksum_invalid_value) - end + let(:checksum_value) { checksum_invalid_value } it "should raise an exception" do expect{ subject.download! }.to raise_error(Vagrant::Errors::DownloaderChecksumError) @@ -213,13 +206,9 @@ context "using both md5 and sha1 digests" do context "that both match expected values" do - subject { described_class.new(source, destination, md5: checksum_expected_value, sha1: checksum_expected_value) } + let(:checksum_value) { checksum_expected_value } - before do - expect(Digest::MD5).to receive(:new).and_return(digest) - expect(Digest::SHA1).to receive(:new).and_return(digest) - expect(digest).to receive(:hexdigest).and_return(checksum_expected_value).exactly(2).times - end + subject { described_class.new(source, destination, md5: checksum_expected_value, sha1: checksum_expected_value) } it "should not raise an exception" do expect(subject.download!).to be(true) @@ -227,12 +216,14 @@ end context "that only sha1 matches expected value" do - subject { described_class.new(source, destination, md5: checksum_invalid_value, sha1: checksum_expected_value) } + subject { described_class.new(source, destination, md5: checksum_expected_value, sha1: checksum_expected_value) } + + let(:valid_checksum) { double("valid_checksum", checksum: checksum_expected_value) } + let(:invalid_checksum) { double("invalid_checksum", checksum: checksum_invalid_value) } before do - allow(Digest::MD5).to receive(:new).and_return(digest) - allow(Digest::SHA1).to receive(:new).and_return(digest) - expect(digest).to receive(:hexdigest).and_return(checksum_expected_value).at_least(:once) + allow(FileChecksum).to receive(:new).with(anything, :sha1).and_return(valid_checksum) + allow(FileChecksum).to receive(:new).with(anything, :md5).and_return(invalid_checksum) end it "should raise an exception" do @@ -241,12 +232,14 @@ end context "that only md5 matches expected value" do - subject { described_class.new(source, destination, md5: checksum_expected_value, sha1: checksum_invalid_value) } + subject { described_class.new(source, destination, md5: checksum_expected_value, sha1: checksum_expected_value) } + + let(:valid_checksum) { double("valid_checksum", checksum: checksum_expected_value) } + let(:invalid_checksum) { double("invalid_checksum", checksum: checksum_invalid_value) } before do - allow(Digest::MD5).to receive(:new).and_return(digest) - allow(Digest::SHA1).to receive(:new).and_return(digest) - expect(digest).to receive(:hexdigest).and_return(checksum_expected_value).at_least(:once) + allow(FileChecksum).to receive(:new).with(anything, :md5).and_return(valid_checksum) + allow(FileChecksum).to receive(:new).with(anything, :sha1).and_return(invalid_checksum) end it "should raise an exception" do @@ -255,14 +248,9 @@ end context "that none match expected value" do + let(:checksum_value) { checksum_expected_value } subject { described_class.new(source, destination, md5: checksum_invalid_value, sha1: checksum_invalid_value) } - before do - allow(Digest::MD5).to receive(:new).and_return(digest) - allow(Digest::SHA1).to receive(:new).and_return(digest) - expect(digest).to receive(:hexdigest).and_return(checksum_expected_value).at_least(:once) - end - it "should raise an exception" do expect{ subject.download! }.to raise_error(Vagrant::Errors::DownloaderChecksumError) end diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/util/file_checksum_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/util/file_checksum_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/util/file_checksum_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/util/file_checksum_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -20,4 +20,16 @@ instance = described_class.new(file, Digest::SHA1) expect(instance.checksum).to eq("264b207c7913e461c43d0f63d2512f4017af4755") end + + it "should support initialize with class or string" do + file = environment.workdir.join("file") + file.open("w+") { |f| f.write("HELLO!") } + + %w(md5 sha1 sha256 sha384 sha512).each do |type| + klass = Digest.const_get(type.upcase) + t_i = described_class.new(file, type) + k_i = described_class.new(file, klass) + expect(t_i.checksum).to eq(k_i.checksum) + end + end end diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/util/ssh_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/util/ssh_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/util/ssh_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/util/ssh_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -39,7 +39,11 @@ dsa_authentication: true }} - let(:ssh_path) { "/usr/bin/ssh" } + let(:ssh_path) { /.*ssh/ } + + before { + allow(Vagrant::Util::Which).to receive(:which).with("ssh", any_args).and_return(ssh_path) + } it "searches original PATH for executable" do expect(Vagrant::Util::Which).to receive(:which).with("ssh", original_path: true).and_return("valid-return") @@ -97,8 +101,6 @@ dsa_authentication: true }} - let(:ssh_path) { "/usr/bin/ssh" } - it "uses the IdentityFile argument and escapes the '%' character" do allow(Vagrant::Util::SafeExec).to receive(:exec).and_return(nil) described_class.exec(ssh_info) @@ -247,7 +249,7 @@ it "enables ssh config loading" do allow(Vagrant::Util::SafeExec).to receive(:exec).and_return(nil) expect(Vagrant::Util::SafeExec).to receive(:exec) do |exe_path, *args| - expect(exe_path).to eq(ssh_path) + expect(exe_path).to match(ssh_path) config_options = ["-F", "/path/to/config"] expect(args & config_options).to eq(config_options) end diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/util/subprocess_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/util/subprocess_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/util/subprocess_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/util/subprocess_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -69,7 +69,7 @@ allow(process_env).to receive(:[]=) allow(ENV).to receive(:[]).with("VAGRANT_INSTALLER_ENV").and_return("1") allow(ENV).to receive(:[]).with("VAGRANT_APPIMAGE").and_return("1") - allow(ENV).to receive(:[]).with("VAGRANT_APPIMAGE_LD_LIBRARY_PATH").and_return(appimage_ld_path) + allow(ENV).to receive(:[]).with("VAGRANT_APPIMAGE_HOST_LD_LIBRARY_PATH").and_return(appimage_ld_path) allow(File).to receive(:file?).with(exec_path).and_return(true) allow(ChildProcess).to receive(:build).and_return(process) allow(Vagrant).to receive(:installer_embedded_dir).and_return(appimage_path) diff -Nru vagrant-2.2.3+dfsg/test/unit/vagrant/vagrantfile_test.rb vagrant-2.2.6+dfsg/test/unit/vagrant/vagrantfile_test.rb --- vagrant-2.2.3+dfsg/test/unit/vagrant/vagrantfile_test.rb 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/test/unit/vagrant/vagrantfile_test.rb 2019-10-14 16:38:13.000000000 +0000 @@ -368,6 +368,38 @@ to raise_error(Vagrant::Errors::ProviderNotUsable) end + context "when provider validation is ignored" do + before do + configure do |config| + config.vm.box = "base" + config.vm.box_version = "1.0" + config.vm.define :guest1 + config.vm.define :guest2 + + config.vm.provider "custom" do |_, c| + c.ssh.port = 123 + end + end + + iso_env.box3("base", "1.0", :custom, vagrantfile: <<-VF) + Vagrant.configure("2") do |config| + config.vagrant.plugins = "vagrant-custom" + end + VF + end + + it "should not raise an error if provider is not found" do + expect { subject.machine_config(:guest1, :custom, boxes, nil, false) }. + not_to raise_error + end + + it "should return configuration from box Vagrantfile" do + config = subject.machine_config(:guest1, :custom, boxes, nil, false)[:config] + expect(config.vagrant.plugins).to be_a(Hash) + expect(config.vagrant.plugins.keys).to include("vagrant-custom") + end + end + context "local box metadata file" do let(:data_path) { double(:data_path) } let(:meta_file) { double(:meta_file) } diff -Nru vagrant-2.2.3+dfsg/.travis.yml vagrant-2.2.6+dfsg/.travis.yml --- vagrant-2.2.3+dfsg/.travis.yml 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/.travis.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -language: ruby - -sudo: false - -cache: bundler - -addons: - apt: - packages: - - bsdtar - -rvm: - - 2.3.8 - - 2.4.5 - - 2.5.3 - - 2.6.0 - -branches: - only: - - master - -env: - global: - - NOKOGIRI_USE_SYSTEM_LIBRARIES=true - -script: bundle exec rake test:unit diff -Nru vagrant-2.2.3+dfsg/Vagrantfile vagrant-2.2.6+dfsg/Vagrantfile --- vagrant-2.2.3+dfsg/Vagrantfile 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/Vagrantfile 2019-10-14 16:38:13.000000000 +0000 @@ -4,13 +4,13 @@ # Ruby, run unit tests, etc. Vagrant.configure("2") do |config| - config.vm.box = "hashicorp/precise64" + config.vm.box = "hashicorp/bionic64" config.vm.hostname = "vagrant" config.ssh.shell = "bash -c 'BASH_ENV=/etc/profile exec bash'" - ["vmware_fusion", "vmware_workstation", "virtualbox"].each do |provider| + ["vmware_desktop", "virtualbox", "hyperv"].each do |provider| config.vm.provider provider do |v, override| - v.memory = "1024" + v.memory = "2048" end end @@ -29,32 +29,32 @@ 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 -# Install basic dependencies -apt-get install -qy build-essential bsdtar curl +# 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 # Import the mpapis public key to verify downloaded releases su -l -c 'gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3' 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 +# 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 # Output the Ruby version (for sanity) su -l -c 'ruby --version' vagrant @@ -63,15 +63,14 @@ apt-get install -qy git # Upgrade Rubygems -su -l -c "rvm ${RUBY_VER_LATEST} do gem update --system" vagrant +su -l -c "rvm ${RUBY_VER} do gem update --system" vagrant -# Install bundler and prepare to run unit tests -su -l -c "gem install bundler -v ${BUNDLER_VER_REQ}" vagrant +# Prepare to run unit tests 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 || echo 'cd /vagrant' >> /home/vagrant/.bash_profile +grep -q 'cd /vagrant' /home/vagrant/.bash_profile 2>/dev/null || echo 'cd /vagrant' >> /home/vagrant/.bash_profile # Touch the marker file so we don't do this again touch ${MARKER_FILE} diff -Nru vagrant-2.2.3+dfsg/vagrant.gemspec vagrant-2.2.6+dfsg/vagrant.gemspec --- vagrant-2.2.3+dfsg/vagrant.gemspec 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/vagrant.gemspec 2019-10-14 16:38:13.000000000 +0000 @@ -12,7 +12,7 @@ s.summary = "Build and distribute virtualized development environments." s.description = "Vagrant is a tool for building and distributing virtualized development environments." - s.required_ruby_version = "~> 2.2", "< 2.7" + s.required_ruby_version = "~> 2.4", "< 2.7" s.required_rubygems_version = ">= 1.3.6" s.rubyforge_project = "vagrant" @@ -34,7 +34,7 @@ s.add_dependency "winrm", "~> 2.1" s.add_dependency "winrm-fs", "~> 1.0" s.add_dependency "winrm-elevated", "~> 1.1" - s.add_dependency "vagrant_cloud", "~> 2.0.2" + s.add_dependency "vagrant_cloud", "~> 2.0.3" # NOTE: The ruby_dep gem is an implicit dependency from the listen gem. Later versions # of the ruby_dep gem impose an aggressive constraint on the required ruby version (>= 2.2.5). diff -Nru vagrant-2.2.3+dfsg/version.txt vagrant-2.2.6+dfsg/version.txt --- vagrant-2.2.3+dfsg/version.txt 2019-01-09 21:25:17.000000000 +0000 +++ vagrant-2.2.6+dfsg/version.txt 2019-10-14 16:38:13.000000000 +0000 @@ -1 +1 @@ -2.2.3 +2.2.6